feat(firmware): event-driven WiFi reconnect with exponential backoff

net_guard registers WiFi.onEvent() so disconnects are handled
immediately instead of polled every 1s. Backoff 1s->2s->4s->...->60s cap.
Every up/down transition is logged to the event log with the disconnect
reason code, so field failures are diagnosable.
This commit is contained in:
2026-04-23 13:26:10 -07:00
parent 95724bf3ff
commit 9f293b4639
3 changed files with 111 additions and 0 deletions

View File

@@ -0,0 +1,55 @@
// firmware/lib/net_guard/net_guard.cpp
#include "net_guard.h"
uint32_t net_guard_next_backoff_ms(uint32_t attempt) {
if (attempt >= 6) return 60000;
return 1000u * (1u << attempt);
}
#ifdef ARDUINO
#include <WiFi.h>
#include "event_log.h"
static const DeviceConfig* s_cfg = nullptr;
static volatile uint8_t s_last_disconnect = 0;
static volatile bool s_up = false;
static volatile uint32_t s_attempts = 0;
static volatile uint32_t s_next_retry_ms = 0;
static void on_wifi_event(WiFiEvent_t event, WiFiEventInfo_t info) {
switch (event) {
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
s_up = true;
s_attempts = 0;
s_next_retry_ms = 0;
event_log_write(EVT_WIFI_UP, (uint16_t)(int16_t)WiFi.RSSI(), 0);
break;
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
s_up = false;
s_last_disconnect = (uint8_t)info.wifi_sta_disconnected.reason;
event_log_write(EVT_WIFI_DOWN, s_last_disconnect, 0);
s_next_retry_ms = millis() + net_guard_next_backoff_ms(s_attempts);
break;
default: break;
}
}
void net_guard_start(const DeviceConfig& cfg) {
s_cfg = &cfg;
WiFi.onEvent(on_wifi_event);
WiFi.setAutoReconnect(false); // we drive reconnect ourselves
}
bool net_guard_is_up() { return s_up; }
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;
s_attempts++;
WiFi.disconnect(false, false);
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);
}
#endif

View File

@@ -0,0 +1,24 @@
// firmware/lib/net_guard/net_guard.h
#pragma once
#include <stdint.h>
// Exponential backoff: 1s, 2s, 4s, 8s, 16s, 32s, 60s, 60s, ...
// attempt 0 -> 1000ms, clamped at 60000ms.
uint32_t net_guard_next_backoff_ms(uint32_t attempt);
#ifdef ARDUINO
#include "config.h"
// Registers WiFi.onEvent() handler and starts auto-reconnect loop.
// Must be called once after WiFi.begin() succeeds.
void net_guard_start(const DeviceConfig& cfg);
// True iff WiFi is currently associated with IP.
bool net_guard_is_up();
// Last disconnect reason code from WIFI_EVENT_STA_DISCONNECTED (0 = none).
uint8_t net_guard_last_disconnect_reason();
// Non-blocking tick called from loop(); kicks reconnect if due.
extern "C" void net_guard_tick();
#endif