WARNING: This software is strictly for authorized educational and security testing purposes; any unauthorized use or deployment is entirely at your own risk and liability.
pico-bit is an open source Rust Embassy firmware for the Raspberry Pi Pico 2 W. It combines a USB Host HID DuckyScript runtime, USB NCM file delivery, LittleFS-backed storage, and a Wi-Fi-hosted dashboard for authorized security research, lab automation, and defensive validation.
Current release: v0.2.2
Pico Bit began as a private MicroPython prototype in August 2025. That version established the device workflow and exposed timing and USB NCM constraints that motivated a no_std Rust implementation. Development of the Rust firmware started in April/May 2026, and the public repository begins with the first working baseline that matched the prototype's scope.
Since that initial publication, development has continued through focused changes, tagged releases, CI validation, and testing on a Raspberry Pi Pico 2 W with a hardware debug probe. Work on CYW43 WPA3 SoftAP support was also contributed upstream and merged into Embassy PR #6529.
- Single-file dashboard served at
http://192.168.4.1 - WPA2/WPA3 transition-mode Wi-Fi AP with AES encryption
- Boot-time and on-demand execution of the saved
payload.dd - Browser editor with firmware-backed save, validation, and run actions
- Host typing target selection by operating system and keyboard layout, persisted in LittleFS
- Host HID and NCM link state surfaced in the dashboard
- Binary Armory upload, download, copy-link, and delete operations over LittleFS
- Protected
payload.ddfile: visible in the file table, downloadable, but not deletable - Recent boot/manual run history for the current boot session
- Compact runtime metrics for storage, staged binary, payload execution, and upload state
- Session-local action timeline and line-addressable validation diagnostics
- Single replaceable Armory binary with a 750 KB upload limit enforced in both frontend and firmware
- On-board status LED patterns for boot, network, HID, payload, Armory, and error states
- Single gzipped Svelte 5 + Tailwind v4 dashboard artifact embedded into firmware flash
cargo-embedas the default local flash, RTT, and debug toolchain- Release workflow that builds and attaches
firmware-{version tag}.uf2andfirmware-{version tag}.elf
- Supported board: Raspberry Pi Pico 2 W (
RPI_PICO2_W) - Firmware target:
thumbv8m.main-none-eabihf - The Pico USB data port provides Host HID and USB NCM transport.
- The Wi-Fi dashboard is exposed through the Pico AP.
- The status LED uses the Pico 2 W on-board LED through the CYW43 controller.
| What | Value |
|---|---|
| Wi-Fi SSID | PicoBit |
| Wi-Fi password | PicoBit24Net |
| Wi-Fi security | WPA2/WPA3 Personal |
| Dashboard URL | http://192.168.4.1 |
| NCM file root | http://192.168.7.1 |
| NCM staged binary | http://192.168.7.1/api/armory/payload.bin |
The dashboard has no portal login in v0.2.2. AP credentials are firmware build-time values. WPA3-capable clients may negotiate SAE; older clients can continue using WPA2-PSK.
The UI is intentionally small and operational:
- Top status cards show AP details, Host HID readiness, and NCM link state.
- The DuckyScript editor edits the single
payload.ddfile. - Save validates and writes the editor text to LittleFS.
- Run saves the current editor text first, then triggers Host HID execution.
- Validation failures open the validation modal and do not trigger execution.
- Selecting a validation diagnostic focuses the matching editor line and column.
- Binary Armory lists
payload.ddand the single staged binary, supports upload/download/copy-link/delete, and blocks deletion ofpayload.dd. - Binary state shows exact size, NCM accessibility, and the direct NCM URL.
- Layout controls update the firmware keyboard target immediately and persist it across reboots.
- Recent runs show compact current-session run metadata.
- Activity shows the six most recent resolved portal actions without retaining verbose logs.
The frontend uses only these endpoints:
| Method | Path | Purpose |
|---|---|---|
GET |
/api/bootstrap |
Fixed startup state: AP, Host HID, NCM, keyboard target, seeded flag |
GET |
/api/armory |
Bounded LittleFS file listing |
POST |
/api/armory/upload |
Stream upload to /armory/payload.bin, replacing any existing Armory binary |
GET |
/api/armory/:filename |
Stream file download |
DELETE |
/api/armory/:filename |
Delete Armory file, except payload.dd |
GET |
/api/payload |
Read current payload.dd |
POST |
/api/payload |
Validate and overwrite payload.dd |
POST |
/api/payload/run |
Validate saved payload.dd and trigger Host HID execution |
POST |
/api/keyboard/layout |
Update keyboard OS/layout codes |
GET |
/api/runs |
Current-session run history |
GET |
/api/metrics |
Bounded storage, staged binary, run, and upload metrics |
There is no status API, auth API, login/logout route, or browser-storage startup restore flow in v0.2.2.
The NCM surface exposes only the Armory list and staged binary download; payload.dd downloads remain portal-only.
- LittleFS is mounted at boot.
/armoryis created automatically.payload.ddis created automatically if missing./keyboard.cfgstores the active OS/layout target and is hidden from Armory listing and deletion.- The editor always overwrites
payload.dd. - Armory stores one replaceable binary at
/armory/payload.bin; a new upload removes previous Armory files, openspayload.binonce, then writes request chunks. - File listings are bounded and serialized without heap allocation.
| Limit | Value |
|---|---|
| Payload editor buffer | 2 KB |
| Armory upload limit | 750 KB |
| Armory upload stream chunk | 4 KB |
| Armory download stream chunk | 1 KB |
| Run history | 6 entries |
| Dashboard artifact | dist/index.html.gz |
The firmware mirrors important boot, Wi-Fi, USB, HTTP mutation, Armory, keyboard, and payload execution events to the existing TX-only SPI debug task. This provides field diagnostics without requiring an RTT probe.
| Signal | Pico GPIO | Direction |
|---|---|---|
| SPI0 SCK | GP2 | Pico output |
| SPI0 MOSI | GP3 | Pico output |
| Ground | GND | Shared reference |
Capture the stream with a 3.3 V-compatible SPI receiver or logic analyzer configured for the firmware SPI0 defaults at 31.25 MHz. The stream is TX-only with no chip-select signal; records are ASCII text terminated by \r\n. It is not UART-compatible.
Representative records:
[BOOT] Pico Bit v0.2.2 starting
[WIFI] portal ready at http://192.168.4.1
[HID] host ready
[ARMORY] upload committed bytes=4096 duration_ms=28
[PAYLOAD] execution source=run result=ok
Logging uses a fixed, non-blocking queue. If the receiver is absent or the queue is briefly saturated, firmware work continues and excess records may be dropped rather than delaying USB, networking, or flash operations. AP passwords and payload contents are never written to this stream.
| State | Pattern |
|---|---|
boot |
3 x 80 ms on / 80 ms off |
setup_entered |
6 x 400 ms on / 200 ms off |
setup_ap_starting |
1 x 180 ms on / 120 ms off, 250 ms gap |
setup_ap_ready |
1 x 700 ms on |
setup_server_ready |
2 x 220 ms on / 140 ms off |
hid_constructed |
1 x 350 ms on / 250 ms off, 600 ms gap |
usb_enumerated |
3 x 350 ms on / 250 ms off, 600 ms gap |
payload_entered |
2 x 350 ms on / 250 ms off, 600 ms gap |
payload_ready |
4 x 350 ms on / 250 ms off, 600 ms gap |
payload_running |
3 x 120 ms on / 90 ms off, 180 ms gap |
payload_complete |
2 x 500 ms on / 300 ms off |
binary_injecting |
4 x 90 ms on / 70 ms off, 160 ms gap |
binary_inject_failed |
5 x 90 ms on / 70 ms off, 260 ms gap |
keyboard_layout_changed |
3 x 80 ms on / 80 ms off, 220 ms gap |
loot_imported |
3 x 180 ms on / 80 ms off, 120 ms gap |
usb_agent_mounted |
2 x 80 ms on / 80 ms off, 120 ms gap |
usb_enum_timeout |
1 x 80 ms on / 80 ms off |
script_error |
4 x 350 ms on / 250 ms off, 1500 ms gap |
payload_read_failed |
5 x 350 ms on / 250 ms off, 1500 ms gap |
payload_find_failed |
6 x 350 ms on / 250 ms off, 1500 ms gap |
setup_ap_failed |
7 x 350 ms on / 250 ms off, 1500 ms gap |
setup_server_failed |
8 x 350 ms on / 250 ms off, 1500 ms gap |
payload_missing |
10 x 350 ms on / 250 ms off, 1500 ms gap |
Build the embedded dashboard first, then build/check firmware:
npm ci --prefix web
npm --prefix web run build
cargo check --target thumbv8m.main-none-eabihfInstall the local flash/debug tool once:
cargo install cargo-embed --lockedFlash and run on a connected RP2350 board. The project cargo run path routes through Embed.toml and opens the default RTT session through cargo-embed:
cargo runOpen the GDB server profile:
cargo embed gdbBuild release firmware:
npm --prefix web run build
cargo build --release --target thumbv8m.main-none-eabihf
cp target/thumbv8m.main-none-eabihf/release/pico-bit firmware-v0.2.2.elf
elf2uf2-rs firmware-v0.2.2.elf firmware-v0.2.2.uf2Frontend gates:
npm --prefix web run check
npm --prefix web run lint
npm --prefix web test -- --run
npm --prefix web run buildFirmware gates:
cargo fmt --all -- --check
cargo check --target thumbv8m.main-none-eabihf
cargo clippy --target thumbv8m.main-none-eabihf --no-deps -- -D warnings -A clippy::new_without_default
cargo build --release --target thumbv8m.main-none-eabihfHost-side tests for firmware-agnostic modules:
cargo test --lib --no-default-features --target x86_64-unknown-linux-gnuOn macOS without a Linux cross-linker, use the native host target for local testing:
cargo test --lib --no-default-features --target aarch64-apple-darwinFor local hardware smoke testing with the committed cargo-embed configuration:
cargo runThe release workflow runs when a GitHub release is published with a tag matching:
v*.*.*
For v0.2.2, publish a GitHub release tagged:
v0.2.2
The workflow builds the web dashboard, compiles and signs the firmware, converts the signed ELF to UF2, and attaches:
firmware-v0.2.2.uf2firmware-v0.2.2.elf
Pico Bit source code is MIT licensed. The bundled CYW43 Wi-Fi blobs retain their separate terms in firmware/THIRD_PARTY_LICENSE.md. The temporary vendored CYW43 driver patch retains its upstream MIT OR Apache-2.0 licenses in vendor/cyw43.
v0.2.2 is complete for the documented UI and firmware scope. Device configuration is firmware-owned, transport responsibilities are explicit, and dynamic dashboard state is sourced from bounded runtime state or LittleFS.
Pico Bit is experimental embedded firmware intended for authorized labs, education, and continued development. Releases are exercised on physical Pico 2 W hardware, but the project has not undergone an independent security audit or product certification.
