How It Works¶
Joypad OS is a modular firmware that translates between any input device and any output protocol. It runs on RP2040, ESP32-S3, and nRF52840 microcontrollers.
Data Flow¶
Input Sources Router Output Targets
───────────── ────── ──────────────
USB HID ──────┐ ┌──→ PCEngine
USB XInput ───┤ ├──→ GameCube
Bluetooth ────┼──→ router_submit_input() ──→ router ──→ ────┼──→ Dreamcast
WiFi (JOCP) ──┤ │ ├──→ Nuon
SNES / N64 ───┤ ▼ ├──→ 3DO / Loopy
GameCube ─────┘ profile_apply() ├──→ Neo Geo
(button remapping) ├──→ USB Device
└──→ UART
Every controller input — whether USB, Bluetooth, WiFi, or a native retro controller — is normalized into a common input_event_t structure, routed through the router, and translated into the output protocol.
Input Layer¶
Joypad OS accepts input from multiple sources:
USB HID — Standard USB gamepads, keyboards, and mice. Vendor-specific drivers handle controllers from Xbox, PlayStation, Nintendo, 8BitDo, Hori, and more. Generic HID parsing covers everything else.
USB XInput — Xbox 360/One/Series controllers using the XInput protocol.
Bluetooth — Wireless controllers via USB Bluetooth dongle (RP2040), built-in radio (Pico W), or BLE (ESP32-S3, nRF52840). Uses BTstack for Classic BT and BLE HID.
WiFi (JOCP) — The Joypad Open Controller Protocol allows controllers to connect over WiFi on Pico W boards. The adapter runs as a WiFi access point with UDP/TCP servers.
Native Controllers — Direct reading of SNES, N64, and GameCube controllers using PIO-based protocols. Useful for building retro-to-modern adapters (e.g., SNES→USB, N64→Dreamcast).
Router¶
The router connects inputs to outputs. Every app configures which routing mode to use:
SIMPLE — 1:1 mapping. Controller N goes to player slot N. Used by most console adapters.
MERGE — All inputs merge into a single output. Used for copilot/accessibility modes and BT2USB (multiple BT controllers → one USB gamepad).
BROADCAST — All inputs go to all outputs. Used for specialized multi-output setups.
The router manages player slots, handles connect/disconnect events, and forwards feedback (rumble, LEDs) from outputs back to inputs.
Output Layer¶
Outputs translate the common input format into console-specific or USB protocols:
Console Protocols — PCEngine, GameCube, Dreamcast, Nuon, 3DO, Neo Geo, and Casio Loopy. Each uses RP2040 PIO (Programmable I/O) state machines for cycle-accurate timing. Console output runs on Core 1 to avoid interference from USB/BT processing on Core 0.
USB Device — Emulates various USB gamepads: HID Gamepad, XInput (Xbox 360), PS3, PS4, Switch, and more. Supports real Xbox 360 console authentication (XSM3). Configurable via config.joypad.ai.
UART — Serial bridge for ESP32 Bluetooth modules (legacy).
Services¶
Built-in services provide cross-cutting features available to all apps:
Profiles — Button remapping. Each app can define multiple profiles (e.g., Default, SSBM, MKWii for GameCube). Users cycle profiles by holding SELECT + D-pad Up/Down for 2 seconds. Selection persists to flash.
Player Management — Tracks connected controllers, assigns player slots, and manages feedback routing (rumble, player LEDs, RGB).
Hotkeys — Detects button combos for special actions (e.g., L1+R1+Start+Select for Nuon in-game reset).
LED Feedback — NeoPixel RGB LED shows connection status and profile changes.
Storage — Flash persistence for settings, profiles, and Bluetooth bonds.
Platform Support¶
Joypad OS runs on three microcontroller platforms:
RP2040¶
The primary platform. Dual-core ARM Cortex-M0+ with hardware PIO for cycle-accurate console protocol timing.
- Core 0: USB host polling, Bluetooth, input processing, main loop
- Core 1: Console output protocol (timing-critical PIO programs)
- PIO: Each console protocol has dedicated PIO programs (e.g.,
joybus.piofor GameCube,maple.piofor Dreamcast) - All apps supported: Console adapters, USB output, BT/WiFi input, native controller reading
ESP32-S3¶
Runs bt2usb only (BLE to USB adapter). Uses FreeRTOS with separate tasks for BTstack (BLE) and USB device output.
- BLE only — no Classic Bluetooth. Modern controllers (Xbox BLE, 8BitDo BLE) work; DS4/Switch Pro require Pico W
- USB OTG — native USB device support (no PIO needed)
- TinyUF2 — drag-and-drop firmware updates
- See ESP32-S3 platform docs for setup
nRF52840¶
Runs bt2usb and usb2usb (with MAX3421E USB host chip). Uses Zephyr RTOS with BTstack running in its own thread.
- BLE only — same controller support as ESP32-S3
- USB host via MAX3421E — SPI-connected USB host IC for
usb2usbon Feather nRF52840 - UF2 bootloader — drag-and-drop firmware updates
- See nRF52840 platform docs for setup
Apps¶
An "app" is a build configuration that selects which inputs, outputs, and features to enable. Each app lives in src/apps/<name>/ and defines:
- Which input sources to enable (USB host, Bluetooth, native controller, WiFi)
- Which output interface to use (console protocol, USB device, UART)
- Router mode (SIMPLE, MERGE, BROADCAST)
- Button remapping profiles
- Hardware pin assignments
For example, usb2gc enables USB host + Bluetooth input, GameCube joybus output, SIMPLE routing, and 5 GameCube-specific button profiles. Meanwhile, bt2usb enables Bluetooth input, USB device output, and MERGE routing.
See the adapter docs for per-app details, or the layers & internals doc for the full developer architecture reference.