Skip to content

fix(rp): recover RP2350W deployment without USB replug #1303

Description

@zackees

Context

FastLED RP2350W bring-up on the attached Pico 2 W is blocked by a recurring state that currently requires physically unplugging/replugging the board. The requirement is unattended deploy/test operation on the same USB port: application hangs, failed upload transitions, and recoverable USB faults must not require cable removal.

Current evidence with released fbuild 2.5.17:

RP2350W runtime identity: 2E8A:F00F
serial: 2DCB876B587EA334
last runtime port: COM18
COM18: health=phantom present=no selectable=no
Windows: USB\VID_0000&PID_0002, problem code 43
location: Port_#0014.Hub_#0001 (direct root USB port)

fbuild port doctor --port COM18 correctly refuses to attribute the unidentified code-43 node to the stale COM18 record. Windows selective suspend is enabled on this host, so suspend/resume is a candidate condition, not a proven root cause.

The present RP deployer already has two stock recovery paths:

  1. Arduino-Pico-compatible 1200-bps CDC touch (crates/fbuild-deploy/src/rp2040.rs::touch_1200bps).
  2. BOOTSEL/PICOBOOT deployment through managed picotool or the UF2 mass-storage volume.

Both depend on an already usable endpoint. The missing middle path is application-mode picotool forced reboot. Official picotool can force compatible RP2040/RP2350 application firmware into BOOTSEL with -f/reboot -u; Arduino-Pico exposes the required Pico SDK USB reset interface behind ENABLE_PICOTOOL_USB. PlatformIO RP board metadata also declares upload.protocol=picotool plus use_1200bps_touch=true, but the mechanisms have different failure coverage: 1200-bps touch needs working CDC, while forced picotool uses the Pico SDK USB reset interface.

Relevant primary sources:

Deployment binary survey

  • fbuild-managed pqt-picotool 4.0.1 is present and is already used for target-bound PICOBOOT info/load -f -x operations.
  • fbuild's UF2 writer covers the driverless ROM mass-storage path.
  • PlatformIO's historical tool-rp2040tools/rp2040load is another PICOBOOT uploader, but does not replace a recovery trigger; its BeforeUpload still starts with the 1200-bps serial touch.
  • Arduino-Pico's application-mode picotool interface is the distinct secondary reset mechanism worth integrating; it must be compiled into the HIL firmware and invoked before fbuild concludes that only a replug can recover the target.
  • SWD/OpenOCD can recover a target only when an external debug probe is physically wired and is outside this board's current setup.

A true loss of VBUS/data continuity or a host controller that cannot transact with any descriptor cannot be repaired by a device protocol. The actionable target is every state where the MCU, Pico SDK reset interface, ROM PICOBOOT interface, or exact Windows USB port remains software-reachable.

Proposal

Implement a layered, target-bound RP2040/RP2350 recovery state machine in fbuild and validate it with a companion FastLED AutoResearch firmware configuration:

  1. Preserve the existing healthy-CDC 1200-bps touch path.
  2. Before declaring BOOTSEL absent, probe the selected runtime USB identity for the Pico SDK reset interface and use managed picotool forced reboot into USB BOOTSEL. Bind every operation by board family, runtime serial, and known USB identity; never issue an unscoped picotool -f command.
  3. After forced reboot, reacquire the same physical target as PICOBOOT/UF2, deploy, and reacquire application CDC exactly as the normal path does.
  4. Build the RP2350W AutoResearch fixture with the Arduino-Pico/Pico SDK picotool USB reset interface enabled. Keep this opt-in to the managed HIL fixture unless a separate API decision makes it appropriate for all FastLED RP builds.
  5. Keep a hardware watchdog active during AutoResearch deploy/test firmware and add explicit liveness feeding boundaries so a CPU/driver deadlock resets back to a USB-capable application without manual intervention.
  6. On Windows, persist enough healthy-device topology to correlate a later unidentified code-43 node at the exact same physical location. Where fbuild can prove that association, offer an explicit/admin-gated device-local USB/PnP recovery attempt. Do not change host-wide selective-suspend policy automatically and do not cycle an unproven hub/device.
  7. Report which recovery layer ran and why each earlier layer failed: CDC touch, application-mode picotool, existing BOOTSEL/PICOBOOT, watchdog reboot observation, and Windows device-local recovery.

Acceptance criteria

  • Add a focused failing test first for an RP deploy where runtime CDC cannot be opened, no BOOTSEL target is initially visible, but a target-bound application picotool reset interface is available; then implement the recovery that turns it green.
  • Add a focused failing test first proving fbuild never issues forced picotool without an exact selected target identity; turn it green with serial/family/VID/PID scoping.
  • Add state-machine coverage for: healthy CDC touch, failed CDC + successful forced picotool reboot, already-in-BOOTSEL, forced reboot timeout, wrong/multiple boards, runtime CDC reacquisition under COM renumbering, and total device absence.
  • Add Windows topology tests showing an unidentified code-43 node is recoverable only when it can be uniquely correlated to the selected board's last healthy physical location; ambiguous nodes fail closed.
  • Add a focused RED -> GREEN FastLED/AutoResearch test proving the RP2350W fixture enables the Pico SDK reset interface and watchdog policy without changing unrelated RP application builds.
  • On the attached RP2350W (2DCB876B587EA334), demonstrate application-mode managed-picotool detection/reboot through fbuild deploy/bash autoresearch; do not invoke picotool directly.
  • Run at least 50 consecutive build -> deploy -> reboot -> application CDC -> JSON-RPC cycles on the same physical USB port with no cable removal, BOOTSEL button press, stale-port selection, or wrong-device action.
  • Inject/reproduce each software-recoverable fault available to the fixture (RPC-triggered watchdog reset, deliberately unresponsive main loop with USB reset interface alive, failed 1200-bps touch, and an exact device-local Windows recovery when safely reproducible) and recover without replugging.
  • Record fbuild version/commit, board VID:PID and serial, physical USB location, every endpoint transition, selected recovery layer, and decisive RPC output.
  • If code 43 remains after watchdog reset and exact device-local PnP recovery, report it as a host/hardware transport failure with evidence; do not claim a device-protocol fix can recover a USB controller that cannot enumerate.

Decisions

  • Priority: P1 for RP2350W HIL continuity. Manual replugging prevents unattended soak and completion of the active bring-up, although this is not a data-loss bug.
  • Primary implementation owner: fbuild. fbuild owns deploy/reset binaries, identity safety, PICOBOOT/UF2 fallback, and Windows recovery; FastLED should only provide the managed HIL firmware capabilities needed to exercise it.
  • Use managed picotool, not PlatformIO/rp2040load as a new dependency. fbuild already checksum-manages picotool and must remain the sole deploy entrypoint.
  • Keep host mutation narrow and explicit. No automatic host-wide power-plan change, driver replacement, Zadig action, or ambiguous hub cycling.
  • Define “never replug” over software-recoverable states. Physical disconnect, failed cable, loss of VBUS, or an unresponsive host controller with no addressable endpoint remains outside what software can guarantee.

Open questions

  • Does Arduino-Pico 5.7.0's ENABLE_PICOTOOL_USB interface remain responsive during the specific FastLED driver hang that preceded code 43, or must the watchdog fire first? The attached-board fault injection should answer this.
  • Can Windows uniquely correlate the present USB\VID_0000&PID_0002 code-43 node to the last healthy RP2350W topology, or is additional location history needed in fbuild before device-local recovery is safe?

Related issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions