From 86dad9057385d4794d6a20f2798d42dbf93ad7d6 Mon Sep 17 00:00:00 2001 From: Philip Lykov Date: Fri, 6 Mar 2026 16:38:50 +0200 Subject: [PATCH] Fix nRF52 AsyncUDP multicast TX/RX race condition causing garbled packets (#9765) socketSendUDP() calls yield() while waiting for SEND_OK, allowing the cooperative scheduler to run AsyncUDP::runOnce(). When runOnce() calls parsePacket() on the same socket during an active send, the multicast loopback packet arriving from the switch produces a garbled 8-byte RX header (wrong source IP, wrong payload length), leading to protobuf decode errors in UdpMulticastHandler. Add a volatile isSending flag that writeTo() sets around endPacket() (the only yield point). runOnce() checks this flag and skips parsePacket() while a send is in progress. Loopback packets stay buffered in the W5100S RX buffer and are read cleanly on the next poll cycle. Also propagate writeTo() return value in UdpMulticastHandler::onSend() instead of unconditionally returning true. Made-with: Cursor Co-authored-by: Ben Meadors --- src/mesh/udp/UdpMulticastHandler.h | 3 +-- src/platform/nrf52/AsyncUDP.cpp | 7 +++++-- src/platform/nrf52/AsyncUDP.h | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/mesh/udp/UdpMulticastHandler.h b/src/mesh/udp/UdpMulticastHandler.h index 71bdf488f..f88b48d62 100644 --- a/src/mesh/udp/UdpMulticastHandler.h +++ b/src/mesh/udp/UdpMulticastHandler.h @@ -105,8 +105,7 @@ class UdpMulticastHandler final LOG_DEBUG("Broadcasting packet over UDP (id=%u)", mp->id); uint8_t buffer[meshtastic_MeshPacket_size]; size_t encodedLength = pb_encode_to_bytes(buffer, sizeof(buffer), &meshtastic_MeshPacket_msg, mp); - udp.writeTo(buffer, encodedLength, udpIpAddress, UDP_MULTICAST_DEFAUL_PORT); - return true; + return udp.writeTo(buffer, encodedLength, udpIpAddress, UDP_MULTICAST_DEFAUL_PORT); } private: diff --git a/src/platform/nrf52/AsyncUDP.cpp b/src/platform/nrf52/AsyncUDP.cpp index d119ffddb..8c937d71f 100644 --- a/src/platform/nrf52/AsyncUDP.cpp +++ b/src/platform/nrf52/AsyncUDP.cpp @@ -33,7 +33,10 @@ bool AsyncUDP::writeTo(const uint8_t *data, size_t len, IPAddress ip, uint16_t p if (!udp.beginPacket(ip, port)) return false; udp.write(data, len); - return udp.endPacket(); + isSending = true; + bool ok = udp.endPacket(); + isSending = false; + return ok; } void AsyncUDP::close() @@ -70,7 +73,7 @@ const uint8_t *AsyncUDPPacket::data() int32_t AsyncUDP::runOnce() { - if (_onPacket && udp.parsePacket() > 0) { + if (_onPacket && !isSending && udp.parsePacket() > 0) { AsyncUDPPacket packet(udp); _onPacket(packet); } diff --git a/src/platform/nrf52/AsyncUDP.h b/src/platform/nrf52/AsyncUDP.h index ecbcb12fb..eb6083bc4 100644 --- a/src/platform/nrf52/AsyncUDP.h +++ b/src/platform/nrf52/AsyncUDP.h @@ -31,6 +31,7 @@ class AsyncUDP : public Print, private concurrency::OSThread private: EthernetUDP udp; uint16_t localPort; + volatile bool isSending = false; std::function _onPacket; virtual int32_t runOnce() override; };