Skip to content

Commit dd433d8

Browse files
committed
Auto node discovery.
1 parent 0266370 commit dd433d8

File tree

9 files changed

+601
-393
lines changed

9 files changed

+601
-393
lines changed

wled00/NodeStruct.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include "NodeStruct.h"
2+
3+
String getNodeTypeDisplayString(uint8_t nodeType) {
4+
switch (nodeType)
5+
{
6+
case NODE_TYPE_ID_ESP8266: return F("ESP8266");
7+
case NODE_TYPE_ID_ESP32: return F("ESP32");
8+
}
9+
return "";
10+
}
11+
12+
NodeStruct::NodeStruct() :
13+
age(0), nodeType(0)
14+
{
15+
for (uint8_t i = 0; i < 4; ++i) { ip[i] = 0; }
16+
}

wled00/NodeStruct.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#ifndef DATASTRUCTS_NODESTRUCT_H
2+
#define DATASTRUCTS_NODESTRUCT_H
3+
4+
/*********************************************************************************************\
5+
* NodeStruct from the ESP Easy project (https://github.com/letscontrolit/ESPEasy)
6+
\*********************************************************************************************/
7+
8+
#include <map>
9+
#include <IPAddress.h>
10+
11+
12+
#define NODE_TYPE_ID_UNDEFINED 0
13+
#define NODE_TYPE_ID_ESP8266 1
14+
#define NODE_TYPE_ID_ESP32 2
15+
16+
String getNodeTypeDisplayString(uint8_t nodeType);
17+
18+
/*********************************************************************************************\
19+
* NodeStruct
20+
\*********************************************************************************************/
21+
struct NodeStruct
22+
{
23+
NodeStruct();
24+
25+
String nodeName;
26+
IPAddress ip;
27+
uint8_t unit;
28+
uint8_t age;
29+
uint8_t nodeType;
30+
};
31+
typedef std::map<uint8_t, NodeStruct> NodesMap;
32+
33+
#endif // DATASTRUCTS_NODESTRUCT_H

wled00/data/index.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,16 @@ function populateInfo(i)
463463
}
464464
}
465465
}
466+
if (i.nodes) {
467+
for (var x=0;x<i.nodes.length;x++)
468+
{
469+
var o = i.nodes[x];
470+
if (o.name) {
471+
var url = `<button class="btn btna-icon tab" onclick="location.assign('http://${o.ip}');">${o.name}</button>`;
472+
urows += inforow(url,o.type);
473+
}
474+
}
475+
}
466476
var vcn = "Kuuhaku";
467477
if (i.ver.startsWith("0.11.")) vcn = "Mirai";
468478
if (i.cn) vcn = i.cn;

wled00/fcn_declare.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ void notify(byte callMode, bool followUp=false);
187187
void realtimeLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC);
188188
void handleNotifications();
189189
void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w);
190+
void refreshNodeList();
191+
void sendSysInfoUDP(uint8_t repeats=1);
190192

191193
//um_manager.cpp
192194
class Usermod {

wled00/html_ui.h

Lines changed: 395 additions & 391 deletions
Large diffs are not rendered by default.

wled00/json.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,24 @@ void serializeInfo(JsonObject root)
533533
root[F("brand")] = "WLED";
534534
root[F("product")] = F("FOSS");
535535
root["mac"] = escapedMac;
536+
537+
JsonArray nodes = root.createNestedArray("nodes");
538+
IPAddress ip = WiFi.localIP();
539+
for (NodesMap::iterator it = Nodes.begin(); it != Nodes.end(); ++it)
540+
{
541+
if (it->second.ip[0] != 0)
542+
{
543+
bool isThisUnit = it->first == ip[3];
544+
545+
if (isThisUnit) continue;
546+
547+
JsonObject node = nodes.createNestedObject();
548+
node[F("name")] = it->second.nodeName;
549+
node[F("type")] = getNodeTypeDisplayString(it->second.nodeType);
550+
node[F("ip")] = it->second.ip.toString();
551+
node[F("age")] = it->second.age;
552+
}
553+
}
536554
}
537555

538556
void serveJson(AsyncWebServerRequest* request)

wled00/udp.cpp

Lines changed: 122 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,32 @@ void handleNotifications()
163163
if (!isSupp && notifierUdp.remoteIP() == Network.localIP()) return; //don't process broadcasts we send ourselves
164164

165165
uint8_t udpIn[packetSize +1];
166-
if (isSupp) notifier2Udp.read(udpIn, packetSize);
167-
else notifierUdp.read(udpIn, packetSize);
166+
uint16_t len;
167+
if (isSupp) len = notifier2Udp.read(udpIn, packetSize);
168+
else len = notifierUdp.read(udpIn, packetSize);
169+
170+
// WLED nodes info notifications
171+
if (isSupp && udpIn[0] == 255 && udpIn[1] == 1 && len >= 40) {
172+
// IPAddress remoteIP = notifier2Udp.remoteIP();
173+
174+
uint8_t unit = udpIn[39];
175+
Nodes[unit].age = 0; // Create a new element when not present
176+
NodesMap::iterator it = Nodes.find(unit);
177+
178+
if (it != Nodes.end()) {
179+
for (byte x = 0; x < 4; x++) {
180+
it->second.ip[x] = udpIn[x + 2];
181+
}
182+
it->second.age = 0; // reset 'age counter'
183+
char tmpNodeName[33] = { 0 };
184+
memcpy(&tmpNodeName[0], reinterpret_cast<byte *>(&udpIn[6]), 32);
185+
tmpNodeName[32] = 0;
186+
it->second.nodeName = tmpNodeName;
187+
it->second.nodeName.trim();
188+
it->second.nodeType = udpIn[38];
189+
}
190+
return;
191+
}
168192

169193
//wled notifier, ignore if realtime packets active
170194
if (udpIn[0] == 0 && !realtimeMode && receiveNotifications)
@@ -349,3 +373,99 @@ void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w)
349373
}
350374
}
351375
}
376+
377+
/*********************************************************************************************\
378+
Refresh aging for remote units, drop if too old...
379+
\*********************************************************************************************/
380+
void refreshNodeList()
381+
{
382+
for (NodesMap::iterator it = Nodes.begin(); it != Nodes.end();) {
383+
bool mustRemove = true;
384+
385+
if (it->second.ip[0] != 0) {
386+
if (it->second.age < 10) {
387+
it->second.age++;
388+
mustRemove = false;
389+
++it;
390+
}
391+
}
392+
393+
if (mustRemove) {
394+
it = Nodes.erase(it);
395+
}
396+
}
397+
}
398+
399+
/*********************************************************************************************\
400+
Broadcast system info to other nodes. (to update node lists)
401+
\*********************************************************************************************/
402+
void sendSysInfoUDP(uint8_t repeats)
403+
{
404+
if (!udpConnected || !repeats) {
405+
return;
406+
}
407+
408+
IPAddress ip = WiFi.localIP();
409+
410+
// TODO: make a nice struct of it and clean up
411+
// 0: 1 byte 'binary token 255'
412+
// 1: 1 byte id '1'
413+
// 2: 4 byte ip
414+
// 6: 32 char name
415+
// 38: 1 byte node type id
416+
// 39: 1 byte node id
417+
// 40 bytes total
418+
419+
// send my info to the world...
420+
for (;repeats--;)
421+
{
422+
/*
423+
escapedMac // mac address
424+
*/
425+
426+
uint8_t data[40] = {0};
427+
data[0] = 255;
428+
data[1] = 1;
429+
430+
for (byte x = 0; x < 4; x++) {
431+
data[x + 2] = ip[x];
432+
}
433+
memcpy((byte *)data + 6, serverDescription, 32);
434+
#ifdef ESP8266
435+
data[38] = NODE_TYPE_ID_ESP8266;
436+
#elif defined(ARDUINO_ARCH_ESP32)
437+
data[38] = NODE_TYPE_ID_ESP32;
438+
#else
439+
data[38] = NODE_TYPE_ID_UNDEFINED;
440+
#endif
441+
data[39] = ip[3]; // unit ID == last IP number
442+
443+
IPAddress broadcastIP(255, 255, 255, 255);
444+
notifier2Udp.beginPacket(broadcastIP, udpPort2);
445+
notifier2Udp.write(data, 40);
446+
notifier2Udp.endPacket();
447+
448+
if (repeats) delay(500);
449+
}
450+
451+
Nodes[ip[3]].age = 0; // Create new node when not already present.
452+
// store my own info also in the list
453+
NodesMap::iterator it = Nodes.find(ip[3]);
454+
455+
if (it != Nodes.end())
456+
{
457+
for (byte x = 0; x < 4; x++) {
458+
it->second.ip[x] = ip[x];
459+
}
460+
it->second.age = 0;
461+
it->second.nodeName = serverDescription;
462+
#ifdef ESP8266
463+
it->second.nodeType = NODE_TYPE_ID_ESP8266;
464+
#elif defined(ARDUINO_ARCH_ESP32)
465+
it->second.nodeType = NODE_TYPE_ID_ESP32;
466+
#else
467+
it->second.nodeType = NODE_TYPE_ID_UNDEFINED;
468+
#endif
469+
it->second.unit = ip[3];
470+
}
471+
}

wled00/wled.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,8 @@ void WLED::loop()
216216
if (millis() - lastMqttReconnectAttempt > 30000) {
217217
if (lastMqttReconnectAttempt > millis()) rolloverMillis++; //millis() rolls over every 50 days
218218
initMqtt();
219+
refreshNodeList();
220+
sendSysInfoUDP();
219221
}
220222
yield();
221223
handleWs();

wled00/wled.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115
#include "FX.h"
116116
#include "ir_codes.h"
117117
#include "const.h"
118+
#include "NodeStruct.h"
118119

119120
#ifndef CLIENT_SSID
120121
#define CLIENT_SSID DEFAULT_CLIENT_SSID
@@ -232,6 +233,8 @@ WLED_GLOBAL char serverDescription[33] _INIT("WLED"); // Name of module
232233
WLED_GLOBAL bool syncToggleReceive _INIT(false); // UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise
233234

234235
// Sync CONFIG
236+
WLED_GLOBAL NodesMap Nodes;
237+
235238
WLED_GLOBAL bool buttonEnabled _INIT(true);
236239
WLED_GLOBAL byte irEnabled _INIT(0); // Infrared receiver
237240

0 commit comments

Comments
 (0)