Skip to content

zephyr-cp/wifi: bound the scan wait and run background tasks - #60

Closed
mikeysklar wants to merge 19 commits into
mainfrom
fix/zephyr-scan-poll-timeout-upstream
Closed

zephyr-cp/wifi: bound the scan wait and run background tasks#60
mikeysklar wants to merge 19 commits into
mainfrom
fix/zephyr-scan-poll-timeout-upstream

Conversation

@mikeysklar

Copy link
Copy Markdown
Owner

common_hal_wifi_scannednetworks_next() waits with k_poll(..., K_FOREVER) and never runs background tasks.

The wait ends only on a result, a channel_done signal, or ctrl-C. Nothing guarantees any of those arrive. If a scan is abandoned the radio can stop reporting, and the VM thread then parks permanently. Because there is no RUN_BACKGROUND_TASKS in the loop, the supervisor's background callbacks stop too, so the web workflow goes down with the REPL.

Observed on a Silicon Labs SiWx917 DK2605A: the board stopped answering on the console UART and on HTTP while still enumerating to the debugger, and needed commander device reset to recover.

What this does

Polls in 50 ms slices with background tasks between them, and ends the scan if nothing arrives for 10 s.

This matches the other ports. Both run background tasks in the equivalent loop and neither blocks indefinitely:

  • ports/espressif/common-hal/wifi/ScannedNetworks.c:39
  • ports/raspberrypi/common-hal/wifi/ScannedNetworks.c:67

zephyr-cp was the only one that did.

Hardware tested

SiWx917-DK2605A (BRD2605A), CircuitPython 10.3.0-alpha.4, associated to a WPA2 AP, host on the same LAN.

Four consecutive scans from code.py while a host polled /cp/version.json every 2 s:

scan 0 results 57 uniq 9 secs 8.8
scan 1 results 53 uniq 9 secs 8.9
scan 2 results 51 uniq 10 secs 8.9
scan 3 results 53 uniq 8 secs 9.0

Web workflow: 12 requests during active scanning, 12 x HTTP 200, 0 failures. Before the change the workflow was not served for the duration of a scan.

The 10 s timeout never tripped in normal operation. Results arrive continuously; it exists only so a radio that goes quiet ends the scan instead of parking the VM.

Also built for nordic_nrf7002dk to check the change compiles and links on a board that is in tree today.

Not tested: any other zephyr-cp board on real hardware, AP mode, or the timeout path itself firing (I have not reproduced a driver going quiet on demand).

Cost

No RAM. The deadline is a local, which matters on the DK2605A where the whole CircuitPython heap measures 7888 bytes.

Flash is tight on nordic_nrf7002dk: it sits at 100% of its 944 KB region with 44 bytes free after this change. An earlier version used k_uptime_get() and a printk on the timeout path and overflowed that region by 28 bytes, so the margin there is real and worth knowing about independently of this PR.

Scope

Does not address the duplicate and dropped scan results also visible in the numbers above (51-57 raw for 8-10 unique). That is a separate problem: two 10-deep queues in series, CONFIG_NET_MGMT_EVENT_QUEUE_SIZE and MAX_BUFFERED_SCAN_RESULTS, both of which drop, and it needs a design decision rather than a bug fix.

AI assistance

Written with Claude Code. I ran the hardware, and the numbers above are from the board and the host, not from a model. The comparison against the espressif and raspberrypi loops was checked against the source in this repo.

weblate and others added 2 commits August 17, 2026 18:55
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: CircuitPython/main
Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/
check_suite["workflowRun"] is null for check suites that are not attached to a
workflow run, for example one that was deleted or one belonging to an app
integration. get_commit_depth_and_check_suite() dereferenced it
unconditionally:

    TypeError: 'NoneType' object is not subscriptable

which fails the scheduler job's Get-last-commit-with-checks step. Skip those
entries rather than indexing into them.
@mikeysklar mikeysklar closed this Aug 17, 2026
@mikeysklar mikeysklar reopened this Aug 17, 2026
dhalbert and others added 9 commits August 17, 2026 18:31
…wrun

tools: skip check suites with no workflowRun in ci_changes_per_commit.py
…main

Translations update from Hosted Weblate
Prevent click noise on `audiomixer.Mixer` and `synthio.Synthesizer` during volume updates
Prevent `audiomixer.Mixer` from resetting all voices when played
common_hal_wifi_scannednetworks_next() waited with k_poll(K_FOREVER). That wait
ends only on a result, a channel_done signal, or ctrl-C. Nothing guarantees any
of those arrive, so if the driver stops reporting there is no way out of the
loop.

This is a defensive bound, not a fix for an observed field failure. A normal
scan is unaffected: results arrive steadily and the loop cycles well inside the
limit.

espressif and raspberrypi both bound their equivalent loops and run background
tasks inside them (ports/espressif/common-hal/wifi/ScannedNetworks.c:39,
ports/raspberrypi/common-hal/wifi/ScannedNetworks.c:67). zephyr-cp did neither.

Poll in 50 ms slices with background tasks between them, and end the scan if
nothing arrives for 10 s. The window restarts whenever a channel completes, so
the limit is on the driver going quiet rather than on how long a full sweep
takes. Elapsed time is an unsigned subtraction from the start, which stays
correct across the 32-bit millisecond rollover.

No new state: the start timestamp is a local, so this costs no RAM.
@mikeysklar
mikeysklar force-pushed the fix/zephyr-scan-poll-timeout-upstream branch from 5f57e85 to 629523a Compare August 19, 2026 01:55
@mikeysklar

Copy link
Copy Markdown
Owner Author

Superseded by adafruit#11214, which targets adafruit:main directly. Closing this one so there is a single thread for the fix.

@mikeysklar mikeysklar closed this Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants