Skip to content

Commit 66ad27a

Browse files
authored
add support for up to 10 ESPNow remotes (wled#4654)
* add support for up to 10 ESPNow remotes * removed debug line * changed todo comment * fixed some issues, shortened html/java code - reverting name to `linked_remote` - ESPNow remote list is now hidden if unchecked - shortened java script function names and variables to save flash - removed now obsolete settings in xml.cpp - correct checking of valid hex string for remote list in java script * fixed indentation, using emplace_back instead of push_back, using JsonVariant, replaced buttons with +/- * shortened java code * updated java code, fixed bug - element is now properly removed - `+` button is hidden if list is full - user needs to remove a remote, then reload the page to add it (workaround for edge case that needs more code to handle otherwise) * add limit * clearer usage description
1 parent d9ad4ec commit 66ad27a

File tree

7 files changed

+108
-28
lines changed

7 files changed

+108
-28
lines changed

wled00/cfg.cpp

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,24 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
3838
JsonObject nw = doc["nw"];
3939
#ifndef WLED_DISABLE_ESPNOW
4040
CJSON(enableESPNow, nw[F("espnow")]);
41-
getStringFromJson(linked_remote, nw[F("linked_remote")], 13);
42-
linked_remote[12] = '\0';
41+
linked_remotes.clear();
42+
JsonVariant lrem = nw[F("linked_remote")];
43+
if (!lrem.isNull()) {
44+
if (lrem.is<JsonArray>()) {
45+
for (size_t i = 0; i < lrem.size(); i++) {
46+
std::array<char, 13> entry{};
47+
getStringFromJson(entry.data(), lrem[i], 13);
48+
entry[12] = '\0';
49+
linked_remotes.emplace_back(entry);
50+
}
51+
}
52+
else { // legacy support for single MAC address in config
53+
std::array<char, 13> entry{};
54+
getStringFromJson(entry.data(), lrem, 13);
55+
entry[12] = '\0';
56+
linked_remotes.emplace_back(entry);
57+
}
58+
}
4359
#endif
4460

4561
size_t n = 0;
@@ -725,7 +741,10 @@ void serializeConfig(JsonObject root) {
725741
JsonObject nw = root.createNestedObject("nw");
726742
#ifndef WLED_DISABLE_ESPNOW
727743
nw[F("espnow")] = enableESPNow;
728-
nw[F("linked_remote")] = linked_remote;
744+
JsonArray lrem = nw.createNestedArray(F("linked_remote"));
745+
for (size_t i = 0; i < linked_remotes.size(); i++) {
746+
lrem.add(linked_remotes[i].data());
747+
}
729748
#endif
730749

731750
JsonArray nw_ins = nw.createNestedArray("ins");

wled00/data/settings_wifi.htm

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,52 @@
136136
getLoc();
137137
loadJS(getURL('/settings/s.js?p=1'), false); // If we set async false, file is loaded and executed, then next statement is processed
138138
if (loc) d.Sf.action = getURL('/settings/wifi');
139+
setTimeout(tE, 500); // wait for DOM to load before calling tE()
139140
}
141+
142+
var rC = 0; // remote count
143+
// toggle visibility of ESP-NOW remote list based on checkbox state
144+
function tE() {
145+
// keep the hidden input with MAC addresses, only toggle visibility of the list UI
146+
gId('rlc').style.display = d.Sf.RE.checked ? 'block' : 'none';
147+
}
148+
// reset remotes: initialize empty list (called from xml.cpp)
149+
function rstR() {
150+
gId('rml').innerHTML = ''; // clear remote list
151+
}
152+
// add remote MAC to the list
153+
function aR(id, mac) {
154+
if (!/^[0-9A-F]{12}$/i.test(mac)) return; // check for valid hex string
155+
let inputs = d.querySelectorAll("#rml input");
156+
for (let i of (inputs || [])) {
157+
if (i.value === mac) return;
158+
}
159+
let l = gId('rml'), r = cE('div'), i = cE('input');
160+
i.type = 'text';
161+
i.name = id;
162+
i.value = mac;
163+
i.maxLength = 12;
164+
i.minLength = 12;
165+
//i.onchange = uR;
166+
r.appendChild(i);
167+
let b = cE('button');
168+
b.type = 'button';
169+
b.className = 'sml';
170+
b.innerText = '-';
171+
b.onclick = (e) => {
172+
r.remove();
173+
};
174+
r.appendChild(b);
175+
l.appendChild(r);
176+
rC++;
177+
gId('+').style.display = gId("rml").childElementCount < 10 ? 'inline' : 'none'; // can't append to list anymore, hide button
178+
}
179+
140180
</script>
141181
<style>@import url("style.css");</style>
142182
</head>
143183
<body onload="S()">
144-
<form id="form_s" name="Sf" method="post">
184+
<form id="form_s" name="Sf" method="post">
145185
<div class="toprow">
146186
<div class="helpB"><button type="button" onclick="H('features/settings/#wifi-settings')">?</button></div>
147187
<button type="button" onclick="B()">Back</button><button type="submit">Save & Connect</button><hr>
@@ -202,11 +242,16 @@ <h3>ESP-NOW Wireless</h3>
202242
<i class="warn">This firmware build does not include ESP-NOW support.<br></i>
203243
</div>
204244
<div id="ESPNOW">
205-
Enable ESP-NOW: <input type="checkbox" name="RE"><br>
245+
Enable ESP-NOW: <input type="checkbox" name="RE" onchange="tE()"><br>
206246
<i>Listen for events over ESP-NOW<br>
207-
Keep disabled if not using a remote or wireless sync, increases power consumption.<br></i>
208-
Paired Remote MAC: <input type="text" name="RMAC" minlength="12" maxlength="12"><br>
209-
Last device seen: <span class="rlid" onclick="d.Sf.RMAC.value=this.textContent;" style="cursor:pointer;">None</span> <br>
247+
Keep disabled if not using a remote or ESP-NOW sync, increases power consumption.<br></i>
248+
<div id="rlc">
249+
Last device seen: <span class="rlid" id="ld">None</span>
250+
<button type="button" class="sml" id="+" onclick="aR('RM'+rC,gId('ld').textContent)">+</button><br>
251+
Linked MACs (10 max):<br>
252+
<div id="rml">
253+
</div>
254+
</div>
210255
</div>
211256

212257
<div id="ethd">

wled00/remote.cpp

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -181,16 +181,10 @@ static bool remoteJson(int button)
181181
return parsed;
182182
}
183183

