2024 C RP2040 PIO TinyUSB CMake Raspberry Pi Pico

Pico N64

USB adapter for N64 controllers on Raspberry Pi Pico — because old controllers deserve better than a drawer.

View on GitHub
Pico N64

Forgotten controllers

I had original N64 controllers that had been gathering dust for years.

The Nintendo 64 dates from 1996, and its controllers use a proprietary connector that nothing modern recognizes natively. Result: unusable on a current setup, condemned to stay in a drawer.

The idea was simple: make them usable again on a PC, without friction. A small USB plug-and-play adapter that turns the controller into a standard gamepad, no driver, no software.

The Raspberry Pi Pico checks all the boxes: €5, ultra simple, and more than powerful enough for this kind of bridge between two worlds.

A protocol that fits on a wire

N64 controllers use a surprisingly minimal proprietary protocol: a single data line, open-drain, with extremely strict timing.

The principle is brutal: the Pico sends a request, and the controller replies with a 32-bit word representing the button state.

But everything is encoded in time. A bit isn't an electrical value, it's a duration. A few microseconds off and the communication breaks.

On a conventional CPU, this kind of fine timing is fragile. An interrupt, a cache miss, and everything can go wrong.

PIO: delegating timing to programmable hardware

The RP2040 in the Raspberry Pi Pico has a rare feature for this price range: PIO (Programmable I/O).

These are small coprocessors specialized in I/O, capable of executing ultra-simple instructions independently of the main CPU.

For the N64 protocol, I moved all the critical logic into a PIO program written in assembly. It generates and reads signals with perfectly deterministic timing, cycle by cycle.

The CPU no longer intervenes in the sensitive part. It triggers the communication, then simply retrieves the result.

Each controller runs on a dedicated state machine. Two controllers = two PIO machines running in parallel, with no extra CPU effort.

USB HID: speaking the universal language

On the USB side, I use TinyUSB, an open-source USB stack designed for microcontrollers.

The Pico presents itself as a HID device (Human Interface Device), the same standard as keyboards, mice and gamepads.

This matters because HID is natively supported by all systems: Windows, Linux, macOS. No driver, no configuration.

N64 inputs are mapped directly onto a standard USB gamepad: buttons, D-Pad, analog stick converted to X/Y axes.

A small test tool via the Web Gamepad API lets you verify the mapping directly in a browser.

Simple hardware, real use

The system supports up to two simultaneous N64 controllers, each exposed as a distinct USB gamepad.

The hardware is deliberately minimal: a Raspberry Pi Pico, controller connectors, and a few pull-up resistors. Nothing else.

No complex regulation, no sophisticated PCB. The protocol and the Pico do the work.

A built-in LED gives a simple status: idle, one controller connected, or two controllers active.

In practice

Once plugged in, it works like a standard USB controller.

Latency is around 8ms (125Hz USB polling), well within the standards of modern controllers.

The flaws come mostly from the controllers themselves: worn sticks, analog drift, mechanical fatigue accumulated over 25 years.

The firmware, on the other hand, is stable and deterministic.

The project fits in under 1000 lines of C and a few dozen lines of PIO assembly. Nothing spectacular in volume — all the work is in deeply understanding the timing and protocol.

realitynauts@bugquest:~
×
Realitynauts
RN

$ cat auteur.txt

Realitynauts — Dev PHP / Python / Hardware
Autistic. Intensely focused. Always in prod.

$ cat opportunites.txt

Does this project resonate? Got a similar
or complementary project? Let's talk.

$ ./contact.sh