Repository navigation
Rebase Zephyr Env Hub integration onto 11.0.0-alpha.2 - #32
Draft
tyeth-ai-assisted wants to merge 36 commits into
Draft
tyeth-ai-assisted wants to merge 36 commits into
tyeth-ai-assisted wants to merge 36 commits into
Conversation
debug.conf bumps the DWC2 and nRF UDC thread stacks because verbose
logging with LOG_MODE_IMMEDIATE formats in-thread and needs more stack,
but the rpi_pico one was missed. It defaults to 512 bytes, which
overflows during USB enumeration on a Pico 2 W:
***** USAGE FAULT *****
Stack overflow (context area not valid)
>>> ZEPHYR FATAL ERROR 2: Stack overflow on CPU 0
Current thread: 0x200034b0 (usbd@50110000)
The board dies partway through configuration, which looks like a bad USB
come-up rather than a stack problem. Independent of any Bluetooth work —
it affects any RP2040/RP2350 board built with DEBUG=1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The CYW43439's Bluetooth controller shares the WiFi gSPI bus rather than sitting on a UART, so it needs the new shared-bus HCI driver (see the companion zephyr branch). With a bt_hci device in the devicetree, the port's existing Kconfig turns CONFIG_BT on by itself and common-hal _bleio needs no board-specific work. Wire it up for the Pico 2 W: - board overlay: add the infineon,cyw43-bt-hci node as a child of the stock infineon,airoc-wifi node, and point zephyr,bt-hci at it. The hyphenated chosen name matters — the port's Kconfig derives CONFIG_BT via dt_chosen_enabled(zephyr,bt-hci). - board conf: deeper system workqueue stack and HW stack protection, as the WHD WiFi, cybt and coexistence call chains are deeper than the port defaults. - compat2driver: map infineon_cyw43_bt_hci to bluetooth/hci, which is what makes zephyr2cp.py set _bleio. Added by hand rather than regenerating the file, which would churn 500+ unrelated lines against the current Zephyr revision. Builds at 67.1% flash and 43.4% RAM, up from 59.7%/38.0% without BLE. Not hardware-tested. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Four changes needed to get BLE actually working on a Pico 2 W, all found by bisecting the failures over SWD. The settings partition was 2K at 0x180800 — neither erase-sector aligned nor large enough for one of the RP2350's 4K sectors. flash_area_get_sectors() returned zero sectors, settings_nvs then computed its sector size from an uninitialised struct and failed with -EDOM, so bt_enable() returned -33 before ever opening the HCI driver. That surfaced as a bare "OSError: 33" from "import _bleio", which is where the adapter is enabled (the _bleio module's __init__ calls common_hal_bleio_adapter_set_enabled). main has since grown it to one aligned 4K sector at 0x17f000, which fixes the -EDOM but is still one sector short: nvs_mount() rejects fewer than two sectors with -EINVAL. Give settings two sectors at 0x17e000, taking the extra one from the code partition. nvm and circuitpy stay at 0x180000 and 0x181000, where cptools/check_partitions.py requires them to match ports/raspberrypi, so the CIRCUITPY filesystem is not moved. A board whose settings sectors hold content from a previous layout then fails differently: NVS reads it as "all sectors closed" and refuses to mount with -EDEADLK. CONFIG_NVS_INIT_BAD_MEMORY_REGION lets it reclaim a region it does not recognise, so the first boot after a layout change recovers on its own instead of needing a manual erase over SWD. The CYW43439's BT controller firmware (CYW4343A2_001.003.016.0065.0000) does not implement the Bluetooth 5 extended advertising/scanning commands. It rejects LE Set Extended Scan Parameters (0x2041) with status 0x01 "Unknown HCI Command", so scanning failed with -EIO. The port defaults BT_EXT_ADV on, which is right for the nRF parts but wrong here, so turn it off for this board and let the host use the legacy 0x200B/0x200C commands. Also raise the system workqueue stack and enable HW_STACK_PROTECTION (the MPU turned several silent corruptions into clean, named faults while debugging this), and enable BT HCI driver/host debug logging in debug.conf. Verified on hardware: patchram loads over the shared gSPI bus, the controller reports BD_ADDR 2C:CF:67:B7:62:AC (= WiFi MAC + 1), and a scan returns nearby advertisers by name. That run used the earlier layout with settings at 0x181000; the 0x17e000 placement is build-tested only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Same CYW43439 as the Pico 2 W, and the shared-bus HCI driver needed no board-specific work, so this is the Pico 2 W change applied to the RP2040 board: - overlay: hang the infineon,cyw43-bt-hci node off the stock infineon,airoc-wifi node and point zephyr,bt-hci at it. - overlay: the settings partition had the same defect as the Pico 2 W's -- originally 2K at 0x180800, neither erase-sector aligned nor big enough for one of the RP2040's 4K sectors; main has since made it one aligned 4K sector at 0x17f000, which is still one short of the two nvs_mount() insists on, so bt_enable() fails before opening the HCI driver. Give settings two sectors at 0x17e000, taken from the code partition. nvm and circuitpy stay at 0x180000 and 0x181000 to match ports/raspberrypi (cptools/check_partitions.py checks this), so CIRCUITPY is not moved. - conf: BT_EXT_ADV=n, since this controller rejects the Bluetooth 5 extended advertising commands, plus a deeper system workqueue stack. HW_STACK_PROTECTION is deliberately left off here: it earned its place on the Cortex-M33 but costs RAM, and this is the constrained board. Builds at 68.67% flash and 84.32% RAM of 264K, so BLE fits with about 42K to spare. Verified on hardware after the "ranges" fix (now on main): the adapter comes up as WiFi MAC + 2 and a legacy LE scan returns nearby advertisers. That run used the earlier layout with settings at 0x181000; the 0x17e000 placement is build-tested only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The custom board workflow predates the zephyr-cp port and cannot build its boards: - It relies on make's default goal to produce firmware.*, but the zephyr-cp Makefile's default goal is the Zephyr ELF, so the firmware.* copies the artifact upload globs for are never made and the upload is empty. Name the firmware.<ext> targets explicitly instead, the way tools/build_release_files.py already does, via a small helper that resolves CIRCUITPY_BUILD_EXTENSIONS for either kind of port. - The port deps action does not check out hal_rpi_pico's cyw43-driver submodule, which the CYW43 shared-bus Bluetooth transport needs for the controller patchram. Fetch it during port setup. Also point the west manifest at the fork branches carrying the Pico 2 W Bluetooth work so the artifact is BLE-capable. That override is CI-only and is marked as such -- it must be reverted before merging. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A CircuitPython program serving HTTP over the station link to a browser that opens several connections per page, with an NTP or mDNS UDP socket beside it, sits at Zephyr's default of six net_contexts: the listener, every accepted client and every UDP socket come out of the same pool, and the seventh socket() or accept() fails with ENOMEM. The BLE stack does not compete for these, but the hub use-case this board is aimed at -- WiFi station + HTTP + BLE advertising and scanning at the same time -- does. Double the pool (CONFIG_NET_MAX_CONTEXTS=12) and the connection-handler table that scales with it (CONFIG_NET_MAX_CONN=16); ZVFS_OPEN_ADD_SIZE_NET follows automatically. Pico 2 W only: the RP2040 board has 42 KB left and runs no server. Build (local, same tree as ci/pico2w-ble-assets @ 94bfd47): FLASH 1,146,096 B (unchanged) RAM 229,144 -> 231,112 B (+1,968 B, 43.03% -> 43.40%) .config: NET_MAX_CONTEXTS=12 NET_MAX_CONN=16 ZVFS_OPEN_ADD_SIZE_NET=12 Not run on hardware. Python side that uses it: tyeth/deepsleep_espnow_wifi_and_ble_env_collector "max_sockets" in collector/config.json (caps.MAX_SOCKETS). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The rpi_pico UDC driver's internal thread stack was only raised in debug.conf, so release builds kept Zephyr's 512-byte default and dropped off USB partway through a _bleio scan -- CDC port and CIRCUITPY volume both disappearing, with a USB device/stack error on reset. Move it to prj.conf so every build gets it. Verified on a Pico 2 W: a 12s active scan yielding 925 reports from 20 distinct devices now completes with USB intact, where the same workload previously killed the bus. This commit used to also raise BT_MAX_CONN to 4 on the Pico 2 W (Zephyr defaults to 1). Upstream has since set CONFIG_BT_MAX_CONN=5 port-wide in prj.conf, so that part is dropped: keeping it would now lower the limit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
start_scan(timeout=...) never ended on the Pico 2 W: the ScanResults loop ran until Ctrl-C, and the KeyboardInterrupt then surfaced on the first statement after the loop (stop_scan() in one run, the print() after it in another), which looked like a stall in scan teardown or in CDC output. gdb on the running board showed nothing blocked. The main thread was in common_hal_bleio_scanresults_next() waiting for the next entry with done still false; bt_dev.flags had BT_DEV_SCANNING set; ncmd_sem.count was 1 and sent_cmd 0 (no HCI command outstanding); the gSPI bus mutex was free; the BT RX poll thread was in its 4 ms k_msleep; every work queue and USB thread was pended idle. stop_scan() itself took 5-6 ms when called explicitly. The cause is in Zephyr's host: bt_le_scan_param.timeout is only passed to the controller on the extended-scanning path (LE Set Extended Scan Enable carries a duration and the controller reports LE Scan Timeout). start_le_scan_legacy() never reads it, and the legacy path is what CONFIG_BT_EXT_ADV=n selects -- which a controller without extended advertising, such as the CYW43439, forces. So the timeout was silently ignored and the scan ran forever. Keep the deadline in the adapter and enforce it from bleio_background(), called from port_background_task() on the main thread, where bt_le_scan_stop() is safe to call (it blocks on an HCI round-trip, which must not happen on the system work queue that also runs the USB CDC console). The ScanResults iterator finishes at the deadline as it does on nRF and ESP32. Measured on hardware, Pico 2 W: timeout=3 s -> loop exited after 3.0 s; timeout=1 -> 1.0 s; timeout=0.5 -> 0.53 s; timeout=2 -> 2.05 s (30 reports). Before the change a timeout=3 scan was still yielding at 57 s (1600 reports). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
wrap_socket(server_side=True) configured mbedTLS with VERIFY_REQUIRED whenever the context had the default root bundle attached, which every ssl.SSLContext() does. On a server that means "require a client certificate", so a plain HTTPS server -- load_cert_chain() and nothing else -- failed every handshake with MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE (-0x7480) and the client saw a timeout. Default server-side contexts to VERIFY_NONE, as CPython's do (CERT_NONE for servers); a context that loaded its own CA with load_verify_locations() still verifies clients. Client-side behaviour is unchanged. Seen on a Pico 2 W serving a captive portal over its softAP; the same code runs on every port using the shared mbedTLS module. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nothing in the port, the SoC defaults or these boards' Kconfig turned CONFIG_NET_TCP on, so every SOCK_STREAM socket failed inside net_context_get() with EPROTOTYPE -- which socketpool reported as a generic "Out of sockets" -- and the web workflow's listener never opened either. Enable TCP on both boards, and raise the fd table to 16: Zephyr sized it from the subsystems' declared needs (13 here), which the DHCPv4 server and the socket-service eventfd eat into. Three socketpool fixes found on the way: - A socket object is allocated with a finaliser and zeroed before zsock_socket() runs. If that failed, num stayed 0 and the finaliser later called zsock_shutdown(0). fd 0 belongs to the socket service's eventfd, whose shorter vtable has no shutdown slot, and the CPU branched into cdc_acm_1's data: "USAGE FAULT / Illegal use of the EPSR", pc 0x200003f8, lr z_impl_zsock_shutdown, system halted. Mark the object closed (num = -1) before attempting creation. - Report errno from a failed zsock_socket() (ENOENT: no net_context left, EPROTOTYPE: protocol off, ENFILE: fd table) instead of the fixed message. - Accepted sockets did not inherit the listener's timeout: the Python-facing accept path only set num, so a fresh object read as timeout 0 and the ssl layer's first recv on an accepted TLS connection raised EAGAIN before the handshake could finish. Inherit it, as the other ports do. Measured on a Pico 2 W with the softAP up: 11 TCP sockets open before ENOENT (12 net_contexts, one held by the DHCPv4 server). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… boards Two Kconfig gaps kept an HTTPS server (or client) from ever completing a handshake on these boards: - Zephyr's mbedTLS 4 default serves secp256r1 through the p256-m PSA driver and leaves the builtin ECP module out. mbedTLS then defines the dummy MBEDTLS_ECP_MAX_BITS 1, and ssl.h sizes the TLS 1.2 premaster buffer (union mbedtls_ssl_premaster_secret._pms_ecdh[MBEDTLS_ECP_MAX_BYTES]) from it: sizeof(handshake->premaster) was 1 in the firmware (DWARF). Every ECDHE key agreement then failed -- "psa_raw_key_agreement() returned -138 (-0x008a)", PSA_ERROR_BUFFER_TOO_SMALL for a 32-byte shared secret -- and the raw PSA status leaked through mbedtls_ssl_read() as OSError 138 on the first read of any TLS connection. Disabling the p256-m driver brings the builtin ECP module back and the buffer is 32 bytes again. This is an upstream sizing bug (ssl.h should size that buffer from PSA when the ECP module is absent); the Kconfig is the workaround until it is fixed. - PEM parsing was not compiled in, so load_cert_chain() with the usual fullchain.pem / key.pem could only fail; the collector's certstore is PEM. Verified on a Pico 2 W: an RSA-2048 PEM certificate served from CIRCUITPY over the softAP; an ESP32-C6 client completed the handshake and received "HTTP/1.0 200 OK" in 2.0 s. Flash +1,868 B (PEM) and +4,716 B (builtin ECP instead of p256-m) on the Pico 2 W; no static RAM change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
wifi.radio.start_ap() returned silently with ap_active False: the whole body of common_hal_wifi_radio_start_ap() was commented-out ESP-IDF code. The AIROC driver already implements ap_enable/ap_disable through wifi_mgmt, so this is the hook-up, plus the pieces around it. start_ap(ssid, password, channel, authmode, max_connections) issues NET_REQUEST_WIFI_AP_ENABLE with the CircuitPython authmode mapped to WIFI_SECURITY_TYPE_NONE / PSK / SAE (the driver brings every PSK variant up as WPA2-AES-PSK; WPA1/TKIP is not offered separately). max_connections gets the ESP32 range check but cannot be applied: the CYW43439's association limit is fixed in the controller. The AP gets 192.168.4.1/24 by default (as on ESP32), settable with set_ipv4_address_ap(), and the DHCPv4 server starts with it (pool of 8 above the AP address, DNS pointing at the AP, RFC 8910 option 114 so phones and laptops find a captive portal automatically); start_dhcp_ap()/stop_dhcp_ap() remain explicit controls. stop_ap() tears it all down. ap_active asks the driver (WIFI_MODE_AP in the interface status) rather than trusting a flag. stations_ap lists the stations the driver reported through NET_EVENT_WIFI_AP_STA_CONNECTED/DISCONNECTED with the IPv4 address from the DHCP lease table (no RSSI: wifi_mgmt's station events carry none). set_ipv4_address() for the station side, start_dhcp() and stop_dhcp() are implemented on the way. The AIROC driver runs the AP on the same net_if as the station and refuses AP_ENABLE with -EBUSY while the station is associated, so there is no simultaneous AP+STA on these boards: disconnect() first. Soft reboot tears the AP down (wifi_reset), and start_ap() disables a running AP the driver still reports before enabling, so the two cannot get out of step -- a soft reboot was seen to leave the driver's flag set when its stop failed. Needs the AIROC driver fix in tyeth/zephyr (branch airoc-softap-fixes): without it the driver's chanspec composition makes the firmware reject every 2.4 GHz channel, and a station leaving took the AP's interface dormant. Verified on a Pico 2 W with an ESP32-C6 as the station: start_ap() completes in 0.15 s; the C6 associates in 7 s, gets 192.168.4.2 from the DHCP server with gateway/DNS 192.168.4.1, pings the AP in 5-8 ms; stations_ap shows it with its lease, empties when it disconnects (the AP stays active), and shows it again on rejoin; the AP survives stop_ap()/start_ap() cycles and a soft reboot. Flash +9,444 B, RAM +3,568 B (DHCPv4 server + socket service). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The port had no frozen-module support: its Python-driven build never runs make, and the freeze pipeline lives in py/circuitpy_mpconfig.mk. Reimplement the pipeline in cptools/build_circuitpython.py behind a per-board opt-in, FROZEN_MPY_DIRS = ["frozen/<lib>", ...] in circuitpython.toml (paths relative to the repository root, as $(TOP)/... is in mpconfigboard.mk): - tools/preprocess_frozen_modules.py stages the trees into <build>/frozen_mpy (repo directory dropped, __version__ filled in, examples and tests left out), before the qstr pass; - MICROPY_QSTR_EXTRA_POOL / MICROPY_MODULE_FROZEN_MPY are added to the flags for the qstr pass as well as the compile -- Q(.frozen) and the sys.path entry that uses it are behind MICROPY_MODULE_FROZEN; - after genhdr/qstrdefs.generated.h and root_pointers.h exist, mpy-cross compiles each module (-s with the module path, so mpy-tool derives the frozen name from it) and tools/mpy-tool.py -f -q emits frozen_content.c, fed the same collected qstr list that produced the generated header so the frozen pool numbers from MP_QSTRnumber_of correctly. tools/makemanifest.py is bypassed: it insists on genhdr/qstrdefs.preprocessed.h, which this builder never produces. Only MPY freezing: MICROPY_MODULE_FROZEN_STR would reference the mp_frozen_str_* tables makemanifest emits. - frozen_content.c is compiled with the no-qstr sources (it defines its own MP_QSTR_* enum values and must never go through extraction). pre_zephyr_build_prep.py builds mpy-cross first when a board freezes modules (honouring MICROPY_MPYCROSS), and tools/ci_fetch_deps.py learns the toml key so CI initialises the right frozen/ submodules (it had a TODO for this). The Pico W opts in with adafruit_ble: it has ~42 KB of heap and a BLE node cannot otherwise load the library. Measured on a Pico 2 W (same core, same flags): importing adafruit_ble plus its advertising.standard and services.nordic modules costs 10,384 B of heap frozen vs 21,792 B from .mpy files on CIRCUITPY (gc.mem_alloc() delta after gc.collect(); gc.mem_free() is not usable here, the split heap grows on demand), 0.055 s vs 0.132 s. Freezing the 20 modules adds 28,336 B of flash and no static RAM on the Pico W (1,208,636 -> 1,236,972 B with the rest of this series). The Pico 2 W is not opted in: it has the heap, and a frozen copy would pin the library version for everyone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…R RTT The Zephyr console on these boards is UART0 on GP0/GP1, which needs a wired probe UART (and the Debug Probe bridge only forwards while DTR is asserted). When USB CDC is down after a kernel halt that is the only way to see what the firmware said. RTT gives the same printk/LOG_* stream over the SWD probe that is already attached, read with OpenOCD (rtt setup/start/server) as plain memory polls, so it does not disturb the target or USB. rtt.conf is a Kconfig fragment appended to the board conf list the same way debug.conf is for DEBUG=1; the two compose. Both rp2040 and rp2350 select HAS_SEGGER_RTT when the segger module is present. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
tyeth-ai-assisted
changed the base branch from
main
to
codex/circuitpython-alpha2-base
October 22, 2026 04:36
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.
Rebase the Pico W/Pico 2 W Env Hub integration onto CircuitPython 11.0.0-alpha.2, retaining shared-bus BLE, bounded scanning, socket/TLS policy, station lifecycle, AP DHCP and RTT work while omitting fixes already upstream. Version headers refresh on Git changes, empty
CP_VERSIONfalls back to Git, and mpy-cross retains its build directory.Commit
3254e272628epins Zephyr5bf4230db7fe. The first passive scan after connectable advertising could fail with errno22 / HCI LE Set Scan Enable status0x12 on Pico 2 W. Zephyr5bf4230db7fefixes the mismatch by tracking implicit identity use and selecting the actual advertiser's identity, including nondefault identities. A new nondefault-identity unit case fails on the preceding source; five executed configurations yield 19 pass/1 fail/35 skip before and 20 pass/0 fail/35 skip after. The unit fixture compilesid.c; physical public-identity comparisons exercise the real advertising path. A revised diagnostic full-hub run passes MCU boot, all 44 deployed-file readbacks, twelve public-address scan starts and twelve HTTP JSON checks. The scanner reports no error butble_rx=0, so node delivery is not established. Full original 4 MiB flash, original files/identity/radios, network/import/power state and independent postflights are restored and verified. Earlier failed jobs retain their status, including a minimal diagnostic missing its initial capture marker despite four successful scan cases. Reproduction, exact pins, failed controls and scope.Both normal Pico builds pass at CP
3254e272628e/ Zephyr5bf4230db7fe, with clean dependencies and no firmware diagnostic hooks. The normal Pico 2 image now passes actual Pico W node advertisement reception: three changing synthetic readings match captured node UART values, with RSSI, receive counts 4/5/6 and twelve HTTP checks. Both original storage images restore byte-exact; original board/version/files and independent postflight pass. Original Pico 2 radio health is explicitly not certified: its earlierimport wifitimeout also reproduces without the sender. Earlier failed attempts remain failed. The new normal Pico W image remains build-only here, and production retains restored9e153. Paired evidence and limits.Commit
5f73172d46fixes CP #43: MSC now propagates block errors, validates ranges without overflow, and refreshes cached FAT sectors only after successful writes. A direct Pico W comparison reports false GOOD on old9e153and proper CHECK CONDITION / WRITE ERROR on the candidate for the same rejected synthetic-MBR request. Valid partition writes and three free-sector pattern/restoration checks pass with whole-disk verification. Original code/full expected flash and three radio checks are restored. All 56 native checks pass versus 34 failures on preceding source; both normal Pico builds pass. Pico 2 physical MSC subtests now pass on the same candidate: false GOOD on baseline, correct CHECK CONDITION on the candidate, valid partition write and three free-sector patterns with complete disk restoration. Its original job remains failed at a later ROM USB identity guard before any restore write; separate recovery verifies original code/full expected flash, three Wi-Fi/BLE cycles and original port state. The mismatched USB tuple was not captured, so the guard failure's cause remains unproven. Injected read errors, the separate #42 stall and Pico 2 64 KiB USB/IP latency remain unqualified. Exact reproduction, preserved guard failure and limits.The production ready-firmware guard is HAL Pico #8, commit
de12fc2a0f75, pinned by CP0d545efdcf74. Its actual-source UBSan fixture passes 17/17 cases versus 8/17 on the preceding source, including readiness clearing after a controller reset. Both normal Pico images build with matching clean dependencies and automatic BLE workflows compiled in, without diagnostic hooks. Both Pico W and Pico 2 W now pass normal startup, stable BLE identity and three disable/enable cycles with Wi-Fi/BLE scans. Each restores original 9e153 code with whole expected-image verification and three radio checks; host bindings and Pico 2 port/import state are restored without restarting the bridge. Two earlier Pico 2 original-firmware preflight stalls remain preserved and unexplained. A matched control using the unchanged Repl.exec and passive recording also passes; no client framing fix is established. Pico 2 forced-error retries are now qualified by a later three-software-error control: usable Wi-Fi after each failure, one firmware download across four attempts, stable BLE identity and three scans. Exact diagnostic and restoration. Natural hardware faults and end-to-end workflow client transfers remain unqualified. The separate CP #42 USB blocker still prevents permanent rollout. Exact builds, minimal probe, preserved failures and qualification limits.Earlier commit
6839890534pins Zephyr #11 atc6200e80ea69: repeated active USB configuration reinitializes endpoints, and cancelled CDC OUT completion rearms reception after returning its buffer. Configuration reset alone failed CDC. The combined diagnostic passed four ordinary Pico 2 USB/IP reconnects, CDC checks, matching uncached disk reads and correct first-INQUIRY exchanges. Full production flash was restored exactly, followed by three Wi-Fi/BLE checks. Twenty-three actual-source native checks pass; the two old handlers each fail two checks. Controlled USB reproduction, failed candidate, recovery and limits. Installed Pico production remains9e153ad624; no production rollout or new long-duration soak is claimed for this pin.Earlier
9e153ad624implements station start/stop lifecycle (#40): 46 actual-source checks pass, and both installed Pico boards pass connect/stop/reconnect, repeated stop, disabled-start and AP-preservation checks. Pico 2 additionally passes two exact 20,495-byte TLS responses with BLE activity and wrong-host/default-untrusted/explicit-unrelated-CA rejection. Certificate date validity is deliberately unchecked. Pico 2 evidence and preserved fixture failures.The preceding socket-service priority fix
6b117bdce5resolves reproduced AP DHCP starvation: priority 0 issues a lease during a 15-second Python busy loop where priority 15 failed. Both boards passed actual channels 1/6/11, DHCP, ping, station MAC/IP, departure and AP stop/restart; Pico W also passed concurrent BLE. Scheduling control, Pico W qualification.Other carried validation includes unsupported extended-scan rejection without replacing an active scan, startup/error and settings guards, shared-radio-bus RX locking, TLS server client-certificate policy, and PSA ECDH sizing controls. Prior tests include 26 scan checks, 60 startup/address checks, 10 Nordic BLE simulator cases and 85 build-tool tests. Nordic simulations do not validate CYW43 hardware. Source audit and minimal reproductions.
Prior Pico W two-hour/240-cycle and Pico 2 thirty-minute/60-cycle passes retain their older firmware attribution. The earlier 140-cycle stall and later USB fixture failure remain failed evidence; they are not explained by unrelated passes. Replacement soak, USB idle failure.
A direct-connected Pico W now reproduces the baseline failure on the second repeated configuration selection, independently of the S31 bridge. The actual
6839890534build passes four selections, eight exact whole-disk reads, CDC before/after each pair, and three Wi-Fi/BLE checks. Original code and host bindings are restored, with three further radio checks. The restoration snapshot preserves only a 10-byte runtime revision change inboot_out.txt; all other file contents are unchanged. The baseline failure and Linux's subsequent recovery remain separate evidence. Direct Pico W comparison and restoration audit.The remaining mixed CDC/MSC failure is now directly identified as S31 host-channel exhaustion: four allocation-failure messages per mixed import order with three physical leaves, none in either single-Pico control. With the unused C6 off and the same three hubs, firmware, descriptors and admission, both mixed orders and both single controls pass with zero exhaustion messages. Exact live channel counts remain unmeasured. The headroom job remains failed because C6 power-on was followed by other leaves reappearing; original firmware passed full-image restoration, but topology/runtime cleanup failed. A separate no-flash recovery restored original topology and passed three Wi-Fi/BLE checks, without bridge reboot. Capacity scheduling and general composite coverage remain open. Instrumented comparison, minimal reproduction and recovery limits.
Keep draft for production USB rollout, natural startup-failure recovery, USB idle/capacity/storage coverage and the remaining coverage ledger gaps. No fresh hosted CI result is claimed.
A later Pico W production attempt is blocked by an additional intermittent console-close storage defect, tracked in CP fork #42. Two candidate runs time out on the initial capacity query before reconfiguration rounds; a USBmon trace records a TEST UNIT READY completing after 10.126 seconds as the console reopens. Matched controls on the older 9e153 build reproduce 2.068/4.066-second reads with reopen scheduled after 2/4 seconds, while open-console and DTR-only controls complete in roughly 30–60 ms; one closed-port attempt does not stall. CPU-busy controls also pass. No priority or USB source fix is claimed. Both candidate attempts are preserved as failures; separate/automatic recovery restores original code with full-image verification, three radio checks and host bindings. Both production Picos remain on 9e153; Pico 2 rollout is not attempted. Evidence, minimal reproduction and recovery limits.