From 4de6656bc4e9fbcfe9d35b63e7d8f9e70f3d0404 Mon Sep 17 00:00:00 2001 From: Arthur Suzuki Date: Thu, 21 Aug 2025 01:00:22 +0200 Subject: [PATCH 1/9] new usermod hooks "onUdpPacket" this new hooks will help you implement new and custom protocols in usermods. I've provided an example (see usermods/udp_name_sync). The example will help you share the main segment name across different WLED instances. The segment name can be useful to sync with some effects like GIF image or scrolling text. If you define new packet format in your usermod, make sure it will either not collide with already used version of wled udp packet : - 0 is for udp sync - 1 is for AudioReactive data - 2 is for udp_name_sync :) Also, the onUdpPacket will override "parseNotification" if it returns "true". Have fun! --- usermods/udp_name_sync/library.json | 5 ++ usermods/udp_name_sync/udp_name_sync.cpp | 79 ++++++++++++++++++++++++ wled00/fcn_declare.h | 2 + wled00/udp.cpp | 3 + wled00/um_manager.cpp | 4 ++ 5 files changed, 93 insertions(+) create mode 100644 usermods/udp_name_sync/library.json create mode 100644 usermods/udp_name_sync/udp_name_sync.cpp diff --git a/usermods/udp_name_sync/library.json b/usermods/udp_name_sync/library.json new file mode 100644 index 00000000..4c5bb448 --- /dev/null +++ b/usermods/udp_name_sync/library.json @@ -0,0 +1,5 @@ +{ + "name": "udp_name_sync", + "build": { "libArchive": false }, + "dependencies": {} +} diff --git a/usermods/udp_name_sync/udp_name_sync.cpp b/usermods/udp_name_sync/udp_name_sync.cpp new file mode 100644 index 00000000..d926e1b6 --- /dev/null +++ b/usermods/udp_name_sync/udp_name_sync.cpp @@ -0,0 +1,79 @@ +#include "wled.h" + +class UdpNameSync : public Usermod { + + private: + + bool enabled = false; + bool initDone = false; + unsigned long lastTime = 0; + char segmentName[WLED_MAX_SEGNAME_LEN] = {0}; + static const char _name[]; + static const char _enabled[]; + + public: + /** + * Enable/Disable the usermod + */ + inline void enable(bool enable) { enabled = enable; } + + /** + * Get usermod enabled/disabled state + */ + inline bool isEnabled() { return enabled; } + + void setup() override { + initDone = true; + } + + void loop() override { + if (!WLED_CONNECTED) return; + if (!udpConnected) return; + Segment& mainseg = strip.getMainSegment(); + if (!strlen(segmentName) && !mainseg.name) return; //name was never set, do nothing + + IPAddress broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP()); + byte udpOut[WLED_MAX_SEGNAME_LEN + 2]; + udpOut[0] = 2; // 0: wled notifier protocol, 1: warls protocol, 2 is free + + if (strlen(segmentName) && !mainseg.name) { //name is back to null + notifierUdp.beginPacket(broadcastIp, udpPort); + strcpy(segmentName,""); + DEBUG_PRINTLN(F("UdpNameSync: sending Null name")); + notifierUdp.write( udpOut , 2); + notifierUdp.endPacket(); + return; + } + + if (0 == strcmp(mainseg.name, segmentName)) return; //same name, do nothing + + notifierUdp.beginPacket(broadcastIp, udpPort); + DEBUG_PRINT(F("UdpNameSync: saving segment name ")); + DEBUG_PRINTLN(mainseg.name); + byte length = strlen(mainseg.name); + strlcpy(segmentName, mainseg.name, length+1); + strlcpy((char *)&udpOut[1], segmentName, length+1); + notifierUdp.write(udpOut, length + 2); + notifierUdp.endPacket(); + DEBUG_PRINT(F("UdpNameSync: Sent segment name : ")); + DEBUG_PRINTLN(segmentName); + } + + bool onUdpPacket(uint8_t * payload, uint8_t len) override { + DEBUG_PRINT(F("UdpNameSync: Received packet")); + if (payload[0] != 2) return false; + //else + Segment& mainseg = strip.getMainSegment(); + mainseg.setName((char *)&payload[1]); + DEBUG_PRINT(F("UdpNameSync: set segment name")); + return true; + } +}; + + +// add more strings here to reduce flash memory usage +const char UdpNameSync::_name[] PROGMEM = "UdpNameSync"; +const char UdpNameSync::_enabled[] PROGMEM = "enabled"; + +static UdpNameSync udp_name_sync; +REGISTER_USERMOD(udp_name_sync); diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 0cd28a31..326ae122 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -442,6 +442,7 @@ class Usermod { virtual void onMqttConnect(bool sessionPresent) {} // fired when MQTT connection is established (so usermod can subscribe) virtual bool onMqttMessage(char* topic, char* payload) { return false; } // fired upon MQTT message received (wled topic) virtual bool onEspNowMessage(uint8_t* sender, uint8_t* payload, uint8_t len) { return false; } // fired upon ESP-NOW message received + virtual bool onUdpPacket(uint8_t* payload, uint8_t len) { return false; } //fired upon UDP packet received virtual void onUpdateBegin(bool) {} // fired prior to and after unsuccessful firmware update virtual void onStateChange(uint8_t mode) {} // fired upon WLED state change virtual uint16_t getId() {return USERMOD_ID_UNSPECIFIED;} @@ -481,6 +482,7 @@ namespace UsermodManager { #ifndef WLED_DISABLE_ESPNOW bool onEspNowMessage(uint8_t* sender, uint8_t* payload, uint8_t len); #endif + bool onUdpPacket(uint8_t* payload, uint8_t len); void onUpdateBegin(bool); void onStateChange(uint8_t); Usermod* lookup(uint16_t mod_id); diff --git a/wled00/udp.cpp b/wled00/udp.cpp index bdb60c36..7b9e53bc 100644 --- a/wled00/udp.cpp +++ b/wled00/udp.cpp @@ -557,6 +557,9 @@ void handleNotifications() return; } + // usermods hook can override processing + if (UsermodManager::onUdpPacket(udpIn, packetSize)) return; + //wled notifier, ignore if realtime packets active if (udpIn[0] == 0 && !realtimeMode && receiveGroups) { diff --git a/wled00/um_manager.cpp b/wled00/um_manager.cpp index 1a7cc226..483fdc68 100644 --- a/wled00/um_manager.cpp +++ b/wled00/um_manager.cpp @@ -68,6 +68,10 @@ bool UsermodManager::onEspNowMessage(uint8_t* sender, uint8_t* payload, uint8_t return false; } #endif +bool UsermodManager::onUdpPacket(uint8_t* payload, uint8_t len) { + for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) if ((*mod)->onUdpPacket(payload, len)) return true; + return false; +} void UsermodManager::onUpdateBegin(bool init) { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) (*mod)->onUpdateBegin(init); } // notify usermods that update is to begin void UsermodManager::onStateChange(uint8_t mode) { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) (*mod)->onStateChange(mode); } // notify usermods that WLED state changed From 2082b01a3cb98f322d4a756b8bd710bb1d03fa75 Mon Sep 17 00:00:00 2001 From: netmindz Date: Thu, 21 Aug 2025 08:53:11 +0100 Subject: [PATCH 2/9] Apply suggestions from code review Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- usermods/udp_name_sync/udp_name_sync.cpp | 11 ++++++----- wled00/um_manager.cpp | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/usermods/udp_name_sync/udp_name_sync.cpp b/usermods/udp_name_sync/udp_name_sync.cpp index d926e1b6..cd279386 100644 --- a/usermods/udp_name_sync/udp_name_sync.cpp +++ b/usermods/udp_name_sync/udp_name_sync.cpp @@ -45,15 +45,16 @@ class UdpNameSync : public Usermod { return; } - if (0 == strcmp(mainseg.name, segmentName)) return; //same name, do nothing + const char* curName = mainseg.name ? mainseg.name : ""; + if (strcmp(curName, segmentName) == 0) return; // same name, do nothing notifierUdp.beginPacket(broadcastIp, udpPort); DEBUG_PRINT(F("UdpNameSync: saving segment name ")); DEBUG_PRINTLN(mainseg.name); - byte length = strlen(mainseg.name); - strlcpy(segmentName, mainseg.name, length+1); - strlcpy((char *)&udpOut[1], segmentName, length+1); - notifierUdp.write(udpOut, length + 2); + size_t length = strlen(mainseg.name); + strlcpy(segmentName, mainseg.name, sizeof(segmentName)); + strlcpy((char *)&udpOut[1], segmentName, sizeof(udpOut) - 1); // leave room for header byte + notifierUdp.write(udpOut, 2 + strnlen((char *)&udpOut[1], sizeof(udpOut) - 1)); notifierUdp.endPacket(); DEBUG_PRINT(F("UdpNameSync: Sent segment name : ")); DEBUG_PRINTLN(segmentName); diff --git a/wled00/um_manager.cpp b/wled00/um_manager.cpp index 483fdc68..647757ad 100644 --- a/wled00/um_manager.cpp +++ b/wled00/um_manager.cpp @@ -68,7 +68,7 @@ bool UsermodManager::onEspNowMessage(uint8_t* sender, uint8_t* payload, uint8_t return false; } #endif -bool UsermodManager::onUdpPacket(uint8_t* payload, uint8_t len) { +bool UsermodManager::onUdpPacket(uint8_t* payload, size_t len) { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) if ((*mod)->onUdpPacket(payload, len)) return true; return false; } From f3e3f585dfc25e731f08dd71f957aad830e72f12 Mon Sep 17 00:00:00 2001 From: Liliputech Date: Sun, 24 Aug 2025 13:52:39 +0200 Subject: [PATCH 3/9] usermod udp_name_sync : properly initialize packet if segment name is empty Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- usermods/udp_name_sync/udp_name_sync.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/usermods/udp_name_sync/udp_name_sync.cpp b/usermods/udp_name_sync/udp_name_sync.cpp index cd279386..31a271fe 100644 --- a/usermods/udp_name_sync/udp_name_sync.cpp +++ b/usermods/udp_name_sync/udp_name_sync.cpp @@ -36,13 +36,14 @@ class UdpNameSync : public Usermod { byte udpOut[WLED_MAX_SEGNAME_LEN + 2]; udpOut[0] = 2; // 0: wled notifier protocol, 1: warls protocol, 2 is free - if (strlen(segmentName) && !mainseg.name) { //name is back to null - notifierUdp.beginPacket(broadcastIp, udpPort); - strcpy(segmentName,""); - DEBUG_PRINTLN(F("UdpNameSync: sending Null name")); - notifierUdp.write( udpOut , 2); - notifierUdp.endPacket(); - return; + if (strlen(segmentName) && !mainseg.name) { // name cleared + notifierUdp.beginPacket(broadcastIp, udpPort); + segmentName[0] = '\0'; + DEBUG_PRINTLN(F("UdpNameSync: sending empty name")); + udpOut[1] = 0; // explicit empty string + notifierUdp.write(udpOut, 2); + notifierUdp.endPacket(); + return; } const char* curName = mainseg.name ? mainseg.name : ""; From 550b4d9deaa8f7f7060c1b00842d14455ea38d61 Mon Sep 17 00:00:00 2001 From: Arthur Suzuki Date: Sun, 24 Aug 2025 15:54:45 +0200 Subject: [PATCH 4/9] fix comments for usermod hooks "onUdpPacket" --- usermods/udp_name_sync/udp_name_sync.cpp | 16 +++++++++++----- wled00/fcn_declare.h | 4 ++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/usermods/udp_name_sync/udp_name_sync.cpp b/usermods/udp_name_sync/udp_name_sync.cpp index 31a271fe..361dd7a5 100644 --- a/usermods/udp_name_sync/udp_name_sync.cpp +++ b/usermods/udp_name_sync/udp_name_sync.cpp @@ -34,7 +34,7 @@ class UdpNameSync : public Usermod { IPAddress broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP()); byte udpOut[WLED_MAX_SEGNAME_LEN + 2]; - udpOut[0] = 2; // 0: wled notifier protocol, 1: warls protocol, 2 is free + udpOut[0] = 200; // 0: wled notifier protocol, 1: warls protocol, 2 is free if (strlen(segmentName) && !mainseg.name) { // name cleared notifierUdp.beginPacket(broadcastIp, udpPort); @@ -46,8 +46,13 @@ class UdpNameSync : public Usermod { return; } - const char* curName = mainseg.name ? mainseg.name : ""; - if (strcmp(curName, segmentName) == 0) return; // same name, do nothing + char checksumSegName = 0; + char checksumCurName = 0; + for(int i=0; i++; mainseg.name[i]==0 || segmentName[i]==0) { + checksumSegName+=segmentName[i]; + checksumCurName+=mainseg.name[i]; + } + if (checksumCurName == checksumSegName) return; // same name, do nothing notifierUdp.beginPacket(broadcastIp, udpPort); DEBUG_PRINT(F("UdpNameSync: saving segment name ")); @@ -61,9 +66,10 @@ class UdpNameSync : public Usermod { DEBUG_PRINTLN(segmentName); } - bool onUdpPacket(uint8_t * payload, uint8_t len) override { + bool onUdpPacket(uint8_t * payload, size_t len) override { DEBUG_PRINT(F("UdpNameSync: Received packet")); - if (payload[0] != 2) return false; + if (receiveDirect) return false; + if (payload[0] != 200) return false; //else Segment& mainseg = strip.getMainSegment(); mainseg.setName((char *)&payload[1]); diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 326ae122..f83f4ae6 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -442,7 +442,7 @@ class Usermod { virtual void onMqttConnect(bool sessionPresent) {} // fired when MQTT connection is established (so usermod can subscribe) virtual bool onMqttMessage(char* topic, char* payload) { return false; } // fired upon MQTT message received (wled topic) virtual bool onEspNowMessage(uint8_t* sender, uint8_t* payload, uint8_t len) { return false; } // fired upon ESP-NOW message received - virtual bool onUdpPacket(uint8_t* payload, uint8_t len) { return false; } //fired upon UDP packet received + virtual bool onUdpPacket(uint8_t* payload, size_t len) { return false; } //fired upon UDP packet received virtual void onUpdateBegin(bool) {} // fired prior to and after unsuccessful firmware update virtual void onStateChange(uint8_t mode) {} // fired upon WLED state change virtual uint16_t getId() {return USERMOD_ID_UNSPECIFIED;} @@ -482,7 +482,7 @@ namespace UsermodManager { #ifndef WLED_DISABLE_ESPNOW bool onEspNowMessage(uint8_t* sender, uint8_t* payload, uint8_t len); #endif - bool onUdpPacket(uint8_t* payload, uint8_t len); + bool onUdpPacket(uint8_t* payload, size_t len); void onUpdateBegin(bool); void onStateChange(uint8_t); Usermod* lookup(uint16_t mod_id); From 4b5c3a396d87ca1d1d6187d334a8f3974c0ae5d5 Mon Sep 17 00:00:00 2001 From: Arthur Suzuki Date: Mon, 25 Aug 2025 22:22:51 +0200 Subject: [PATCH 5/9] applied suggestions from review --- usermods/udp_name_sync/udp_name_sync.cpp | 9 +- wled00/udp.cpp | 150 +++++++++++------------ 2 files changed, 71 insertions(+), 88 deletions(-) diff --git a/usermods/udp_name_sync/udp_name_sync.cpp b/usermods/udp_name_sync/udp_name_sync.cpp index 361dd7a5..d7f67117 100644 --- a/usermods/udp_name_sync/udp_name_sync.cpp +++ b/usermods/udp_name_sync/udp_name_sync.cpp @@ -46,13 +46,8 @@ class UdpNameSync : public Usermod { return; } - char checksumSegName = 0; - char checksumCurName = 0; - for(int i=0; i++; mainseg.name[i]==0 || segmentName[i]==0) { - checksumSegName+=segmentName[i]; - checksumCurName+=mainseg.name[i]; - } - if (checksumCurName == checksumSegName) return; // same name, do nothing + const char* curName = mainseg.name ? mainseg.name : ""; + if (strncmp(curName, segmentName, sizeof(segmentName)) == 0) return; // same name, do nothing notifierUdp.beginPacket(broadcastIp, udpPort); DEBUG_PRINT(F("UdpNameSync: saving segment name ")); diff --git a/wled00/udp.cpp b/wled00/udp.cpp index 7b9e53bc..bd9b4395 100644 --- a/wled00/udp.cpp +++ b/wled00/udp.cpp @@ -557,9 +557,6 @@ void handleNotifications() return; } - // usermods hook can override processing - if (UsermodManager::onUdpPacket(udpIn, packetSize)) return; - //wled notifier, ignore if realtime packets active if (udpIn[0] == 0 && !realtimeMode && receiveGroups) { @@ -568,93 +565,82 @@ void handleNotifications() return; } - if (!receiveDirect) return; + if (receiveDirect) { + //TPM2.NET + if (udpIn[0] == 0x9c) { + //WARNING: this code assumes that the final TMP2.NET payload is evenly distributed if using multiple packets (ie. frame size is constant) + //if the number of LEDs in your installation doesn't allow that, please include padding bytes at the end of the last packet + byte tpmType = udpIn[1]; + if (tpmType == 0xaa) { //TPM2.NET polling, expect answer + sendTPM2Ack(); return; + } + if (tpmType != 0xda) return; //return if notTPM2.NET data - //TPM2.NET - if (udpIn[0] == 0x9c) - { - //WARNING: this code assumes that the final TMP2.NET payload is evenly distributed if using multiple packets (ie. frame size is constant) - //if the number of LEDs in your installation doesn't allow that, please include padding bytes at the end of the last packet - byte tpmType = udpIn[1]; - if (tpmType == 0xaa) { //TPM2.NET polling, expect answer - sendTPM2Ack(); return; + realtimeIP = (isSupp) ? notifier2Udp.remoteIP() : notifierUdp.remoteIP(); + realtimeLock(realtimeTimeoutMs, REALTIME_MODE_TPM2NET); + if (realtimeOverride) return; + + tpmPacketCount++; //increment the packet count + if (tpmPacketCount == 1) tpmPayloadFrameSize = (udpIn[2] << 8) + udpIn[3]; //save frame size for the whole payload if this is the first packet + byte packetNum = udpIn[4]; //starts with 1! + byte numPackets = udpIn[5]; + + unsigned id = (tpmPayloadFrameSize/3)*(packetNum-1); //start LED + unsigned totalLen = strip.getLengthTotal(); + for (size_t i = 6; i < tpmPayloadFrameSize + 4U && id < totalLen; i += 3, id++) { + setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0); + } + if (tpmPacketCount == numPackets) { //reset packet count and show if all packets were received + tpmPacketCount = 0; + if (useMainSegmentOnly) strip.trigger(); + else strip.show(); + } + return; } - if (tpmType != 0xda) return; //return if notTPM2.NET data - realtimeIP = (isSupp) ? notifier2Udp.remoteIP() : notifierUdp.remoteIP(); - realtimeLock(realtimeTimeoutMs, REALTIME_MODE_TPM2NET); - if (realtimeOverride) return; + //UDP realtime: 1 warls 2 drgb 3 drgbw 4 dnrgb 5 dnrgbw + if (udpIn[0] > 0 && udpIn[0] < 6) { + realtimeIP = (isSupp) ? notifier2Udp.remoteIP() : notifierUdp.remoteIP(); + DEBUG_PRINTLN(realtimeIP); + if (packetSize < 2) return; - tpmPacketCount++; //increment the packet count - if (tpmPacketCount == 1) tpmPayloadFrameSize = (udpIn[2] << 8) + udpIn[3]; //save frame size for the whole payload if this is the first packet - byte packetNum = udpIn[4]; //starts with 1! - byte numPackets = udpIn[5]; + if (udpIn[1] == 0) { + realtimeTimeout = 0; // cancel realtime mode immediately + return; + } else { + realtimeLock(udpIn[1]*1000 +1, REALTIME_MODE_UDP); + } + if (realtimeOverride) return; - unsigned id = (tpmPayloadFrameSize/3)*(packetNum-1); //start LED - unsigned totalLen = strip.getLengthTotal(); - for (size_t i = 6; i < tpmPayloadFrameSize + 4U && id < totalLen; i += 3, id++) { - setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0); - } - if (tpmPacketCount == numPackets) { //reset packet count and show if all packets were received - tpmPacketCount = 0; + unsigned totalLen = strip.getLengthTotal(); + if (udpIn[0] == 1 && packetSize > 5) { //warls + for (size_t i = 2; i < packetSize -3; i += 4) { + setRealtimePixel(udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3], 0); + } + } else if (udpIn[0] == 2 && packetSize > 4) { //drgb + for (size_t i = 2, id = 0; i < packetSize -2 && id < totalLen; i += 3, id++) + { + setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0); + } + } else if (udpIn[0] == 3 && packetSize > 6) { //drgbw + for (size_t i = 2, id = 0; i < packetSize -3 && id < totalLen; i += 4, id++) { + setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]); + } + } else if (udpIn[0] == 4 && packetSize > 7) { //dnrgb + unsigned id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00); + for (size_t i = 4; i < packetSize -2 && id < totalLen; i += 3, id++) { + setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0); + } + } else if (udpIn[0] == 5 && packetSize > 8) { //dnrgbw + unsigned id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00); + for (size_t i = 4; i < packetSize -2 && id < totalLen; i += 4, id++) { + setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]); + } + } if (useMainSegmentOnly) strip.trigger(); else strip.show(); - } - return; - } - - //UDP realtime: 1 warls 2 drgb 3 drgbw 4 dnrgb 5 dnrgbw - if (udpIn[0] > 0 && udpIn[0] < 6) - { - realtimeIP = (isSupp) ? notifier2Udp.remoteIP() : notifierUdp.remoteIP(); - DEBUG_PRINTLN(realtimeIP); - if (packetSize < 2) return; - - if (udpIn[1] == 0) { - realtimeTimeout = 0; // cancel realtime mode immediately return; - } else { - realtimeLock(udpIn[1]*1000 +1, REALTIME_MODE_UDP); } - if (realtimeOverride) return; - - unsigned totalLen = strip.getLengthTotal(); - if (udpIn[0] == 1 && packetSize > 5) //warls - { - for (size_t i = 2; i < packetSize -3; i += 4) - { - setRealtimePixel(udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3], 0); - } - } else if (udpIn[0] == 2 && packetSize > 4) //drgb - { - for (size_t i = 2, id = 0; i < packetSize -2 && id < totalLen; i += 3, id++) - { - setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0); - } - } else if (udpIn[0] == 3 && packetSize > 6) //drgbw - { - for (size_t i = 2, id = 0; i < packetSize -3 && id < totalLen; i += 4, id++) - { - setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]); - } - } else if (udpIn[0] == 4 && packetSize > 7) //dnrgb - { - unsigned id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00); - for (size_t i = 4; i < packetSize -2 && id < totalLen; i += 3, id++) - { - setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0); - } - } else if (udpIn[0] == 5 && packetSize > 8) //dnrgbw - { - unsigned id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00); - for (size_t i = 4; i < packetSize -2 && id < totalLen; i += 4, id++) - { - setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]); - } - } - if (useMainSegmentOnly) strip.trigger(); - else strip.show(); - return; } // API over UDP @@ -672,6 +658,8 @@ void handleNotifications() } releaseJSONBufferLock(); } + + UsermodManager::onUdpPacket(udpIn, packetSize); } From f8ce5980a17cee530c2e13dc5164131c83deb46c Mon Sep 17 00:00:00 2001 From: Arthur Suzuki Date: Mon, 25 Aug 2025 22:45:01 +0200 Subject: [PATCH 6/9] removed tabs and replace by space --- wled00/udp.cpp | 52 +++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/wled00/udp.cpp b/wled00/udp.cpp index bd9b4395..5600b673 100644 --- a/wled00/udp.cpp +++ b/wled00/udp.cpp @@ -572,7 +572,7 @@ void handleNotifications() //if the number of LEDs in your installation doesn't allow that, please include padding bytes at the end of the last packet byte tpmType = udpIn[1]; if (tpmType == 0xaa) { //TPM2.NET polling, expect answer - sendTPM2Ack(); return; + sendTPM2Ack(); return; } if (tpmType != 0xda) return; //return if notTPM2.NET data @@ -588,12 +588,12 @@ void handleNotifications() unsigned id = (tpmPayloadFrameSize/3)*(packetNum-1); //start LED unsigned totalLen = strip.getLengthTotal(); for (size_t i = 6; i < tpmPayloadFrameSize + 4U && id < totalLen; i += 3, id++) { - setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0); + setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0); } if (tpmPacketCount == numPackets) { //reset packet count and show if all packets were received - tpmPacketCount = 0; - if (useMainSegmentOnly) strip.trigger(); - else strip.show(); + tpmPacketCount = 0; + if (useMainSegmentOnly) strip.trigger(); + else strip.show(); } return; } @@ -605,37 +605,37 @@ void handleNotifications() if (packetSize < 2) return; if (udpIn[1] == 0) { - realtimeTimeout = 0; // cancel realtime mode immediately - return; + realtimeTimeout = 0; // cancel realtime mode immediately + return; } else { - realtimeLock(udpIn[1]*1000 +1, REALTIME_MODE_UDP); + realtimeLock(udpIn[1]*1000 +1, REALTIME_MODE_UDP); } if (realtimeOverride) return; unsigned totalLen = strip.getLengthTotal(); if (udpIn[0] == 1 && packetSize > 5) { //warls - for (size_t i = 2; i < packetSize -3; i += 4) { - setRealtimePixel(udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3], 0); - } + for (size_t i = 2; i < packetSize -3; i += 4) { + setRealtimePixel(udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3], 0); + } } else if (udpIn[0] == 2 && packetSize > 4) { //drgb - for (size_t i = 2, id = 0; i < packetSize -2 && id < totalLen; i += 3, id++) - { - setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0); - } + for (size_t i = 2, id = 0; i < packetSize -2 && id < totalLen; i += 3, id++) + { + setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0); + } } else if (udpIn[0] == 3 && packetSize > 6) { //drgbw - for (size_t i = 2, id = 0; i < packetSize -3 && id < totalLen; i += 4, id++) { - setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]); - } + for (size_t i = 2, id = 0; i < packetSize -3 && id < totalLen; i += 4, id++) { + setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]); + } } else if (udpIn[0] == 4 && packetSize > 7) { //dnrgb - unsigned id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00); - for (size_t i = 4; i < packetSize -2 && id < totalLen; i += 3, id++) { - setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0); - } + unsigned id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00); + for (size_t i = 4; i < packetSize -2 && id < totalLen; i += 3, id++) { + setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0); + } } else if (udpIn[0] == 5 && packetSize > 8) { //dnrgbw - unsigned id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00); - for (size_t i = 4; i < packetSize -2 && id < totalLen; i += 4, id++) { - setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]); - } + unsigned id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00); + for (size_t i = 4; i < packetSize -2 && id < totalLen; i += 4, id++) { + setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]); + } } if (useMainSegmentOnly) strip.trigger(); else strip.show(); From a60be251d237b20f86ee8c413eba52cca9509473 Mon Sep 17 00:00:00 2001 From: Arthur Suzuki Date: Thu, 28 Aug 2025 10:49:27 +0200 Subject: [PATCH 7/9] fix nitpicks from coderabbit --- usermods/udp_name_sync/udp_name_sync.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/usermods/udp_name_sync/udp_name_sync.cpp b/usermods/udp_name_sync/udp_name_sync.cpp index d7f67117..fdeda78a 100644 --- a/usermods/udp_name_sync/udp_name_sync.cpp +++ b/usermods/udp_name_sync/udp_name_sync.cpp @@ -5,8 +5,6 @@ class UdpNameSync : public Usermod { private: bool enabled = false; - bool initDone = false; - unsigned long lastTime = 0; char segmentName[WLED_MAX_SEGNAME_LEN] = {0}; static const char _name[]; static const char _enabled[]; @@ -15,15 +13,14 @@ class UdpNameSync : public Usermod { /** * Enable/Disable the usermod */ - inline void enable(bool enable) { enabled = enable; } + inline void enable(bool value) { enabled = value; } /** * Get usermod enabled/disabled state */ - inline bool isEnabled() { return enabled; } + inline bool isEnabled() const { return enabled; } void setup() override { - initDone = true; } void loop() override { @@ -32,10 +29,9 @@ class UdpNameSync : public Usermod { Segment& mainseg = strip.getMainSegment(); if (!strlen(segmentName) && !mainseg.name) return; //name was never set, do nothing - IPAddress broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP()); + IPAddress broadcastIp = uint32_t(Network.localIP()) | ~uint32_t(Network.subnetMask()); byte udpOut[WLED_MAX_SEGNAME_LEN + 2]; - udpOut[0] = 200; // 0: wled notifier protocol, 1: warls protocol, 2 is free - + udpOut[0] = 200; // custom usermod packet type (avoid 0..5 used by core protocols) if (strlen(segmentName) && !mainseg.name) { // name cleared notifierUdp.beginPacket(broadcastIp, udpPort); segmentName[0] = '\0'; @@ -52,7 +48,6 @@ class UdpNameSync : public Usermod { notifierUdp.beginPacket(broadcastIp, udpPort); DEBUG_PRINT(F("UdpNameSync: saving segment name ")); DEBUG_PRINTLN(mainseg.name); - size_t length = strlen(mainseg.name); strlcpy(segmentName, mainseg.name, sizeof(segmentName)); strlcpy((char *)&udpOut[1], segmentName, sizeof(udpOut) - 1); // leave room for header byte notifierUdp.write(udpOut, 2 + strnlen((char *)&udpOut[1], sizeof(udpOut) - 1)); From 62fad4dcdfbf8f30b5b3e5bf1ad4dd56c27b36f7 Mon Sep 17 00:00:00 2001 From: Arthur Suzuki Date: Sat, 30 Aug 2025 01:52:36 +0200 Subject: [PATCH 8/9] applied coderabbit suggestions --- usermods/udp_name_sync/udp_name_sync.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/usermods/udp_name_sync/udp_name_sync.cpp b/usermods/udp_name_sync/udp_name_sync.cpp index fdeda78a..7b78a624 100644 --- a/usermods/udp_name_sync/udp_name_sync.cpp +++ b/usermods/udp_name_sync/udp_name_sync.cpp @@ -6,6 +6,7 @@ class UdpNameSync : public Usermod { bool enabled = false; char segmentName[WLED_MAX_SEGNAME_LEN] = {0}; + static constexpr uint8_t kPacketType = 200; // custom usermod packet type static const char _name[]; static const char _enabled[]; @@ -21,9 +22,11 @@ class UdpNameSync : public Usermod { inline bool isEnabled() const { return enabled; } void setup() override { + enable(true); } void loop() override { + if (!enabled) return; if (!WLED_CONNECTED) return; if (!udpConnected) return; Segment& mainseg = strip.getMainSegment(); @@ -31,7 +34,7 @@ class UdpNameSync : public Usermod { IPAddress broadcastIp = uint32_t(Network.localIP()) | ~uint32_t(Network.subnetMask()); byte udpOut[WLED_MAX_SEGNAME_LEN + 2]; - udpOut[0] = 200; // custom usermod packet type (avoid 0..5 used by core protocols) + udpOut[0] = kPacketType; // custom usermod packet type (avoid 0..5 used by core protocols) if (strlen(segmentName) && !mainseg.name) { // name cleared notifierUdp.beginPacket(broadcastIp, udpPort); segmentName[0] = '\0'; @@ -48,6 +51,7 @@ class UdpNameSync : public Usermod { notifierUdp.beginPacket(broadcastIp, udpPort); DEBUG_PRINT(F("UdpNameSync: saving segment name ")); DEBUG_PRINTLN(mainseg.name); + DEBUG_PRINTLN(curName); strlcpy(segmentName, mainseg.name, sizeof(segmentName)); strlcpy((char *)&udpOut[1], segmentName, sizeof(udpOut) - 1); // leave room for header byte notifierUdp.write(udpOut, 2 + strnlen((char *)&udpOut[1], sizeof(udpOut) - 1)); @@ -58,14 +62,20 @@ class UdpNameSync : public Usermod { bool onUdpPacket(uint8_t * payload, size_t len) override { DEBUG_PRINT(F("UdpNameSync: Received packet")); + if (!enabled) return false; if (receiveDirect) return false; - if (payload[0] != 200) return false; - //else + if (len < 2) return false; // need type + at least 1 byte for name (can be 0) + if (payload[0] != kPacketType) return false; Segment& mainseg = strip.getMainSegment(); - mainseg.setName((char *)&payload[1]); + char tmp[WLED_MAX_SEGNAME_LEN] = {0}; + size_t copyLen = len - 1; + if (copyLen > sizeof(tmp) - 1) copyLen = sizeof(tmp) - 1; + memcpy(tmp, &payload[1], copyLen); + tmp[copyLen] = '\0'; + mainseg.setName(tmp); DEBUG_PRINT(F("UdpNameSync: set segment name")); return true; - } + } }; From c8757d45c8fb8c58f48312296bc9a90a1a622305 Mon Sep 17 00:00:00 2001 From: Arthur Suzuki Date: Sat, 30 Aug 2025 04:12:08 +0200 Subject: [PATCH 9/9] fix more nitpicks comments --- usermods/udp_name_sync/udp_name_sync.cpp | 26 +++++++++++------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/usermods/udp_name_sync/udp_name_sync.cpp b/usermods/udp_name_sync/udp_name_sync.cpp index 7b78a624..b31b8569 100644 --- a/usermods/udp_name_sync/udp_name_sync.cpp +++ b/usermods/udp_name_sync/udp_name_sync.cpp @@ -22,6 +22,7 @@ class UdpNameSync : public Usermod { inline bool isEnabled() const { return enabled; } void setup() override { + // Enabled when this usermod is compiled, set to false if you prefer runtime opt-in enable(true); } @@ -30,12 +31,16 @@ class UdpNameSync : public Usermod { if (!WLED_CONNECTED) return; if (!udpConnected) return; Segment& mainseg = strip.getMainSegment(); - if (!strlen(segmentName) && !mainseg.name) return; //name was never set, do nothing + if (segmentName[0] == '\0' && !mainseg.name) return; //name was never set, do nothing + + const char* curName = mainseg.name ? mainseg.name : ""; + if (strncmp(curName, segmentName, sizeof(segmentName)) == 0) return; // same name, do nothing IPAddress broadcastIp = uint32_t(Network.localIP()) | ~uint32_t(Network.subnetMask()); byte udpOut[WLED_MAX_SEGNAME_LEN + 2]; - udpOut[0] = kPacketType; // custom usermod packet type (avoid 0..5 used by core protocols) - if (strlen(segmentName) && !mainseg.name) { // name cleared + udpOut[0] = kPacketType; // custom usermod packet type (avoid 0..5 used by core protocols) + + if (segmentName[0] != '\0' && !mainseg.name) { // name cleared notifierUdp.beginPacket(broadcastIp, udpPort); segmentName[0] = '\0'; DEBUG_PRINTLN(F("UdpNameSync: sending empty name")); @@ -45,19 +50,17 @@ class UdpNameSync : public Usermod { return; } - const char* curName = mainseg.name ? mainseg.name : ""; - if (strncmp(curName, segmentName, sizeof(segmentName)) == 0) return; // same name, do nothing - notifierUdp.beginPacket(broadcastIp, udpPort); DEBUG_PRINT(F("UdpNameSync: saving segment name ")); - DEBUG_PRINTLN(mainseg.name); DEBUG_PRINTLN(curName); - strlcpy(segmentName, mainseg.name, sizeof(segmentName)); + strlcpy(segmentName, curName, sizeof(segmentName)); strlcpy((char *)&udpOut[1], segmentName, sizeof(udpOut) - 1); // leave room for header byte - notifierUdp.write(udpOut, 2 + strnlen((char *)&udpOut[1], sizeof(udpOut) - 1)); + size_t nameLen = strnlen((char *)&udpOut[1], sizeof(udpOut) - 1); + notifierUdp.write(udpOut, 2 + nameLen); notifierUdp.endPacket(); DEBUG_PRINT(F("UdpNameSync: Sent segment name : ")); DEBUG_PRINTLN(segmentName); + return; } bool onUdpPacket(uint8_t * payload, size_t len) override { @@ -78,10 +81,5 @@ class UdpNameSync : public Usermod { } }; - -// add more strings here to reduce flash memory usage -const char UdpNameSync::_name[] PROGMEM = "UdpNameSync"; -const char UdpNameSync::_enabled[] PROGMEM = "enabled"; - static UdpNameSync udp_name_sync; REGISTER_USERMOD(udp_name_sync);