diff --git a/platformio_override.sample.ini b/platformio_override.sample.ini
index cb5b43e7b..19b8c273a 100644
--- a/platformio_override.sample.ini
+++ b/platformio_override.sample.ini
@@ -529,3 +529,14 @@ monitor_filters = esp32_exception_decoder
lib_deps =
${esp32.lib_deps}
TFT_eSPI @ 2.5.33 ;; this is the last version that compiles with the WLED default framework - newer versions require platform = espressif32 @ ^6.3.2
+
+# ------------------------------------------------------------------------------
+# Usermod examples
+# ------------------------------------------------------------------------------
+
+# 433MHz RF remote example for esp32dev
+[env:esp32dev_usermod_RF433]
+extends = env:esp32dev
+build_flags = ${env:esp32dev.build_flags} -D USERMOD_RF433
+lib_deps = ${env:esp32dev.lib_deps}
+ sui77/rc-switch @ 2.6.4
diff --git a/usermods/usermod_v2_RF433/readme.md b/usermods/usermod_v2_RF433/readme.md
new file mode 100644
index 000000000..43919f11b
--- /dev/null
+++ b/usermods/usermod_v2_RF433/readme.md
@@ -0,0 +1,18 @@
+# RF433 remote usermod
+
+Usermod for controlling WLED using a generic 433 / 315MHz remote and simple 3-pin receiver
+See for compatibility details
+
+## Build
+
+- Create a `platformio_override.ini` file at the root of the wled source directory if not already present
+- Copy the `433MHz RF remote example for esp32dev` section from `platformio_override.sample.ini` into it
+- Duplicate/adjust for other boards
+
+## Usage
+
+- Connect receiver to a free pin
+- Set pin in Config->Usermods
+- Info pane will show the last received button code
+- Upload the remote433.json sample file in this folder to the ESP with the file editor at [http://\[wled-ip\]/edit](http://ip/edit)
+- Edit as necessary, the key is the button number retrieved from the info pane, and the "cmd" can be either an [HTTP API](https://kno.wled.ge/interfaces/http-api/) or a [JSON API](https://kno.wled.ge/interfaces/json-api/) command.
\ No newline at end of file
diff --git a/usermods/usermod_v2_RF433/remote433.json b/usermods/usermod_v2_RF433/remote433.json
new file mode 100644
index 000000000..d5d930a81
--- /dev/null
+++ b/usermods/usermod_v2_RF433/remote433.json
@@ -0,0 +1,34 @@
+{
+ "13985576": {
+ "cmnt": "Toggle Power using HTTP API",
+ "cmd": "T=2"
+ },
+ "3670817": {
+ "cmnt": "Force Power ON using HTTP API",
+ "cmd": "T=1"
+ },
+ "13985572": {
+ "cmnt": "Set brightness to 200 using JSON API",
+ "cmd": {"bri":200}
+ },
+ "3670818": {
+ "cmnt": "Run Preset 1 using JSON API",
+ "cmd": {"ps":1}
+ },
+ "13985570": {
+ "cmnt": "Increase brightness by 40 using HTTP API",
+ "cmd": "A=~40"
+ },
+ "13985569": {
+ "cmnt": "Decrease brightness by 40 using HTTP API",
+ "cmd": "A=~-40"
+ },
+ "7608836": {
+ "cmnt": "Start 1min timer using JSON API",
+ "cmd": {"nl":{"on":true,"dur":1,"mode":0}}
+ },
+ "7608840": {
+ "cmnt": "Select random effect on all segments using JSON API",
+ "cmd": {"seg":{"fx":"r"}}
+ }
+}
\ No newline at end of file
diff --git a/usermods/usermod_v2_RF433/usermod_v2_RF433.h b/usermods/usermod_v2_RF433/usermod_v2_RF433.h
new file mode 100644
index 000000000..ebaf433f1
--- /dev/null
+++ b/usermods/usermod_v2_RF433/usermod_v2_RF433.h
@@ -0,0 +1,183 @@
+#pragma once
+
+#include "wled.h"
+#include "Arduino.h"
+#include
+
+#define RF433_BUSWAIT_TIMEOUT 24
+
+class RF433Usermod : public Usermod
+{
+private:
+ RCSwitch mySwitch = RCSwitch();
+ unsigned long lastCommand = 0;
+ unsigned long lastTime = 0;
+
+ bool modEnabled = true;
+ int8_t receivePin = -1;
+
+ static const char _modName[];
+ static const char _modEnabled[];
+ static const char _receivePin[];
+
+ bool initDone = false;
+
+public:
+
+ void setup()
+ {
+ mySwitch.disableReceive();
+ if (modEnabled)
+ {
+ mySwitch.enableReceive(receivePin);
+ }
+ initDone = true;
+ }
+
+ /*
+ * connected() is called every time the WiFi is (re)connected
+ * Use it to initialize network interfaces
+ */
+ void connected()
+ {
+ }
+
+ void loop()
+ {
+ if (!modEnabled || strip.isUpdating())
+ return;
+
+ if (mySwitch.available())
+ {
+ unsigned long receivedCommand = mySwitch.getReceivedValue();
+ mySwitch.resetAvailable();
+
+ // Discard duplicates, limit long press repeat
+ if (lastCommand == receivedCommand && millis() - lastTime < 800)
+ return;
+
+ lastCommand = receivedCommand;
+ lastTime = millis();
+
+ DEBUG_PRINT(F("RF433 Receive: "));
+ DEBUG_PRINTLN(receivedCommand);
+
+ if(!remoteJson433(receivedCommand))
+ DEBUG_PRINTLN(F("RF433: unknown button"));
+ }
+ }
+
+ // Add last received button to info pane
+ void addToJsonInfo(JsonObject &root)
+ {
+ if (!initDone)
+ return; // prevent crash on boot applyPreset()
+ JsonObject user = root["u"];
+ if (user.isNull())
+ user = root.createNestedObject("u");
+
+ JsonArray switchArr = user.createNestedArray("RF433 Last Received"); // name
+ switchArr.add(lastCommand);
+ }
+
+ void addToConfig(JsonObject &root)
+ {
+ JsonObject top = root.createNestedObject(FPSTR(_modName)); // usermodname
+ top[FPSTR(_modEnabled)] = modEnabled;
+ JsonArray pinArray = top.createNestedArray("pin");
+ pinArray.add(receivePin);
+
+ DEBUG_PRINTLN(F(" config saved."));
+ }
+
+ bool readFromConfig(JsonObject &root)
+ {
+ JsonObject top = root[FPSTR(_modName)];
+ if (top.isNull())
+ {
+ DEBUG_PRINT(FPSTR(_modName));
+ DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
+ return false;
+ }
+ getJsonValue(top[FPSTR(_modEnabled)], modEnabled);
+ getJsonValue(top["pin"][0], receivePin);
+
+ DEBUG_PRINTLN(F("config (re)loaded."));
+
+ // Redo init on update
+ if(initDone)
+ setup();
+
+ return true;
+ }
+
+ /*
+ * getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
+ * This could be used in the future for the system to determine whether your usermod is installed.
+ */
+ uint16_t getId()
+ {
+ return USERMOD_ID_RF433;
+ }
+
+ // this function follows the same principle as decodeIRJson() / remoteJson()
+ bool remoteJson433(int button)
+ {
+ char objKey[14];
+ bool parsed = false;
+
+ if (!requestJSONBufferLock(22)) return false;
+
+ sprintf_P(objKey, PSTR("\"%d\":"), button);
+
+ unsigned long start = millis();
+ while (strip.isUpdating() && millis()-start < RF433_BUSWAIT_TIMEOUT) yield(); // wait for strip to finish updating, accessing FS during sendout causes glitches
+
+ // attempt to read command from remote.json
+ readObjectFromFile(PSTR("/remote433.json"), objKey, pDoc);
+ JsonObject fdo = pDoc->as();
+ if (fdo.isNull()) {
+ // the received button does not exist
+ releaseJSONBufferLock();
+ return parsed;
+ }
+
+ String cmdStr = fdo["cmd"].as();
+ JsonObject jsonCmdObj = fdo["cmd"]; //object
+
+ if (jsonCmdObj.isNull()) // we could also use: fdo["cmd"].is()
+ {
+ // HTTP API command
+ String apireq = "win"; apireq += '&'; // reduce flash string usage
+ if (!cmdStr.startsWith(apireq)) cmdStr = apireq + cmdStr; // if no "win&" prefix
+ if (!irApplyToAllSelected && cmdStr.indexOf(F("SS="))<0) {
+ char tmp[10];
+ sprintf_P(tmp, PSTR("&SS=%d"), strip.getMainSegmentId());
+ cmdStr += tmp;
+ }
+ fdo.clear(); // clear JSON buffer (it is no longer needed)
+ handleSet(nullptr, cmdStr, false); // no stateUpdated() call here
+ stateUpdated(CALL_MODE_BUTTON);
+ parsed = true;
+ } else {
+ // command is JSON object
+ if (jsonCmdObj[F("psave")].isNull())
+ deserializeState(jsonCmdObj, CALL_MODE_BUTTON_PRESET);
+ else {
+ uint8_t psave = jsonCmdObj[F("psave")].as();
+ char pname[33];
+ sprintf_P(pname, PSTR("IR Preset %d"), psave);
+ fdo.clear();
+ if (psave > 0 && psave < 251) savePreset(psave, pname, fdo);
+ }
+ parsed = true;
+ }
+ releaseJSONBufferLock();
+ return parsed;
+ }
+};
+
+const char RF433Usermod::_modName[] PROGMEM = "RF433 Remote";
+const char RF433Usermod::_modEnabled[] PROGMEM = "Enabled";
+const char RF433Usermod::_receivePin[] PROGMEM = "RX Pin";
+
diff --git a/wled00/const.h b/wled00/const.h
index bdd20beba..6a023fadc 100644
--- a/wled00/const.h
+++ b/wled00/const.h
@@ -204,6 +204,7 @@
#define USERMOD_ID_POV_DISPLAY 53 //Usermod "usermod_pov_display.h"
#define USERMOD_ID_PIXELS_DICE_TRAY 54 //Usermod "pixels_dice_tray.h"
#define USERMOD_ID_DEEP_SLEEP 55 //Usermod "usermod_deep_sleep.h"
+#define USERMOD_ID_RF433 56 //Usermod "usermod_v2_RF433.h"
//Access point behavior
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
diff --git a/wled00/usermods_list.cpp b/wled00/usermods_list.cpp
index 3430e337d..15ded987d 100644
--- a/wled00/usermods_list.cpp
+++ b/wled00/usermods_list.cpp
@@ -242,11 +242,14 @@
#include "../usermods/LD2410_v2/usermod_ld2410.h"
#endif
-
#ifdef USERMOD_DEEP_SLEEP
#include "../usermods/deep_sleep/usermod_deep_sleep.h"
#endif
+#ifdef USERMOD_RF433
+ #include "../usermods/usermod_v2_RF433/usermod_v2_RF433.h"
+#endif
+
void registerUsermods()
{
/*
@@ -479,4 +482,8 @@ void registerUsermods()
#ifdef USERMOD_DEEP_SLEEP
UsermodManager::add(new DeepSleepUsermod());
#endif
+
+ #ifdef USERMOD_RF433
+ UsermodManager::add(new RF433Usermod());
+ #endif
}