fix(rp2040): Pico W always boots the W firmware (no `import network` crash)

Two robustness fixes for the paid-WiFi open-core split:

1. A pi-pico-w board now boots the RPI_PICO_W firmware variant (which has the
   `network` module) based on its BOARD KIND, not on whether the WiFi
   peripheral happens to be attached. Previously the variant was
   `pioPeripheral ? 'pico-w' : 'pico'`, so any moment the peripheral was
   absent (see #2) booted the plain Pico firmware and a Pico W sketch crashed
   with "ImportError: no module named 'network'". Store boardKind in
   attachPioPeripheral and pick the variant from it. (OSS: 'pico-w' isn't
   registered, so firmwareConfig falls back to 'pico' — a self-hosted Pico W
   has no WiFi engine anyway.)

2. Re-attach the PIO peripheral in loadMicroPythonProgram before loading
   firmware. An example deep-link adds the board during render, which races the
   pro overlay's async mountPro that installs the CYW43 factory — so the
   board-add attach returned null and a PAID user's Pico W booted plain
   firmware too. attachPioPeripheral is idempotent; by run time the factory is
   installed, so a paid user gets the W peripheral and real WiFi.
This commit is contained in:
David Montero 2026-06-15 18:55:13 +02:00
parent 90c2d1da51
commit d1088aefc2
2 changed files with 27 additions and 4 deletions

View File

@ -165,6 +165,12 @@ export class RP2040Simulator {
// in OSS (no factory installed); attached for boards a factory supports.
private pioPeripheral: PioPeripheral | null = null;
private pioHookedFifos: Array<{ restore: () => void }> = [];
// The board kind this simulator runs (set by attachPioPeripheral). A
// 'pi-pico-w' boots the RPI_PICO_W firmware (with the `network` module)
// regardless of whether a WiFi peripheral attached, so a Pico W sketch never
// crashes with "ImportError: no module named 'network'" — even if the pro
// factory hadn't installed yet when the board was added.
private boardKind = '';
/** Serial output callback — fires for each byte the Pico sends on UART0 (or USBCDC in MicroPython mode) */
public onSerialData: ((char: string) => void) | null = null;
@ -268,10 +274,14 @@ export class RP2040Simulator {
files: Array<{ name: string; content: string }>,
onProgress?: (loaded: number, total: number) => void,
): Promise<void> {
// A WiFi/gSPI peripheral (the pro overlay's CYW43) is attached only for
// pi-pico-w boards; its presence selects the RPI_PICO_W firmware variant
// (network + driver + bigger LittleFS). OSS has no peripheral -> 'pico'.
const variant = this.pioPeripheral ? 'pico-w' : 'pico';
// A pi-pico-w boots the RPI_PICO_W firmware variant (network + driver +
// bigger LittleFS) whether or not a WiFi peripheral attached — so a Pico W
// sketch never crashes on `import network`. The pro overlay registers that
// variant; in OSS it isn't registered and firmwareConfig() falls back to
// 'pico' (a self-hosted Pico W has no WiFi anyway). The pioPeripheral check
// stays as a belt-and-suspenders for any future factory-backed board.
const variant =
this.boardKind === 'pi-pico-w' || this.pioPeripheral ? 'pico-w' : 'pico';
console.log(`[RP2040] Loading MicroPython firmware (${variant})...`);
// 1. Get MicroPython UF2 firmware (cached in IndexedDB)
@ -385,6 +395,10 @@ export class RP2040Simulator {
* fragile FIFO plumbing + GPIO24 host-wake lifecycle stay here.
*/
attachPioPeripheral(boardKind: string, boardId: string): PioPeripheral | null {
// Record the kind even when no peripheral attaches (free user / OSS /
// factory-not-installed-yet) so loadMicroPython still picks the W firmware
// for a pi-pico-w board.
this.boardKind = boardKind;
if (this.pioPeripheral) return this.pioPeripheral;
const peripheral = createPioPeripheral(boardKind, boardId);
if (!peripheral) return null;

View File

@ -1557,6 +1557,15 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
// RP2040 path: load firmware + filesystem in browser
const sim = getBoardSimulator(boardId);
if (!(sim instanceof RP2040Simulator)) return;
// (Re)attach the PIO peripheral before loading firmware. An example
// deep-link adds the board during render, which can race the pro
// overlay's async mountPro that installs the CYW43 factory — so the
// board-add attach returned null and a paid user's Pico W would boot
// the plain firmware (no `network` -> ImportError). attachPioPeripheral
// is idempotent; by run time the factory is installed, so a paid user
// gets the W peripheral -> the RPI_PICO_W firmware variant. No-op in
// OSS (no factory) and for free users (factory returns null).
sim.attachPioPeripheral(board.boardKind, boardId);
await sim.loadMicroPython(files);
}