Retry wifi join on transient negative link status - #241
Open
adrianwedd wants to merge 2 commits into
Open
Conversation
wait_status() returns False on timeout, but reconnect_wifi() ignored the return value when waiting for CYW43_LINK_UP. A join that timed out (e.g. associated but never obtained a DHCP lease) fell through to 'Connected successfully!' and ifconfig(), and the caller carried on as if the network were up — readings would then fail to upload with no useful error logged. Raise instead, reporting the link state we were stuck in.
adrianwedd
force-pushed
the
wifi-transient-retry
branch
from
July 29, 2026 11:48
21246be to
42edf3c
Compare
The CYW43 can report a transient negative status shortly after wlan.connect() — typically CYW43_LINK_FAIL ~3s in, and BADAUTH has also been reported with a correct password on a cold boot (and every battery wake of an Enviro board is a cold boot) — while a fresh join attempt made seconds later succeeds. reconnect_wifi() raised on the first negative status via wait_status(), so a single transient blip aborted the whole reading cycle and lit the warning LED even though the network was healthy. Observed on an Enviro Grow (RSSI -53, WPA2/2.4GHz, AP <16% utilised): the firmware logged a connection failure and halted, while the AP's controller showed the same radio subsequently associating with full signal quality; a manual wlan.connect() at the REPL from the same position associated instantly. Retry the join up to 3 times. Between attempts, disconnect and wait for LINK_DOWN first: MicroPython's connect() calls cyw43_wifi_join() without a preceding cyw43_wifi_leave(), and the Pico W SDK advises leaving before rejoining after a failure. This also prevents a stale negative status from the previous attempt being misread as a new failure. wlan.connect() itself is inside the try so a synchronous OSError from a wedged driver counts as a failed attempt instead of crashing the cycle. Retries only run on cycles that would otherwise have failed outright, so the added awake time (worst case ~35s) is confined to the failure path.
adrianwedd
force-pushed
the
wifi-transient-retry
branch
from
July 29, 2026 11:51
42edf3c to
8796988
Compare
adrianwedd
marked this pull request as ready for review
July 29, 2026 12:15
Member
|
Bad auth should be fixed upstream, ala georgerobotics/cyw43-driver#154, rather than patching every downstream consumer. Don't just paste Claude code responses into GitHub, please. It talks too darn much. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Retry wifi join on transient negative link status
Problem
reconnect_wifi()gives up on the first negative status from the CYW43:wait_status()raises as soon aswlan.status()goes negative, and the caller re-raises without retrying the join. In practice the CYW43 can report a transient failure (typicallyCYW43_LINK_FAIL, ~3 s afterwlan.connect()) even though a fresh join attempt made seconds later succeeds. By then the firmware has already logged a failure, lit the warning LED, and gone back to sleep — so every blip costs a full reading-upload cycle and leaves the WARN LED blinking, which reads to users like a real configuration problem.Evidence it's the connect logic, not RF or config
Diagnosed on an Enviro Grow (2.4 GHz, WPA2-only, PMF off, AP under 16% channel utilisation, RSSI −53 at the board's position, AP is a UniFi UDR7):
log.txtshowed association failing on ~50% of 5-minute wakes over one evening, ending in six consecutive failures; each "failure" was logged exactly ~3 s after "connecting" — the first negative status poll.wlan.connect()at the REPL from the same position associated instantly.Fix (two commits)
Commit 1 fixes an independent pre-existing bug:
wait_status(CYW43_LINK_UP)returningFalse(timeout) was ignored, so a join that associated but never got a DHCP lease fell through to "Connected successfully!" and the caller proceeded with a dead network and no useful error. A timeout now raises, reporting the link state it was stuck in.Commit 2 retries the join up to 3 times. Design points, deliberately:
LINK_DOWNfirst. MicroPython'sconnect()callscyw43_wifi_join()with no precedingcyw43_wifi_leave()(extmod/network_cyw43.c), and the Pico W SDK guidance is to leave before rejoining after a failure. This also prevents a stale negative status from the previous attempt being misread as a new failure on the next poll.wlan.connect()is inside thetryso a synchronousOSErrorfrom a wedged driver counts as a failed attempt rather than crashing the cycle.BADAUTH. That is intentional:-3is reported with a correct password on cold boots (see e.g. micropython#12930 discussion of first-attempt −1/−3 statuses), and on battery an Enviro cold-boots on every wake. A genuinely wrong password now costs ~30 s extra once per wake — but a wrongly-abandoned retry on a spuriousBADAUTHwould cost the whole cycle.3 attempts × 1 s pause is a judgment call, not tuned from a large sample: observed transient failures resolved within seconds (manual rejoin instant; radio associated by itself well within the next wake), so three well-separated fresh joins comfortably covers the observed recovery window while keeping the worst case bounded.
Testing
A minimal variant of this retry (re-kick
wlan.connect()on negative status, without the disconnect/teardown — the 0.0.10 code this was diagnosed on has a different loop structure) has been running on the affected Enviro Grow: immediately before, the board had failed six consecutive 5-minute wake cycles; since patching, every wake cycle has connected and uploaded (10 consecutive cycles over the first 50 minutes at time of writing) with the warning LED staying off. The exact code in this PR compiles clean (CPythoncompile()and on-device checks of the equivalent change); I'm happy to run this branch on the board and report back if useful.For context, similar symptoms (intermittent connect failures / blinking warning LED despite good RF) have been reported in the Enviro Grow and Enviro Indoor forum threads — I can't confirm those share this root cause, but the symptom signature matches.