* Add clamping logic for milliseconds conversion and unit tests
* Simplify comments in secondsToMsClamped function
Removed detailed comments about seconds to milliseconds conversion.
Router::handleReceived stores its allocCopy of the encrypted packet in the
class member p_encrypted. callModules() invokes module replies that re-enter
the router via MeshService::sendToMesh -> Router::sendLocal, which on a
broadcast reply recursively calls handleReceived. The inner call overwrites
the outer's p_encrypted without releasing it; on the way out it nulls the
member, the outer release(p_encrypted) now releases nullptr, and the original
allocation is permanently leaked. ~381 B per recursion.
Promote p_encrypted to a function-local so each invocation owns its own copy
for its full lifetime. The MQTT-publish null check at the call site (added by
PR #9136 as a workaround for this bug) stays in place because allocCopy can
still legitimately return nullptr on packetPool exhaustion.
Copilot's review of PR #8999 (the original introduction) flagged this exact
pattern at merge time:
"Storing p_encrypted as a class member can cause issues with recursive or
concurrent calls to handleReceived() since each call would overwrite the
previous packet pointer."
The historical reason for the member (S&F needing to retain the encrypted
copy across calls) was satisfied differently by PR #9799 (ServerAPI converted
to std::unique_ptr + cleanup on connection close), so the member is no longer
load-bearing.
Reproduces issues #9632 / #10101 / #8729 (heap leak when MeshMonitor
connected; TCP drops on Station G2 / LILYGO ServerAPI dump abort).
Hardware A/B on Station G2 under sustained TCP-API retry storm (open :4403,
request config, disconnect mid-stream, repeat at ~0.6/s) - 9 min run:
| Build | heapFree drift | rebootCount delta |
| this patch | -1.5 KB (noise)| 0 |
| stock 2.7.13 | -73 KB (8.1KB/min) | +1 (OOM crash) |
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
The CALIBRATE_ALL (0x7F) command inside resetAGC() clears bit 0 of the
undocumented 0x8B5 register. That bit is set once in init() by #9571 and
#9777 to improve SX1262 RX sensitivity, and the AGC-reset path was not
re-applying it. Result: every SX1262 node silently loses the RX
sensitivity patch ~60s after boot and never recovers until reboot.
Empirically confirmed on Heltec Mesh Node T114 (nRF52840 + SX1262):
- Post-calibration read of 0x8B5 = 0x04 (bit 0 cleared)
- After re-apply: 0x05 (bit 0 set)
Reproducible every AGC_RESET_INTERVAL_MS tick.
Fix re-applies the register bit alongside the existing post-calibration
re-applies (setDio2AsRfSwitch, setRxBoostedGainMode).
2026-04-21 09:50:35 -05:00
Ben MeadorsCopilotcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
The W5100S Ethernet chip has only 4 hardware sockets. On RAK4631
Ethernet gateways with syslog and NTP enabled, all 4 sockets were
permanently consumed (NTP UDP + Syslog UDP + TCP API listener + TCP
API client), leaving none for MQTT, DHCP lease renewal, or additional
TCP connections.
- NTP: Remove permanent timeClient.begin() at startup; NTPClient::update()
auto-initializes when needed. Add timeClient.end() after each query to
release the UDP socket immediately.
- Syslog: Remove socket allocation from Syslog::enable(). Open and close
the UDP socket on-demand in _sendLog() around each message send.
- MQTT: Fix socket leak in isValidConfig() where a successful test
connection was never closed (PubSubClient destructor does not call
disconnect). Add explicit pubSub->disconnect() before returning.
Made-with: Cursor
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
* Fix TransmitHistory to improve epoch handling
* Enable epoch handling in unit tests
* Improve comments and test handling for epoch persistence in TransmitHistory
* Add boot-relative timestamp handling and unit tests for TransmitHistory
* loadFromDisk should handle legacy entries and clean up old v1 files after migration
* Revert "loadFromDisk should handle legacy entries and clean up old v1 files after migration"
This reverts commit eb7e5c7acfa4ac077fe50980be752e4b42a739b8.
* Add NodeInfoModule integration for RTC quality changes and trigger immediate checks
* Update test conditions for RTC quality checks
* Fix issue 9792, decode packet for TR test
* Fix 9792: Assure packet id decoded for TR test
* Potential fix for pull request finding
Log improvement for failure to decode packet.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* trunk fmt
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Tom Fifield <tom@tomfifield.net>
* Fix for preserving pki_encrypted and public_key when relaying UDP multicast packets to radio.
PKI DMs sent over UDP multicast had their pki_encrypted flag and public_key fields explicitly cleared before being forwarded to the LoRa radio. This caused the receiving node to treat the packet as a channel-encrypted message it couldn't decrypt, silently dropping it.
The MQTT ingress path correctly preserves these fields. The UDP multicast ingress path should behave the same way.
* Zeroize MeshPacket before decoding
Zeroize MeshPacket before decoding to prevent data leakage.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
* Fix RAK4631 Ethernet gateway API connection loss after W5100S brownout
PoE power instability can brownout the W5100S while the nRF52 MCU keeps
running, causing all chip registers (MAC, IP, sockets) to revert to
defaults. The firmware had no mechanism to detect or recover from this.
Changes:
- Detect W5100S chip reset by periodically verifying MAC address register
in reconnectETH(); on mismatch, perform full hardware reset and
re-initialize Ethernet interface and services
- Add deInitApiServer() for clean API server teardown during recovery
- Add ~APIServerPort destructor to prevent memory leaks
- Switch nRF52 from EthernetServer::available() to accept() to prevent
the same connected client from being repeatedly re-reported
- Add proactive dead-connection cleanup in APIServerPort::runOnce()
- Add 15-minute TCP idle timeout to close half-open connections that
consume limited W5100S hardware sockets
Fixesmeshtastic/firmware#6970
Made-with: Cursor
* Log actual elapsed idle time instead of constant timeout value
Address Copilot review comment: log millis() - lastContactMsec to show
the real time since last client activity, rather than always logging the
TCP_IDLE_TIMEOUT_MS constant.
Made-with: Cursor
* Update src/mesh/api/ServerAPI.h
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Stop UDP multicast handler during W5100S brownout recovery
After a W5100S chip brownout, the udpHandler isRunning flag stays
true while the underlying socket is dead. Without calling stop(),
the subsequent start() no-ops and multicast is silently broken
after recovery.
Made-with: Cursor
* Address Copilot review: recovery flags and timeout constant
Move ethStartupComplete and ntp_renew reset to immediately after
service teardown, before Ethernet.begin(). Previously, if DHCP
failed the early return left ethStartupComplete=true, preventing
service re-initialization on subsequent retries.
Replace #define TCP_IDLE_TIMEOUT_MS with static constexpr uint32_t
for type safety and better C++ practice.
Made-with: Cursor
---------
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Add C++17 support
* Add C++17 runtime probes and update build configurations for STM32 and Portduino
* Remove unflags
* Update C++ standard flags across the platforms
* Convert a couple of instances to structured bindings
* NRF52 platform.txt to add C++17 support
* Still need the unflags apparently
* Remove C++17 runtime probe tests from test_main.cpp
* Reconfigured doesnt need a nodiscard
* Remove nodiscard attribute from init() method in RadioInterface
* Remove mbedtls/error.h from build flags
Removed include directive for mbedtls/error.h from build flags.
* Fix IRAM overflow
* Fix IRAM overflow
* Add build flag to exclude MQTT from rak11200
* Update C++ standard from gnu17 to gnu++17
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 <benmmeadors@gmail.com>
* Update Heltec Tracker v2 to version KCT8103L.
* Fixed the issue of inaccurate comments.
---------
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Adds ROUTER_LATE and CLIENT_BASE to preferred rebroadcaster check
(skip unsolicited NodeInfo) and prevents ROUTER_LATE from setting
rebroadcast mode to NONE, which would silently break relaying.
ROUTER_LATE is an infrastructure relay that should use the same power
assumptions, interval defaults, and LoRa wake behavior as ROUTER.
Without this, ROUTER_LATE uses client-class defaults and will not
wake on LoRa activity from deep sleep, making it a broken relay.
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
ROUTER_LATE already has high base intervals and should not be further
scaled by congestion. TAK_TRACKER is a tracker variant that should
skip congestion scaling like TRACKER does.
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
ROUTER_LATE now preserves node_info_broadcast_secs during factory
reset and auto-enables Store & Forward server mode, matching ROUTER
infrastructure behavior.
* refactor: update throttling factor calculation and add unit tests for scaling behavior
* refactor: adjust throttling factor calculation for improved accuracy in different configurations
* Update src/mesh/Default.h
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update src/mesh/Default.h
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* refactor: enhance throttling factor calculation and introduce pow_of_2 utility function
* refactor: improve expected ms calculation in unit tests for Default::getConfiguredOrDefaultMsScaled
* refactor: improve scaling logic for routers and sensors in computeExpectedMs function
---------
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Improve resource cleanup on connection close
* Copilot had some good feedback. Let's just make the api a unique pointer
* Update src/mesh/api/ServerAPI.cpp
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Copilot stupidly suggesting we call protected methods
* Gotta do it in the superclasses as well
* Fix moar
* Refactor MQTT unit test to ensure proper subscription handling and clear side effects
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>