From 84d9ba349b17185ed29cb415f4cdaf022437c96b Mon Sep 17 00:00:00 2001 From: Peter Woolery Date: Thu, 23 Apr 2026 13:31:47 -0700 Subject: [PATCH] fix(firmware): net_guard boot-state seed + no spurious disconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Seed s_up from WiFi.status() in net_guard_start so the first STA_GOT_IP (fired during setup's busy-wait, before onEvent was registered) is not missed — prevents a reconnect flap on every boot. - Drop WiFi.disconnect() from net_guard_tick; WiFi.begin() alone re-associates cleanly and avoids a spurious STA_DISCONNECTED that was double-logging EVT_WIFI_DOWN on every retry. - Re-check s_up after the millis() timing gate to close the GOT_IP-vs-tick race. - Document the volatile-only shared-state contract. --- firmware/lib/net_guard/net_guard.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/firmware/lib/net_guard/net_guard.cpp b/firmware/lib/net_guard/net_guard.cpp index df59d8f..f0e0cf1 100644 --- a/firmware/lib/net_guard/net_guard.cpp +++ b/firmware/lib/net_guard/net_guard.cpp @@ -10,6 +10,9 @@ uint32_t net_guard_next_backoff_ms(uint32_t attempt) { #include #include "event_log.h" +// Shared with the WiFi event task. 32-bit aligned loads/stores are atomic on +// Xtensa; volatile suffices. Tick re-evaluates every loop iteration, so stale +// reads self-correct within ~200ms. static const DeviceConfig* s_cfg = nullptr; static volatile uint8_t s_last_disconnect = 0; static volatile bool s_up = false; @@ -36,6 +39,10 @@ static void on_wifi_event(WiFiEvent_t event, WiFiEventInfo_t info) { void net_guard_start(const DeviceConfig& cfg) { s_cfg = &cfg; + // Seed s_up from the current WiFi state. setup()'s busy-wait on + // WiFi.begin() can produce a STA_GOT_IP before onEvent() is registered; + // without this seed, the first tick would force a spurious reconnect. + if (WiFi.status() == WL_CONNECTED) s_up = true; WiFi.onEvent(on_wifi_event); WiFi.setAutoReconnect(false); // we drive reconnect ourselves } @@ -47,8 +54,11 @@ uint8_t net_guard_last_disconnect_reason() { return s_last_disconnect; } extern "C" void net_guard_tick() { if (s_up || s_cfg == nullptr) return; if (millis() < s_next_retry_ms) return; + if (s_up) return; // re-check after the timing gate — closes GOT_IP-vs-tick race s_attempts++; - WiFi.disconnect(false, false); + // WiFi.begin() alone re-associates cleanly; a prior WiFi.disconnect() call + // synchronously emits STA_DISCONNECTED on the event task, which would + // double-log EVT_WIFI_DOWN (reason=ASSOC_LEAVE) on every retry. WiFi.begin(s_cfg->wifi_ssid.c_str(), s_cfg->wifi_pass.c_str()); s_next_retry_ms = millis() + net_guard_next_backoff_ms(s_attempts); }