diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index fa0397fc65..a342886d0b 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -38,8 +38,24 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { JsonObject nw = doc["nw"]; #ifndef WLED_DISABLE_ESPNOW CJSON(enableESPNow, nw[F("espnow")]); - getStringFromJson(linked_remote, nw[F("linked_remote")], 13); - linked_remote[12] = '\0'; + linked_remotes.clear(); + JsonVariant lrem = nw[F("linked_remote")]; + if (!lrem.isNull()) { + if (lrem.is()) { + for (size_t i = 0; i < lrem.size(); i++) { + std::array entry{}; + getStringFromJson(entry.data(), lrem[i], 13); + entry[12] = '\0'; + linked_remotes.emplace_back(entry); + } + } + else { // legacy support for single MAC address in config + std::array entry{}; + getStringFromJson(entry.data(), lrem, 13); + entry[12] = '\0'; + linked_remotes.emplace_back(entry); + } + } #endif size_t n = 0; @@ -725,7 +741,10 @@ void serializeConfig(JsonObject root) { JsonObject nw = root.createNestedObject("nw"); #ifndef WLED_DISABLE_ESPNOW nw[F("espnow")] = enableESPNow; - nw[F("linked_remote")] = linked_remote; + JsonArray lrem = nw.createNestedArray(F("linked_remote")); + for (size_t i = 0; i < linked_remotes.size(); i++) { + lrem.add(linked_remotes[i].data()); + } #endif JsonArray nw_ins = nw.createNestedArray("ins"); diff --git a/wled00/data/settings_wifi.htm b/wled00/data/settings_wifi.htm index 1531d161fc..61ce863229 100644 --- a/wled00/data/settings_wifi.htm +++ b/wled00/data/settings_wifi.htm @@ -136,12 +136,52 @@ getLoc(); loadJS(getURL('/settings/s.js?p=1'), false); // If we set async false, file is loaded and executed, then next statement is processed if (loc) d.Sf.action = getURL('/settings/wifi'); + setTimeout(tE, 500); // wait for DOM to load before calling tE() } + + var rC = 0; // remote count + // toggle visibility of ESP-NOW remote list based on checkbox state + function tE() { + // keep the hidden input with MAC addresses, only toggle visibility of the list UI + gId('rlc').style.display = d.Sf.RE.checked ? 'block' : 'none'; + } + // reset remotes: initialize empty list (called from xml.cpp) + function rstR() { + gId('rml').innerHTML = ''; // clear remote list + } + // add remote MAC to the list + function aR(id, mac) { + if (!/^[0-9A-F]{12}$/i.test(mac)) return; // check for valid hex string + let inputs = d.querySelectorAll("#rml input"); + for (let i of (inputs || [])) { + if (i.value === mac) return; + } + let l = gId('rml'), r = cE('div'), i = cE('input'); + i.type = 'text'; + i.name = id; + i.value = mac; + i.maxLength = 12; + i.minLength = 12; + //i.onchange = uR; + r.appendChild(i); + let b = cE('button'); + b.type = 'button'; + b.className = 'sml'; + b.innerText = '-'; + b.onclick = (e) => { + r.remove(); + }; + r.appendChild(b); + l.appendChild(r); + rC++; + gId('+').style.display = gId("rml").childElementCount < 10 ? 'inline' : 'none'; // can't append to list anymore, hide button + } + -
+

@@ -202,11 +242,16 @@

ESP-NOW Wireless

This firmware build does not include ESP-NOW support.
- Enable ESP-NOW:
+ Enable ESP-NOW:
Listen for events over ESP-NOW
Keep disabled if not using a remote or wireless sync, increases power consumption.
- Paired Remote MAC:
- Last device seen: None
+
+ Last device seen: None +
+ Linked MACs:
+
+
+
diff --git a/wled00/remote.cpp b/wled00/remote.cpp index 8c060a70ca..14c3c0d01d 100644 --- a/wled00/remote.cpp +++ b/wled00/remote.cpp @@ -181,16 +181,10 @@ static bool remoteJson(int button) return parsed; } -// Callback function that will be executed when data is received +// Callback function that will be executed when data is received from a linked remote void handleWiZdata(uint8_t *incomingData, size_t len) { message_structure_t *incoming = reinterpret_cast(incomingData); - if (strcmp(last_signal_src, linked_remote) != 0) { - DEBUG_PRINT(F("ESP Now Message Received from Unlinked Sender: ")); - DEBUG_PRINTLN(last_signal_src); - return; - } - if (len != sizeof(message_structure_t)) { DEBUG_PRINTF_P(PSTR("Unknown incoming ESP Now message received of length %u\n"), len); return; diff --git a/wled00/set.cpp b/wled00/set.cpp index c817f2553c..24671edcc8 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -91,8 +91,21 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) bool oldESPNow = enableESPNow; enableESPNow = request->hasArg(F("RE")); if (oldESPNow != enableESPNow) forceReconnect = true; - strlcpy(linked_remote, request->arg(F("RMAC")).c_str(), 13); - strlwr(linked_remote); //Normalize MAC format to lowercase + linked_remotes.clear(); // clear old remotes + for (size_t n = 0; n < 10; n++) { + char rm[4]; + snprintf(rm, sizeof(rm), "RM%d", n); // "RM0" to "RM9" + if (request->hasArg(rm)) { + const String& arg = request->arg(rm); + if (arg.isEmpty()) continue; + std::array mac{}; + strlcpy(mac.data(), request->arg(rm).c_str(), 13); + strlwr(mac.data()); + if (mac[0] != '\0') { + linked_remotes.emplace_back(mac); + } + } + } #endif #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET) diff --git a/wled00/udp.cpp b/wled00/udp.cpp index 4395b285d0..c2d450b9ee 100644 --- a/wled00/udp.cpp +++ b/wled00/udp.cpp @@ -959,14 +959,22 @@ void espNowReceiveCB(uint8_t* address, uint8_t* data, uint8_t len, signed int rs // usermods hook can override processing if (UsermodManager::onEspNowMessage(address, data, len)) return; - // handle WiZ Mote data - if (data[0] == 0x91 || data[0] == 0x81 || data[0] == 0x80) { - handleWiZdata(data, len); + bool knownRemote = false; + for (const auto& mac : linked_remotes) { + if (strlen(mac.data()) == 12 && strcmp(last_signal_src, mac.data()) == 0) { + knownRemote = true; + break; + } + } + if (!knownRemote) { + DEBUG_PRINT(F("ESP Now Message Received from Unlinked Sender: ")); + DEBUG_PRINTLN(last_signal_src); return; } - if (strlen(linked_remote) == 12 && strcmp(last_signal_src, linked_remote) != 0) { - DEBUG_PRINTLN(F("ESP-NOW unpaired remote sender.")); + // handle WiZ Mote data + if (data[0] == 0x91 || data[0] == 0x81 || data[0] == 0x80) { + handleWiZdata(data, len); return; } diff --git a/wled00/wled.h b/wled00/wled.h index f8dc1252a8..a74df05e3a 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -538,7 +538,8 @@ WLED_GLOBAL bool serialCanTX _INIT(false); WLED_GLOBAL bool enableESPNow _INIT(false); // global on/off for ESP-NOW WLED_GLOBAL byte statusESPNow _INIT(ESP_NOW_STATE_UNINIT); // state of ESP-NOW stack (0 uninitialised, 1 initialised, 2 error) WLED_GLOBAL bool useESPNowSync _INIT(false); // use ESP-NOW wireless technology for sync -WLED_GLOBAL char linked_remote[13] _INIT(""); // MAC of ESP-NOW remote (Wiz Mote) +//WLED_GLOBAL char linked_remote[13] _INIT(""); // MAC of ESP-NOW remote (Wiz Mote) +WLED_GLOBAL std::vector> linked_remotes; // MAC of ESP-NOW remotes (Wiz Mote) WLED_GLOBAL char last_signal_src[13] _INIT(""); // last seen ESP-NOW sender #endif diff --git a/wled00/xml.cpp b/wled00/xml.cpp index 19868d01d9..d6d4abf4a2 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -216,7 +216,11 @@ void getSettingsJS(byte subPage, Print& settingsScript) #ifndef WLED_DISABLE_ESPNOW printSetFormCheckbox(settingsScript,PSTR("RE"),enableESPNow); - printSetFormValue(settingsScript,PSTR("RMAC"),linked_remote); + settingsScript.printf_P(PSTR("rstR();")); // reset remote list + for (size_t i = 0; i < linked_remotes.size(); i++) { + settingsScript.printf_P(PSTR("aR(\"RM%u\",\"%s\");"), i, linked_remotes[i].data()); // add remote to list + } + settingsScript.print(F("tE();")); // fill fields #else //hide remote settings if not compiled settingsScript.print(F("toggle('ESPNOW');")); // hide ESP-NOW setting @@ -258,10 +262,6 @@ void getSettingsJS(byte subPage, Print& settingsScript) #ifndef WLED_DISABLE_ESPNOW if (strlen(last_signal_src) > 0) { //Have seen an ESP-NOW Remote printSetClassElementHTML(settingsScript,PSTR("rlid"),0,last_signal_src); - } else if (!enableESPNow) { - printSetClassElementHTML(settingsScript,PSTR("rlid"),0,(char*)F("(Enable ESP-NOW to listen)")); - } else { - printSetClassElementHTML(settingsScript,PSTR("rlid"),0,(char*)F("None")); } #endif }