mirror of
https://github.com/wled/WLED.git
synced 2025-07-19 00:36:36 +00:00
add support for up to 10 ESPNow remotes (#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
This commit is contained in:
parent
d9ad4ec743
commit
66ad27ad3a
@ -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<JsonArray>()) {
|
||||
for (size_t i = 0; i < lrem.size(); i++) {
|
||||
std::array<char, 13> 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<char, 13> 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");
|
||||
|
@ -136,12 +136,52 @@ Static subnet mask:<br>
|
||||
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
|
||||
}
|
||||
|
||||
</script>
|
||||
<style>@import url("style.css");</style>
|
||||
</head>
|
||||
<body onload="S()">
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<div class="toprow">
|
||||
<div class="helpB"><button type="button" onclick="H('features/settings/#wifi-settings')">?</button></div>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save & Connect</button><hr>
|
||||
@ -202,11 +242,16 @@ Static subnet mask:<br>
|
||||
<i class="warn">This firmware build does not include ESP-NOW support.<br></i>
|
||||
</div>
|
||||
<div id="ESPNOW">
|
||||
Enable ESP-NOW: <input type="checkbox" name="RE"><br>
|
||||
Enable ESP-NOW: <input type="checkbox" name="RE" onchange="tE()"><br>
|
||||
<i>Listen for events over ESP-NOW<br>
|
||||
Keep disabled if not using a remote or wireless sync, increases power consumption.<br></i>
|
||||
Paired Remote MAC: <input type="text" name="RMAC" minlength="12" maxlength="12"><br>
|
||||
Last device seen: <span class="rlid" onclick="d.Sf.RMAC.value=this.textContent;" style="cursor:pointer;">None</span> <br>
|
||||
Keep disabled if not using a remote or ESP-NOW sync, increases power consumption.<br></i>
|
||||
<div id="rlc">
|
||||
Last device seen: <span class="rlid" id="ld">None</span>
|
||||
<button type="button" class="sml" id="+" onclick="aR('RM'+rC,gId('ld').textContent)">+</button><br>
|
||||
Linked MACs (10 max):<br>
|
||||
<div id="rml">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="ethd">
|
||||
|
@ -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<message_structure_t *>(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;
|
||||
|
@ -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<char, 13> 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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<std::array<char, 13>> linked_remotes; // MAC of ESP-NOW remotes (Wiz Mote)
|
||||
WLED_GLOBAL char last_signal_src[13] _INIT(""); // last seen ESP-NOW sender
|
||||
#endif
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user