184-
// Callback function that will be executed when data is received
184+
// Callback function that will be executed when data is received from a linked remote
185185
void handleWiZdata(uint8_t *incomingData, size_t len) {
186186
message_structure_t *incoming = reinterpret_cast<message_structure_t *>(incomingData);
187187

188-
if (strcmp(last_signal_src, linked_remote) != 0) {
189-
DEBUG_PRINT(F("ESP Now Message Received from Unlinked Sender: "));
190-
DEBUG_PRINTLN(last_signal_src);
191-
return;
192-
}
193-
194188
if (len != sizeof(message_structure_t)) {
195189
DEBUG_PRINTF_P(PSTR("Unknown incoming ESP Now message received of length %u\n"), len);
196190
return;

wled00/set.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,21 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
9191
bool oldESPNow = enableESPNow;
9292
enableESPNow = request->hasArg(F("RE"));
9393
if (oldESPNow != enableESPNow) forceReconnect = true;
94-
strlcpy(linked_remote, request->arg(F("RMAC")).c_str(), 13);
95-
strlwr(linked_remote); //Normalize MAC format to lowercase
94+
linked_remotes.clear(); // clear old remotes
95+
for (size_t n = 0; n < 10; n++) {
96+
char rm[4];
97+
snprintf(rm, sizeof(rm), "RM%d", n); // "RM0" to "RM9"
98+
if (request->hasArg(rm)) {
99+
const String& arg = request->arg(rm);
100+
if (arg.isEmpty()) continue;
101+
std::array<char, 13> mac{};
102+
strlcpy(mac.data(), request->arg(rm).c_str(), 13);
103+
strlwr(mac.data());
104+
if (mac[0] != '\0') {
105+
linked_remotes.emplace_back(mac);
106+
}
107+
}
108+
}
96109
#endif
97110

98111
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)

wled00/udp.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -959,14 +959,22 @@ void espNowReceiveCB(uint8_t* address, uint8_t* data, uint8_t len, signed int rs
959959
// usermods hook can override processing
960960
if (UsermodManager::onEspNowMessage(address, data, len)) return;
961961

962-
// handle WiZ Mote data
963-
if (data[0] == 0x91 || data[0] == 0x81 || data[0] == 0x80) {
964-
handleWiZdata(data, len);
962+
bool knownRemote = false;
963+
for (const auto& mac : linked_remotes) {
964+
if (strlen(mac.data()) == 12 && strcmp(last_signal_src, mac.data()) == 0) {
965+
knownRemote = true;
966+
break;
967+
}
968+
}
969+
if (!knownRemote) {
970+
DEBUG_PRINT(F("ESP Now Message Received from Unlinked Sender: "));
971+
DEBUG_PRINTLN(last_signal_src);
965972
return;
966973
}
967974

968-
if (strlen(linked_remote) == 12 && strcmp(last_signal_src, linked_remote) != 0) {
969-
DEBUG_PRINTLN(F("ESP-NOW unpaired remote sender."));
975+
// handle WiZ Mote data
976+
if (data[0] == 0x91 || data[0] == 0x81 || data[0] == 0x80) {
977+
handleWiZdata(data, len);
970978
return;
971979
}
972980

wled00/wled.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,8 @@ WLED_GLOBAL bool serialCanTX _INIT(false);
538538
WLED_GLOBAL bool enableESPNow _INIT(false); // global on/off for ESP-NOW
539539
WLED_GLOBAL byte statusESPNow _INIT(ESP_NOW_STATE_UNINIT); // state of ESP-NOW stack (0 uninitialised, 1 initialised, 2 error)
540540
WLED_GLOBAL bool useESPNowSync _INIT(false); // use ESP-NOW wireless technology for sync
541-
WLED_GLOBAL char linked_remote[13] _INIT(""); // MAC of ESP-NOW remote (Wiz Mote)
541+
//WLED_GLOBAL char linked_remote[13] _INIT(""); // MAC of ESP-NOW remote (Wiz Mote)
542+
WLED_GLOBAL std::vector<std::array<char, 13>> linked_remotes; // MAC of ESP-NOW remotes (Wiz Mote)
542543
WLED_GLOBAL char last_signal_src[13] _INIT(""); // last seen ESP-NOW sender
543544
#endif
544545

wled00/xml.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,11 @@ void getSettingsJS(byte subPage, Print& settingsScript)
216216

217217
#ifndef WLED_DISABLE_ESPNOW
218218
printSetFormCheckbox(settingsScript,PSTR("RE"),enableESPNow);
219-
printSetFormValue(settingsScript,PSTR("RMAC"),linked_remote);
219+
settingsScript.printf_P(PSTR("rstR();")); // reset remote list
220+
for (size_t i = 0; i < linked_remotes.size(); i++) {
221+
settingsScript.printf_P(PSTR("aR(\"RM%u\",\"%s\");"), i, linked_remotes[i].data()); // add remote to list
222+
}
223+
settingsScript.print(F("tE();")); // fill fields
220224
#else
221225
//hide remote settings if not compiled
222226
settingsScript.print(F("toggle('ESPNOW');")); // hide ESP-NOW setting
@@ -258,10 +262,6 @@ void getSettingsJS(byte subPage, Print& settingsScript)
258262
#ifndef WLED_DISABLE_ESPNOW
259263
if (strlen(last_signal_src) > 0) { //Have seen an ESP-NOW Remote
260264
printSetClassElementHTML(settingsScript,PSTR("rlid"),0,last_signal_src);
261-
} else if (!enableESPNow) {
262-
printSetClassElementHTML(settingsScript,PSTR("rlid"),0,(char*)F("(Enable ESP-NOW to listen)"));
263-
} else {
264-
printSetClassElementHTML(settingsScript,PSTR("rlid"),0,(char*)F("None"));
265265
}
266266
#endif
267267
}

0 commit comments

Comments
 (0)