Repository navigation
Design History
Why the firmware looks the way it does. This is the one place the historical narrative lives; for how it works today, see Architecture.
openrb-pico began as a rewrite of openrb-instruments — the original DIY Rock Band MIDI pro-drums adapter for Xbox One/Series, an Arduino/AVR build (ATmega32u4 Leonardo, on LUFA). That firmware is the mature, shipping reference this project targets; the goal was to move it onto the more capable RP2040, initially on the Adafruit Feather RP2040 USB Host.
The move's central challenge was the USB stack. The RP2040 has a single native USB controller, so openrb-pico bit-bangs a second USB host in PIO (Pico-PIO-USB) to read the instrument while the native USB serves the console-facing device side — two USB roles at once, split across the chip's two cores.
The firmware then evolved through three phases on the RP2040, each covered below: a C superloop, then a FreeRTOS-SMP task model, then a rewrite to modern C++23 with a proper HAL. Getting the PIO-USB host reliable runs through the first of those.
The port started by getting an Xbox controller to enumerate through a CH334R hub on a
bit-banged PIO-USB host, which meant bumping the whole USB stack: pico-sdk 1.5.1 → 2.2.0,
TinyUSB 0.16 → 0.18, Pico-PIO-USB 0.5.3 → 0.7.2, usb_midi_host → 2.0.0. The firmware was
then a dual-core bare-metal superloop written in C: core0 ran the device + features,
core1 was dedicated to tuh_task.
Two root-cause bugs had to be fixed to get reliable enumeration:
-
core1 early-launch race —
multicore_launch_core1could fall back to the bootrom if the inter-core handshake raced; fixed with a settle/reset dance. - PIO-USB timing margin through the hub repeater — the extra hop cost signal margin; originally worked around by overclocking to 240 MHz to double the PIO sub-bit timing resolution.
Overclocking was later dropped. Upgrading Pico-PIO-USB to its upstream main
(post-0.7.2 bus-turnaround / handshake timing fixes) made enumeration reliable every time
from a cold start at 120 MHz — the clock the PIO-USB library is actually designed for. The
old 0.7.2 needed 240 MHz only because it lacked those fixes; with them, 240 MHz and an
accompanying PID-mismatch patch were both retired. 120 MHz has been the clock ever since,
unchanged by the FreeRTOS port.
The evidence (rev 0.2 hardware, boot-success rate over 10 resets; "success" = the controller's IN endpoint returns reports):
| Config | Result |
|---|---|
| pico_pio_usb 0.7.2, 240 MHz, both patches | 10/10 |
| pico_pio_usb 0.7.2, 120 MHz, both patches | 0/10 — IN data corrupted |
upstream main, 120 MHz, hub patch only |
enumerates every time |
With the old 0.7.2, 120 MHz was a hard blocker regardless of which core hosted; the upstream
upgrade is what made it viable and let the overclock and the PID-mismatch patch go — only the
hub-descriptor patch (wValue=0x2900) remains. (For contrast, Santroller reaches 120 MHz a
different way — forked pico_pio_usb timing with the host on core0 — not via patches; its fork
is coupled to TinyUSB 0.20 glue and won't compose partially.)
Two pitfalls have repeatedly produced false enumeration results — honor them before trusting any A/B:
-
SWD
reset≠ the physical RESET button for this board's USB-wedge behaviour. Ground truth is a physical reset while watching the controller LED (LED on = enumerated). - The CH334R hub stays powered across RP2040 resets (downstream VBUS is hardwired +5 V on rev 0.2), so it accumulates bad state and wedges until a full power cycle. Keep reset batches small and power-cycle between candidates.
-
pico-sdk 2.2.0 (fork
delabrcd/pico-sdk@openrb) -
TinyUSB 0.20 (fork
delabrcd/tinyusb@openrb) — supersedes the 0.18 named in older notes; bringsUSBH_CONTROL_RETRY_MAX -
Pico-PIO-USB upstream
main(forkdelabrcd/Pico-PIO-USB@openrb) - FreeRTOS-Kernel V11.2.0 (RP2040 SMP port)
- usb_midi_host 2.0.0
One submodule patch remains (a hub-descriptor wValue fix); the 240 MHz PID patch is gone.
The bare-metal superloop was moved onto a FreeRTOS-SMP task model: both cores run under
one scheduler, core1 is a single usb_host task, and everything else is core0 tasks
(CFG_TUSB_OS=OPT_OS_FREERTOS). This was a concurrency-model change only — clock, patches,
and library composition were untouched.
The old core1 launch race became a new hazard in this model: a sleep_ms in pre-scheduler
boot code deadlocks (the SDK sleep needs the scheduler), so boot code uses busy_wait_ms.
That was "the single nastiest bug of the port."
With the concurrency model settled, the firmware was rewritten from its original C (a
flat src/*.c + inc/* layout) to modern C++23, restructured into the layered
modules/ tree it has today. The rewrite brought static dependency injection with a single
composition root — no singletons, no heap — and a zero-cost compile-time HAL (hardware
access through concepts and value types) that cleanly separates the portable
feature/protocol logic from the pico-sdk, with the layering enforced in CI. See
Architecture for the end state.
On the rev-0.2 board the downstream hub VBUS is hardwired to +5 V, so firmware can't
power-cycle the controller. Across an RP2040 warm reset the controller keeps stale USB
session state and comes back as a "silent zombie" — it mounts but never sends input.
Recovery is layered (recovery.cpp,
host_controller.cpp):
- Runtime hub-RESET# pulse (preferred, no reboot) — on core1, when a wedge is detected (a long IN-error streak, or 30 s of silence from a mounted controller), pulse the hub's active-low RESET# to force a clean re-enumeration. Bounded to 3 attempts, self-clearing on a fresh heartbeat, and it stands down after exhaustion so a real unplug can't thrash the hub.
- Watchdog reboot (last resort) — on core0, auth-gated and bounded to 4 reboots (the count survives across reboots in a watchdog scratch register, cleared on a real power-on), for a mounted-but-silent controller that the hub reset didn't recover.
The real fix is a hardware respin adding a firmware-controlled downstream VBUS switch; the
FEATHER board already has one (GPIO18), which is why it doesn't need this machinery.
Getting started
Features
Background
Repo