Repository navigation
Add UDP packet retry mechanism to udp_name_sync usermod #5873
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
220ab35
a03f4ba
945555c
af1dc48
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,10 +5,15 @@ class UdpNameSync : public Usermod { | |
| private: | ||
|
|
||
| bool enabled = false; | ||
| char segmentName[WLED_MAX_SEGNAME_LEN] = {0}; | ||
| char segmentName[WLED_MAX_SEGNAME_LEN + 1] = {0}; // Segment::setName() accepts up to WLED_MAX_SEGNAME_LEN chars, so keep room for the terminator | ||
| static constexpr uint8_t kPacketType = 200; // custom usermod packet type | ||
| static const char _name[]; | ||
| static const char _enabled[]; | ||
|
|
||
| // Retry mechanism variables (similar to core UDP sync) | ||
| unsigned long _lastNameSentTime = 0; | ||
| uint8_t _nameSendCount = 0; | ||
| bool _nameNeedsSync = false; | ||
|
|
||
| public: | ||
| /** | ||
|
|
@@ -30,37 +35,68 @@ class UdpNameSync : public Usermod { | |
| if (!enabled) return; | ||
| if (!WLED_CONNECTED) return; | ||
| if (!udpConnected) return; | ||
|
|
||
| Segment& mainseg = strip.getMainSegment(); | ||
| if (segmentName[0] == '\0' && !mainseg.name) return; //name was never set, do nothing | ||
| // Early return only if name was never set and no retry is pending | ||
| // This allows empty-name packets to reach the retry branch when _nameNeedsSync is set | ||
| if (segmentName[0] == '\0' && !mainseg.name && !_nameNeedsSync) return; //name was never set, do nothing | ||
|
softhack007 marked this conversation as resolved.
|
||
|
|
||
| const char* curName = mainseg.name ? mainseg.name : ""; | ||
| if (strncmp(curName, segmentName, sizeof(segmentName)) == 0) return; // same name, do nothing | ||
|
|
||
|
|
||
| // Check for name change first - this takes priority over retries | ||
| if (strncmp(curName, segmentName, sizeof(segmentName)) != 0) { | ||
|
coderabbitai[bot] marked this conversation as resolved.
coderabbitai[bot] marked this conversation as resolved.
|
||
| // Name changed, send new name (initial send, reset retry counter) | ||
| sendNamePacket(false); | ||
| return; | ||
| } | ||
|
|
||
| // Name hasn't changed - check if we need to retry | ||
| if (_nameNeedsSync && udpConnected && (_nameSendCount < udpNumRetries) && ((millis() - _lastNameSentTime) > 250)) { | ||
| sendNamePacket(true); // retry | ||
|
Comment on lines
+54
to
+55
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Treat a changed name as a new send before retrying. If the name changes after an earlier packet becomes due for retry, this branch calls 🤖 Prompt for AI Agents |
||
| return; | ||
| } | ||
|
|
||
| // If we were waiting for retries but they're now complete, clear the flag | ||
| if (_nameNeedsSync && (_nameSendCount >= udpNumRetries || !udpConnected)) { | ||
| _nameNeedsSync = false; | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| } | ||
|
|
||
| // Name is in sync, no action needed | ||
| return; | ||
| } | ||
|
|
||
| void sendNamePacket(bool isRetry) { | ||
| IPAddress broadcastIp = uint32_t(WLEDNetwork.localIP()) | ~uint32_t(WLEDNetwork.subnetMask()); | ||
| byte udpOut[WLED_MAX_SEGNAME_LEN + 2]; | ||
| udpOut[0] = kPacketType; // custom usermod packet type (avoid 0..5 used by core protocols) | ||
|
|
||
|
|
||
| Segment& mainseg = strip.getMainSegment(); | ||
| const char* curName = mainseg.name ? mainseg.name : ""; | ||
|
|
||
| if (segmentName[0] != '\0' && !mainseg.name) { // name cleared | ||
| notifierUdp.beginPacket(broadcastIp, udpPort); | ||
| segmentName[0] = '\0'; | ||
| DEBUG_PRINTLN(F("UdpNameSync: sending empty name")); | ||
| strlcpy(segmentName, "", sizeof(segmentName)); | ||
|
coderabbitai[bot] marked this conversation as resolved.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason to use |
||
| if (!isRetry) DEBUG_PRINTLN(F("UdpNameSync: sending empty name")); | ||
| udpOut[1] = 0; // explicit empty string | ||
| notifierUdp.beginPacket(broadcastIp, udpPort); | ||
| notifierUdp.write(udpOut, 2); | ||
| notifierUdp.endPacket(); | ||
| return; | ||
| } else { | ||
| strlcpy(segmentName, curName, sizeof(segmentName)); | ||
| strlcpy((char *)&udpOut[1], segmentName, sizeof(udpOut) - 1); // leave room for header byte | ||
| size_t nameLen = strnlen((char *)&udpOut[1], sizeof(udpOut) - 1); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of re-walking the string several times, consider using the return value of the first size_t nameLen = strlcpy(segmentName, curName, sizeof(segmentName));
nameLen = std::min(nameLen, WLED_MAX_SEGNAME_LEN); // clamp
memcpy((char *)&udpOut[1], segmentName, nameLen + 1); // leave room for header byte |
||
| notifierUdp.beginPacket(broadcastIp, udpPort); | ||
| notifierUdp.write(udpOut, 2 + nameLen); | ||
| notifierUdp.endPacket(); | ||
| if (!isRetry) { | ||
| DEBUG_PRINT(F("UdpNameSync: Sent segment name : ")); | ||
| DEBUG_PRINTLN(segmentName); | ||
| } | ||
| } | ||
|
|
||
| notifierUdp.beginPacket(broadcastIp, udpPort); | ||
| DEBUG_PRINT(F("UdpNameSync: saving segment name ")); | ||
| DEBUG_PRINTLN(curName); | ||
| strlcpy(segmentName, curName, sizeof(segmentName)); | ||
| strlcpy((char *)&udpOut[1], segmentName, sizeof(udpOut) - 1); // leave room for header byte | ||
| 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; | ||
|
|
||
| // Update retry tracking (match core behavior) | ||
| _lastNameSentTime = millis(); | ||
| _nameSendCount = isRetry ? _nameSendCount + 1 : 0; | ||
| _nameNeedsSync = true; | ||
| } | ||
|
|
||
| bool onUdpPacket(uint8_t * payload, size_t len) override { | ||
|
|
@@ -81,5 +117,9 @@ class UdpNameSync : public Usermod { | |
| } | ||
| }; | ||
|
|
||
| // Static member definitions | ||
| const char UdpNameSync::_name[] PROGMEM = "UDP Name Sync"; | ||
| const char UdpNameSync::_enabled[] PROGMEM = "enabled"; | ||
|
|
||
| static UdpNameSync udp_name_sync; | ||
| REGISTER_USERMOD(udp_name_sync); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This variable isn't needed -- the test of
_nameSendCount < udpNumRetriesis equivalent. In the rare case of someone increasingudpNumRetries, sending a few extra packets is idempotent, so we can save the memory.