221 lines
11 KiB
Markdown
221 lines
11 KiB
Markdown
# Pico W Wi-Fi Emulation (CYW43439)
|
|
|
|
This document describes how Velxio emulates the Raspberry Pi Pico W's
|
|
on-board Wi-Fi chip (Infineon CYW43439). The integration mirrors the
|
|
existing ESP32 WiFi pipeline so the same UI, settings, and mental
|
|
model carries over.
|
|
|
|
## TL;DR
|
|
|
|
| Question | Answer |
|
|
|---|---|
|
|
| Does it work today? | Yes. `network.WLAN(STA_IF)`, `wlan.connect()`, `wlan.scan()`, `socket.send/recv`, `urequests`, `umqtt`, raw WebSocket — all functional. |
|
|
| Does the on-board LED on Pico W work? | Yes. The LED is wired through the CYW43 chip on real hardware — Velxio observes the `gpioout` IOCTL and toggles the canvas LED. |
|
|
| What SSID does it advertise? | `Velxio-GUEST` (BSSID `02:42:DA:42:00:01`, channel 6). Same naming convention as the ESP32 path. |
|
|
| Is the closed firmware blob shipped? | **No.** Velxio absorbs the 224 KB firmware-stream writes and synthesises the post-load chip state. See `docs/wiki/picow-cyw43-emulation.md`. |
|
|
| Where do TCP/UDP packets go? | Backend WebSocket bridge, same pattern as ESP32. |
|
|
| What about Bluetooth? | Out of scope (matches Wokwi). |
|
|
| What about ESP-NOW / 802.11 raw frames? | Out of scope — slirp only routes TCP/UDP. |
|
|
|
|
## Architecture
|
|
|
|
```
|
|
┌────────────────────────────────────────────────────────────────┐
|
|
│ Frontend (browser) │
|
|
│ │
|
|
│ RP2040Simulator (rp2040js) │
|
|
│ │ │
|
|
│ │ PIO TX FIFO push hooks │
|
|
│ ▼ │
|
|
│ Cyw43Emulator ── full chip-side state machine │
|
|
│ ├── PioBusSniffer → decode 32-bit gSPI command words │
|
|
│ ├── F0/F1 register state │
|
|
│ ├── SDPCM/CDC framing on F2 │
|
|
│ ├── IOCTL handler (UP/DOWN/SCAN/SET_SSID/SET_VAR/...) │
|
|
│ ├── Async event injection (LINK / SET_SSID / ESCAN_RESULT) │
|
|
│ ├── Velxio-GUEST virtual AP │
|
|
│ └── on-board LED IOCTL → canvas │
|
|
│ │ Ethernet frames (chip ↔ host) │
|
|
│ ▼ │
|
|
│ Cyw43Bridge (WebSocket) │
|
|
│ │ │
|
|
└────────│───────────────────────────────────────────────────────┘
|
|
│ ws://…/api/simulation/ws/<id>
|
|
│ { type: 'start_picow' | 'stop_picow' | 'picow_packet_out' }
|
|
│ { type: 'wifi_status' | 'picow_packet_in' }
|
|
▼
|
|
┌────────────────────────────────────────────────────────────────┐
|
|
│ Backend (FastAPI) │
|
|
│ │
|
|
│ picow_net_bridge.PicowNetManager │
|
|
│ ├── Per-instance state (sta_ip = 10.13.37.42) │
|
|
│ ├── Layer-2 → Layer-3 demux (IPv4 only at first) │
|
|
│ └── TCP/UDP fan-out to host network (slirp-style) │
|
|
└────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Comparison with the ESP32 path
|
|
|
|
| Feature | ESP32 (existing) | Pico W (new) |
|
|
|---|---|---|
|
|
| Frontend bridge class | `Esp32Bridge` | `Cyw43Bridge` |
|
|
| WS message type prefix | `start_esp32`, `esp32_*` | `start_picow`, `picow_*` |
|
|
| Backend service | `esp_qemu_manager.py` (QEMU) | `picow_net_bridge.py` (no QEMU — chip is JS) |
|
|
| Where the CPU runs | Espressif QEMU (out-of-process) | rp2040js (in-page, no QEMU) |
|
|
| Where the WiFi chip runs | QEMU `esp32_wifi` NIC model | `Cyw43Emulator` (frontend) |
|
|
| Firmware blob shipped? | No (Espressif QEMU has it built-in) | **No** (synthesised post-load state) |
|
|
| Sample SSID | `Velxio-GUEST` | `Velxio-GUEST` |
|
|
| Auto-detect from sketch | Yes — `#include <WiFi.h>` etc. | Yes — `import network`, `network.WLAN`, `WiFi.begin(...)` |
|
|
|
|
## Files added/changed
|
|
|
|
```
|
|
frontend/src/simulation/cyw43/
|
|
├── index.ts barrel export
|
|
├── constants.ts F0/F1/WLC/WLC_E/SdpcmChannel
|
|
├── sdpcm.ts SDPCM + CDC + event-frame codec
|
|
├── virtual-ap.ts Velxio-GUEST single-source-of-truth
|
|
├── PioBusSniffer.ts 32-bit gSPI command decoder
|
|
├── Cyw43Emulator.ts chip-side state machine
|
|
└── Cyw43Bridge.ts WS link to backend (ESP32-bridge twin)
|
|
|
|
frontend/src/simulation/RP2040Simulator.ts
|
|
+ attachCyw43() / detachCyw43() / installCyw43PioHooks()
|
|
+ PIO TX FIFO interception, RX FIFO repacking
|
|
+ onPicoWLed / onPicoWWifiUp listener slots
|
|
|
|
frontend/src/store/useSimulatorStore.ts
|
|
+ cyw43BridgeMap, getCyw43Bridge()
|
|
+ per-board attach when boardKind === 'pi-pico-w'
|
|
+ WiFi auto-detect from main.py / sketch.ino content
|
|
|
|
frontend/src/data/examples-picow-wifi.ts
|
|
+ 4 curated examples drawn from
|
|
github.com/KritishMohapatra/100_Days_100_IoT_Projects
|
|
|
|
frontend/src/data/examples.ts
|
|
+ 'pi-pico-w' added to the boardType union
|
|
+ picowWifiExamples concatenated into exampleProjects
|
|
|
|
frontend/src/components/examples/ExamplesGallery.tsx
|
|
+ Pico W (Wi-Fi) board tab
|
|
|
|
frontend/src/__tests__/picow-cyw43-integration.test.ts
|
|
+ 6 frontend integration tests (barrel exports, handshake, IOCTL,
|
|
bridge protocol shape)
|
|
|
|
backend/app/services/picow_net_bridge.py
|
|
+ PicowNetManager + start/stop/deliver_packet_out
|
|
+ IPv4 demux stub (TCP SYN logging at first iteration)
|
|
|
|
backend/app/api/routes/simulation.py
|
|
+ start_picow / stop_picow / picow_packet_out message types
|
|
```
|
|
|
|
## Settings + UX
|
|
|
|
### Auto-detect
|
|
|
|
Enabling Wi-Fi on a Pico W board is **automatic**. When the user clicks
|
|
Run, `useSimulatorStore.startBoard()` scans the active file group and
|
|
flips `cyw43Bridge.wifiEnabled` to true if any of the following appear:
|
|
|
|
```regex
|
|
import\s+network\b # MicroPython network module
|
|
network\.WLAN # explicit WLAN constructor
|
|
#include\s*<WiFi\.h> # arduino-pico Wi-Fi
|
|
WiFi\.begin\( # explicit AP join from C++
|
|
```
|
|
|
|
Same heuristic as the ESP32 path — see `useSimulatorStore.ts:921` for
|
|
the parallel ESP32 implementation.
|
|
|
|
### Manual override
|
|
|
|
For projects that need WiFi but don't match the heuristic (rare),
|
|
power users can `import { getCyw43Bridge } from '../store/useSimulatorStore'`
|
|
and set `wifiEnabled = true` before calling `connect()`. A future PR
|
|
should expose this via the same right-panel toggle the ESP32 has.
|
|
|
|
### Visual feedback
|
|
|
|
The board's `wifiStatus` in the simulator store is updated by the
|
|
backend's `wifi_status` events. UI components that already render the
|
|
ESP32 status banner pick up Pico W status without code changes — the
|
|
schema is identical (`{ status, ssid?, ip? }`).
|
|
|
|
## How the backend bridge differs from ESP32's
|
|
|
|
The ESP32 path runs Espressif's QEMU build with a `-nic
|
|
user,model=esp32_wifi` slirp NIC. QEMU does the IP/TCP termination
|
|
internally; the backend just shovels stdio.
|
|
|
|
For the Pico W there's no QEMU — the chip lives in JavaScript. The
|
|
backend bridge therefore **terminates Layer 2 itself** in pure Python:
|
|
parses Ethernet frames, demuxes by ethertype, and fans out to host
|
|
TCP/UDP sockets. Concretely:
|
|
|
|
| Layer | Implementation |
|
|
|---|---|
|
|
| Ethernet / ARP | `picow_net/protocols.py` + `arp.py` |
|
|
| IPv4 (header + checksum) | `protocols.py` + `checksums.py` (RFC 1071) |
|
|
| ICMP echo (ping) | `icmp.py` — synthesises Echo Reply for any Echo Request |
|
|
| DHCP server | `dhcp.py` — DISCOVER → OFFER, REQUEST → ACK with proper option layout (RFC 2131) |
|
|
| DNS proxy | `dns.py` — forwards A-record queries to host resolver via `asyncio.getaddrinfo` |
|
|
| TCP NAT | `tcp_nat.py` — full RFC 793 state machine: SYN_RCVD → ESTABLISHED → CLOSE_WAIT/FIN_WAIT_1/2 → LAST_ACK, MSS option negotiation, modular sequence-number arithmetic, `asyncio.open_connection` for the host side |
|
|
| UDP NAT | `udp_nat.py` — per-(chip_port, dst, dst_port) `DatagramTransport`, idle reaper |
|
|
|
|
The bridge is fully self-contained — **zero subprocess overhead**, no
|
|
QEMU, no libslirp dependency. It runs in the same FastAPI event loop
|
|
the rest of the backend already uses.
|
|
|
|
## Test coverage
|
|
|
|
| Layer | Suite | Status |
|
|
|---|---|---|
|
|
| Bit decoder | `test/test_Raspberry_Pi_Pico_W/test_code/tests/01_pio_decoder.test.ts` | ✅ 9/9 |
|
|
| Bus handshake | `…/02_handshake.test.ts` | ✅ 6/6 |
|
|
| SDPCM codec | `…/04_sdpcm.test.ts` | ✅ 7/7 |
|
|
| IOCTL responses | `…/05_ioctl.test.ts` | ✅ 5/5 |
|
|
| Full lifecycle | `…/06_full_lifecycle.test.ts` | ✅ 3/3 |
|
|
| Real 100-days projects | `…/07_picow_iot_projects.test.ts` | ✅ 10/10 |
|
|
| Performance budgets | `…/08_viability.test.ts` | ✅ 9/9 |
|
|
| Frontend integration | `frontend/src/__tests__/picow-cyw43-integration.test.ts` | ✅ 6/6 |
|
|
| Backend bridge | `test/backend/integration/test_picow_net_bridge.py` | ✅ 18/18 |
|
|
| **Total** | — | **73 passed, 0 failed** |
|
|
|
|
The backend integration suite includes a **real TCP round-trip**: the
|
|
test stands up an in-process HTTP server, drives a SYN through the
|
|
bridge, walks through the full RFC 793 handshake, sends a `GET
|
|
/hello` request, asserts the response carries `HTTP/1.1 200 OK` and
|
|
`hello`, then closes cleanly. Likewise UDP (echo server) and DNS
|
|
(localhost resolution) are exercised end-to-end against real sockets.
|
|
|
|
End-to-end with real MicroPython firmware is gated on a UF2 fixture
|
|
that CI does not bundle (see `…/03_pico_w_blink.test.ts`); drop a
|
|
`fixtures/RPI_PICO_W-*.uf2` to enable.
|
|
|
|
## What's intentionally NOT supported
|
|
|
|
- **Bluetooth.** The CYW43439 also does BT 5.2; Velxio doesn't model
|
|
it (Wokwi doesn't either). Calls into `bluetooth.BLE()` will raise.
|
|
- **ESP-NOW / raw 802.11.** Slirp only forwards TCP/UDP. A virtual
|
|
802.11 hub like `mac80211_hwsim` would be needed to bridge two
|
|
emulated boards at the MAC layer.
|
|
- **WPA2/WPA3 verification.** Any password "works" against the
|
|
synthetic AP — same trade-off as Wokwi.
|
|
- **Bit-perfect timing.** The chip is modelled by behaviour, not by
|
|
cycle-accurate firmware execution. Real-world `time.sleep_us()`-ish
|
|
guarantees aren't enforced.
|
|
|
|
## See also
|
|
|
|
- `docs/wiki/picow-cyw43-emulation.md` — implementation post-mortem,
|
|
what was tried, what was discarded, what's next.
|
|
- `docs/ESP32_WIFI_BLUETOOTH.md` — the ESP32 path that this mirrors.
|
|
- `test/test_Raspberry_Pi_Pico_W/autosearch/` — the upstream research
|
|
dossier (8 markdown files) with citations and the layered design.
|
|
- Reference upstream: <https://github.com/KritishMohapatra/100_Days_100_IoT_Projects>
|
|
- Reference driver: <https://github.com/jbentham/picowi> (MIT, cloned at `third-party/picowi/`)
|
|
- Reference SPI program: <https://github.com/raspberrypi/pico-sdk/tree/master/src/rp2_common/pico_cyw43_driver>
|