Skip to content

24g2usb

8BitDo SF30 2.4G wireless receiver to USB HID gamepad.

Overview

Drives an nRF24L01+ radio over SPI, impersonating the 8BitDo SF30 2.4G dongle closely enough that a SF30 2.4G controller pairs and links to it directly. The linked controller is presented to the host as a USB HID gamepad -- no 8BitDo dongle required. The receiver supports exactly one controller at a time; see "Single controller only" below for why.

Input

24G Input -- interrupt-driven nRF24L01+ receiver decoding the SF30 2.4G wire protocol (64-channel frequency hopping, 13-byte frames). See 24G Protocol for the full wire format.

Output

USB Device Output -- USB HID gamepad with multiple emulation modes.

Wiring

The nRF24L01+ module connects to spi0. CE and CSN are ordinary GPIOs driven by the driver, not SPI peripheral functions.

nRF24L01+ Pico / Pico 2 (W) Notes
VCC 3V3 Not 5V. Add a decoupling cap (10uF) across VCC/GND at the module -- these radios are notoriously sensitive to supply noise.
GND GND
CE GPIO 4 Plain GPIO
CSN GPIO 5 Plain GPIO (chip select, driven manually)
SCK GPIO 6 spi0 SCK
MOSI GPIO 7 spi0 TX
MISO GPIO 0 spi0 RX
IRQ GPIO 8 Required -- the receiver is interrupt-driven, not polled

GPIO 4 and GPIO 7 are not interchangeable: on RP2040/RP2350, GPIO 7 is the fixed spi0 TX function and GPIO 4 is spi0 RX, so MOSI must be on 7 and CE on 4.

The same GPIO assignments apply to the Waveshare RP2350-Zero -- all six radio signals (MISO, CE, CSN, SCK, MOSI, IRQ) sit on its right header column, with 3V3/GND on the left. See 24g2usb-rp2350zero carrier PCB build guide.

Core Configuration

Setting Value
Routing mode SIMPLE (1:1)
Player slots 1 (fixed)
SCK pin GPIO 6 (spi0)
MOSI pin GPIO 7 (spi0)
MISO pin GPIO 0 (spi0)
CSN pin GPIO 5
CE pin GPIO 4
IRQ pin GPIO 8

Pins are clear of GP23/24/25/29, which the CYW43 module claims on _w boards.

Key Features

  • No dongle required -- a SF30 2.4G controller pairs and links directly, cold-acquiring in under a second.
  • Interrupt-driven radio -- the receiver runs off the nRF24's IRQ line and a hardware alarm, not the main polling loop, so flash writes and other core-0 work can't stall a dwell and drop a packet.
  • Pairing gesture -- hold the BOOTSEL button ~1.5s to begin pairing. See 24G Input for the full flow.
  • LED indicator -- solid while a controller is linked (leds_set_connected_devices()), fast ~100ms blink while a pairing rendezvous is in progress (leds_set_pairing()), slow ~500ms blink otherwise (idle, waiting for a controller).
  • USB output modes -- SInput, XInput, PS3, PS4, Switch, Keyboard/Mouse (BOOTSEL double-click to cycle, triple-click to reset to HID).

Single controller only

The receiver supports exactly one paired controller, matched by MAX_PLAYER_SLOTS 1 and USB_OUTPUT_PORTS 1. USB output only ever surfaces player index 0 (sinput_mode.c, hid_mode.c and xinput_mode.c all (void)player_index and write to the single ITF_NUM_HID_GAMEPAD interface -- only gc_adapter_mode.c routes player_index to distinct USB interfaces, same limitation bt2usb carries), and two controllers hopping the same 64-channel table at independent phase could starve each other's dwell indefinitely if they powered on close in phase. Rather than ship a scheduler for a case USB couldn't deliver anyway, this app tracks a single controller. Pairing re-offers the same address and replaces whichever controller was previously paired to it.

Supported Boards

Board Build Command
Pico 2 W make 24g2usb_pico2_w
Pico W make 24g2usb_pico_w
Pico make 24g2usb_pico
Pico 2 make 24g2usb_pico2
Waveshare RP2350-Zero make 24g2usb_rp2350zero

No CYW43 or BTstack needed -- these boards are used only for their onboard LED / form factor.

Build and Flash

make 24g2usb_pico2_w
make flash-24g2usb_pico2_w