- net_guard_tick now detects status-vs-event divergence. If s_up is
true but WiFi.status() says otherwise (rare: driver wedge, silent
RF failure), force DOWN state and schedule reconnect. Uses 0xFF
disconnect reason so the event log distinguishes this path.
- Forward-declare DeviceConfig in net_guard.h so consumers that don't
call net_guard_start don't transitively pull config.h.
loop() no longer blocks for 5s after a disconnect; reconnect is
scheduled from the WiFi event handler with exponential backoff.
Buffered reports flush on every clean UP transition.
- 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.
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.