Skip to content

Commit b9aeb19

Browse files
kilrahKilrah
and
Kilrah
authored
RF433 json usermod (wled#4234)
* RF433 remote usermod --------- Co-authored-by: Kilrah <[email protected]>
1 parent a4c3491 commit b9aeb19

File tree

6 files changed

+255
-1
lines changed

6 files changed

+255
-1
lines changed

platformio_override.sample.ini

+11
Original file line numberDiff line numberDiff line change
@@ -529,3 +529,14 @@ monitor_filters = esp32_exception_decoder
529529
lib_deps =
530530
${esp32.lib_deps}
531531
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
532+
533+
# ------------------------------------------------------------------------------
534+
# Usermod examples
535+
# ------------------------------------------------------------------------------
536+
537+
# 433MHz RF remote example for esp32dev
538+
[env:esp32dev_usermod_RF433]
539+
extends = env:esp32dev
540+
build_flags = ${env:esp32dev.build_flags} -D USERMOD_RF433
541+
lib_deps = ${env:esp32dev.lib_deps}
542+
sui77/rc-switch @ 2.6.4

usermods/usermod_v2_RF433/readme.md

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# RF433 remote usermod
2+
3+
Usermod for controlling WLED using a generic 433 / 315MHz remote and simple 3-pin receiver
4+
See <https://github.com/sui77/rc-switch/> for compatibility details
5+
6+
## Build
7+
8+
- Create a `platformio_override.ini` file at the root of the wled source directory if not already present
9+
- Copy the `433MHz RF remote example for esp32dev` section from `platformio_override.sample.ini` into it
10+
- Duplicate/adjust for other boards
11+
12+
## Usage
13+
14+
- Connect receiver to a free pin
15+
- Set pin in Config->Usermods
16+
- Info pane will show the last received button code
17+
- Upload the remote433.json sample file in this folder to the ESP with the file editor at [http://\[wled-ip\]/edit](http://ip/edit)
18+
- 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.
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"13985576": {
3+
"cmnt": "Toggle Power using HTTP API",
4+
"cmd": "T=2"
5+
},
6+
"3670817": {
7+
"cmnt": "Force Power ON using HTTP API",
8+
"cmd": "T=1"
9+
},
10+
"13985572": {
11+
"cmnt": "Set brightness to 200 using JSON API",
12+
"cmd": {"bri":200}
13+
},
14+
"3670818": {
15+
"cmnt": "Run Preset 1 using JSON API",
16+
"cmd": {"ps":1}
17+
},
18+
"13985570": {
19+
"cmnt": "Increase brightness by 40 using HTTP API",
20+
"cmd": "A=~40"
21+
},
22+
"13985569": {
23+
"cmnt": "Decrease brightness by 40 using HTTP API",
24+
"cmd": "A=~-40"
25+
},
26+
"7608836": {
27+
"cmnt": "Start 1min timer using JSON API",
28+
"cmd": {"nl":{"on":true,"dur":1,"mode":0}}
29+
},
30+
"7608840": {
31+
"cmnt": "Select random effect on all segments using JSON API",
32+
"cmd": {"seg":{"fx":"r"}}
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
#pragma once
2+
3+
#include "wled.h"
4+
#include "Arduino.h"
5+
#include <RCSwitch.h>
6+
7+
#define RF433_BUSWAIT_TIMEOUT 24
8+
9+
class RF433Usermod : public Usermod
10+
{
11+
private:
12+
RCSwitch mySwitch = RCSwitch();
13+
unsigned long lastCommand = 0;
14+
unsigned long lastTime = 0;
15+
16+
bool modEnabled = true;
17+
int8_t receivePin = -1;
18+
19+
static const char _modName[];
20+
static const char _modEnabled[];
21+
static const char _receivePin[];
22+
23+
bool initDone = false;
24+
25+
public:
26+
27+
void setup()
28+
{
29+
mySwitch.disableReceive();
30+
if (modEnabled)
31+
{
32+
mySwitch.enableReceive(receivePin);
33+
}
34+
initDone = true;
35+
}
36+
37+
/*
38+
* connected() is called every time the WiFi is (re)connected
39+
* Use it to initialize network interfaces
40+
*/
41+
void connected()
42+
{
43+
}
44+
45+
void loop()
46+
{
47+
if (!modEnabled || strip.isUpdating())
48+
return;
49+
50+
if (mySwitch.available())
51+
{
52+
unsigned long receivedCommand = mySwitch.getReceivedValue();
53+
mySwitch.resetAvailable();
54+
55+
// Discard duplicates, limit long press repeat
56+
if (lastCommand == receivedCommand && millis() - lastTime < 800)
57+
return;
58+
59+
lastCommand = receivedCommand;
60+
lastTime = millis();
61+
62+
DEBUG_PRINT(F("RF433 Receive: "));
63+
DEBUG_PRINTLN(receivedCommand);
64+
65+
if(!remoteJson433(receivedCommand))
66+
DEBUG_PRINTLN(F("RF433: unknown button"));
67+
}
68+
}
69+
70+
// Add last received button to info pane
71+
void addToJsonInfo(JsonObject &root)
72+
{
73+
if (!initDone)
74+
return; // prevent crash on boot applyPreset()
75+
JsonObject user = root["u"];
76+
if (user.isNull())
77+
user = root.createNestedObject("u");
78+
79+
JsonArray switchArr = user.createNestedArray("RF433 Last Received"); // name
80+
switchArr.add(lastCommand);
81+
}
82+
83+
void addToConfig(JsonObject &root)
84+
{
85+
JsonObject top = root.createNestedObject(FPSTR(_modName)); // usermodname
86+
top[FPSTR(_modEnabled)] = modEnabled;
87+
JsonArray pinArray = top.createNestedArray("pin");
88+
pinArray.add(receivePin);
89+
90+
DEBUG_PRINTLN(F(" config saved."));
91+
}
92+
93+
bool readFromConfig(JsonObject &root)
94+
{
95+
JsonObject top = root[FPSTR(_modName)];
96+
if (top.isNull())
97+
{
98+
DEBUG_PRINT(FPSTR(_modName));
99+
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
100+
return false;
101+
}
102+
getJsonValue(top[FPSTR(_modEnabled)], modEnabled);
103+
getJsonValue(top["pin"][0], receivePin);
104+
105+
DEBUG_PRINTLN(F("config (re)loaded."));
106+
107+
// Redo init on update
108+
if(initDone)
109+
setup();
110+
111+
return true;
112+
}
113+
114+
/*
115+
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
116+
* This could be used in the future for the system to determine whether your usermod is installed.
117+
*/
118+
uint16_t getId()
119+
{
120+
return USERMOD_ID_RF433;
121+
}
122+
123+
// this function follows the same principle as decodeIRJson() / remoteJson()
124+
bool remoteJson433(int button)
125+
{
126+
char objKey[14];
127+
bool parsed = false;
128+
129+
if (!requestJSONBufferLock(22)) return false;
130+
131+
sprintf_P(objKey, PSTR("\"%d\":"), button);
132+
133+
unsigned long start = millis();
134+
while (strip.isUpdating() && millis()-start < RF433_BUSWAIT_TIMEOUT) yield(); // wait for strip to finish updating, accessing FS during sendout causes glitches
135+
136+
// attempt to read command from remote.json
137+
readObjectFromFile(PSTR("/remote433.json"), objKey, pDoc);
138+
JsonObject fdo = pDoc->as<JsonObject>();
139+
if (fdo.isNull()) {
140+
// the received button does not exist
141+
releaseJSONBufferLock();
142+
return parsed;
143+
}
144+
145+
String cmdStr = fdo["cmd"].as<String>();
146+
JsonObject jsonCmdObj = fdo["cmd"]; //object
147+
148+
if (jsonCmdObj.isNull()) // we could also use: fdo["cmd"].is<String>()
149+
{
150+
// HTTP API command
151+
String apireq = "win"; apireq += '&'; // reduce flash string usage
152+
if (!cmdStr.startsWith(apireq)) cmdStr = apireq + cmdStr; // if no "win&" prefix
153+
if (!irApplyToAllSelected && cmdStr.indexOf(F("SS="))<0) {
154+
char tmp[10];
155+
sprintf_P(tmp, PSTR("&SS=%d"), strip.getMainSegmentId());
156+
cmdStr += tmp;
157+
}
158+
fdo.clear(); // clear JSON buffer (it is no longer needed)
159+
handleSet(nullptr, cmdStr, false); // no stateUpdated() call here
160+
stateUpdated(CALL_MODE_BUTTON);
161+
parsed = true;
162+
} else {
163+
// command is JSON object
164+
if (jsonCmdObj[F("psave")].isNull())
165+
deserializeState(jsonCmdObj, CALL_MODE_BUTTON_PRESET);
166+
else {
167+
uint8_t psave = jsonCmdObj[F("psave")].as<int>();
168+
char pname[33];
169+
sprintf_P(pname, PSTR("IR Preset %d"), psave);
170+
fdo.clear();
171+
if (psave > 0 && psave < 251) savePreset(psave, pname, fdo);
172+
}
173+
parsed = true;
174+
}
175+
releaseJSONBufferLock();
176+
return parsed;
177+
}
178+
};
179+
180+
const char RF433Usermod::_modName[] PROGMEM = "RF433 Remote";
181+
const char RF433Usermod::_modEnabled[] PROGMEM = "Enabled";
182+
const char RF433Usermod::_receivePin[] PROGMEM = "RX Pin";
183+

wled00/const.h

+1
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@
204204
#define USERMOD_ID_POV_DISPLAY 53 //Usermod "usermod_pov_display.h"
205205
#define USERMOD_ID_PIXELS_DICE_TRAY 54 //Usermod "pixels_dice_tray.h"
206206
#define USERMOD_ID_DEEP_SLEEP 55 //Usermod "usermod_deep_sleep.h"
207+
#define USERMOD_ID_RF433 56 //Usermod "usermod_v2_RF433.h"
207208

208209
//Access point behavior
209210
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot

wled00/usermods_list.cpp

+8-1
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,14 @@
242242
#include "../usermods/LD2410_v2/usermod_ld2410.h"
243243
#endif
244244

245-
246245
#ifdef USERMOD_DEEP_SLEEP
247246
#include "../usermods/deep_sleep/usermod_deep_sleep.h"
248247
#endif
249248

249+
#ifdef USERMOD_RF433
250+
#include "../usermods/usermod_v2_RF433/usermod_v2_RF433.h"
251+
#endif
252+
250253
void registerUsermods()
251254
{
252255
/*
@@ -479,4 +482,8 @@ void registerUsermods()
479482
#ifdef USERMOD_DEEP_SLEEP
480483
UsermodManager::add(new DeepSleepUsermod());
481484
#endif
485+
486+
#ifdef USERMOD_RF433
487+
UsermodManager::add(new RF433Usermod());
488+
#endif
482489
}

0 commit comments

Comments
 (0)