What's new

Worklog N64 USB/Bluetooth Controller Adapter

JacksonS

.
.
.
Joined
Feb 17, 2016
Messages
308
Likes
624
Location
Chicago, IL
Portables
6
Hi y'all,

I'm starting a new worklog here for a little project I'm doing for fun!

I've been playing my N64 lately and wanted to use the Nintendo 64 controller for Switch because I like the authentic-feeling joystick. I was able to use it on my N64 console wirelessly with a BlueRetro adapter: https://github.com/darthcloud/BlueRetro

But using this adapter, I get some input delay as well as frequent connection drops. Instead of trying to debug BlueRetro I wanted to make my own solution to learn how it all works. I decided to start with USB instead of Bluetooth, though they're really similar under the hood for Switch controllers (both HID).

To make this work, I'm using an STM32F401 microcontroller which acts as a USB host to get data from the Switch controller. Then I'll emulate an N64 controller on the same microcontroller and plug it into the console, which should be straightforward since both the console and microcontroller use a 3.3V logic level. Here's the first PCB design and a pic of the controller after I managed to talk to it over USB and light up the LEDs:

Top.PNG
Bottom.PNG
IMG_3258.jpg


Once it's all done I'll post schematics/PCB as well as code for others to use. Hopefully it'll be helpful to someone
 

Legend

.
Joined
May 10, 2019
Messages
130
Likes
248
Location
Canada Boi
Portables
2
This is sick! Excited to see the finished product!
 

JacksonS

.
.
.
Joined
Feb 17, 2016
Messages
308
Likes
624
Location
Chicago, IL
Portables
6
Thanks for the messages guys, I'm happy to be modding again :)

I got set up with a STM32F412G-Discovery module which is a really solid piece of hardware for ~$40 on DigiKey. It has lots of features but I'm only really using the GPIO and the USB Host port. It's similar enough to STM32F401 to use until I have the custom PCB.

IMG_3274.jpeg


I've spent a while trying to emulate the N64 controller a few different ways and wanted to document here what I've done. The N64 uses a single-wire bi-directional asynchronous communication; very much like the GameCube, but about 4 times slower, which makes emulation easier.

I looked at BlueRetro and found they use a module on the ESP32 called "RMT" which can basically read and write arbitrary waveforms, which is pretty cool. I couldn't find an equivalent on STM32, although looking at the GC+ 2.0 source code it looks like Aurelio's used a similarly cool peripheral on the PIC called the Signal Measurement Timer (SMT). So I guess I've chosen the worst microcontroller here with no good hardware emulation option *sad*

I tried to use the SPI module because the STM32F4 supports single-wire half-duplex SPI, which basically follows the same back and forth as the data on an N64 controller line. However I discovered the N64 doesn't send data at exactly the 1MHz baud rate I expected, but it's slightly slower. So my SPI transactions got more error prone with each bit and I couldn't make it work reliably.

So, I went with bit-banging using GPIO in C which luckily works just fine, with the caveat that it takes a lot of CPU overhead since it's all handled in software. Below I've included a bunch of captures from my logic analyzer to show how the communication works on both the OEM controller and my STM32 emulation.

IMG_0114.jpg

Here's the console sending one byte to the OEM controller. Each bit consists of a low period followed by a high period. For bit 0, it's 3 microseconds low and 1 microsecond high. For bit 1, it's 1 microsecond low and 3 microseconds high. After the console has sent all the bits it needs to (8 in this case), it follows with a stop bit which is 1 microsecond low and at least 2 microseconds high. After that, the controller is free to respond, and should do so quickly.

IMG_0120.jpg

Here's a full transaction between the console and OEM controller. The console sends byte 0x01, which is requesting 4 bytes of controller data (2 bytes buttons + 2 bytes analog). The controller then responds using the same bit format as described above. Lastly, the controller needs to send a stop bit consisting of 2 microseconds low and then releasing the data line high again.

The hard part has been reliably reading data sent from the console and responding quickly enough. Here's a breakdown of that:
IMG_0117.jpg

Here there's a second pin on the logic analyzer (Status Pin), which is just an output from the STM32 that shows its activity for debugging. The transaction starts when the console drives the data pin low. The STM32 is configured to detect this falling edge as an interrupt and immediately enter the routine where it reads, then responds. The STM32 reads the data pin 2uS after the console drives it low, then waits for the next falling edge which indicates the start of the next bit. This repeats until the expected number of bits are read.

Here's what the STM32 read and response looks like:
IMG_0118.jpg


And a more complicated example:
IMG_0119.jpg

The command sent here is 0x03 which is a write to the controller pak. The pak can be read from and written to in 32-byte chunks like a block of memory. On a real controller, the pak can a memory card or a rumble pak; either way, this is how it has to be addressed. This transaction is actually what it looks like when the console turns on rumble (yes, 32 bytes to indicate "rumble on"). Lastly, the controller must respond (immdediately) which a CRC which is calculated based on all the bits that were just sent. In my implementation, this CRC is updated on the fly after every single bit read to make sure that it can respond right away once all the bits are read.

So far this implementation works great on every game I've tested. Most games seem to sample controller data at the frame rate which is usually no more than 30Hz. This leaves more than enough CPU time to manage all the USB host stuff. But theoretically, there could be a game which samples controller data so often that the CPU has no time to do anything else, which would mean no USB :(. If anyone knows of a microcontroller that has both USB host functionality and a hardware block that can handle this kind of communication, I'd love to know lol
 

YveltalGriffin

First Wii U Trimmer
.
Joined
Jun 7, 2016
Messages
423
Likes
1,962
Location
South Florida
Portables
5
Hi Jackson! Cool project! :D

The RP2040 and RP2350 have a USB 1.1 controller (peripheral and host mode) as well as PIO blocks which can do the sort of arbitrary protocol stuff you're after. Tails86 has had great success using them to connect USB controllers to the Dreamcast.
 

JacksonS

.
.
.
Joined
Feb 17, 2016
Messages
308
Likes
624
Location
Chicago, IL
Portables
6
Hi Jackson! Cool project! :D

The RP2040 and RP2350 have a USB 1.1 controller (peripheral and host mode) as well as PIO blocks which can do the sort of arbitrary protocol stuff you're after. Tails86 has had great success using them to connect USB controllers to the Dreamcast.
Sweet, thanks! Can’t believe I missed that. Maybe will delve into RP2040 after I wrap up this software emulation approach.
 

JacksonS

.
.
.
Joined
Feb 17, 2016
Messages
308
Likes
624
Location
Chicago, IL
Portables
6
Ok, the RP2040 is a really neat chip. The PIO blocks are super powerful and fun to program in their little mini-assembly language. They're like miniature independent processors that are best at shuffling data in and out with precise timing. The biggest constraint is that each mini assembly program needs to be less than 32 instructions.
1740361409985.png


Got set up with a Raspberry Pi Pico W which luckily has Bluetooth integrated for the later part of this project. After some hair pulling we've got a command and response with the N64 through PIO:
1740361200740.png


There's some ringing on the line so I suspect I'll need to mess with external pullup resistors as I'm only using weak internal GPIO pullups right now. The N64 doesn't seem to notice.

Next I'm gonna try to move over my USB gamepad handling stuff from the STM32 to the RP2040.
 

JacksonS

.
.
.
Joined
Feb 17, 2016
Messages
308
Likes
624
Location
Chicago, IL
Portables
6
I ran into a big snag with this project - basically USB isn't viable because the Switch controller pulls too much current trying to charge its battery when it's plugged in. It seems the N64 3.3V controller supply can't handle this load. I've been testing with my custom STM32 PCB, and while it works with external power, it glitches and loses USB connection when it's powered from the N64. Big sad. There might be a USB command that disables charging while the controller is connected, but I don't think it's known, if it even exists.

My now useless STM32 board with a 3.3V to 5V boost regulator and a USB-C DRP (dual-role port) for programming and controller connection:
IMG_3335.jpgIMG_3336.jpgIMG_3337.jpg

So I'm moving onto just doing Bluetooth instead. It'll be functionally like BlueRetro, but with better performance. I'm still writing all the software myself to make sure it can hit the low latency and reliably connectivity I'm looking for. Today I got my Raspberry Pi Pico W working finally as a Bluetooth N64 adapter:

I think all the hair pulling on the software side was worth it cause it works really well... no latency, no connection drops, rumble works perfectly. Now I have to not be lazy and make a custom PCB instead of just playing with this breadboard version :|
 

cy

.
Joined
Sep 3, 2020
Messages
173
Likes
515
Portables
8
I'm a bit late to the welcoming party welcoming you back, but it's great to see you back! Your Wii SP consoles were always an inspiration to me! And I totally get the whole not wanting to design your own board thing, I personally don't like having to wait for the boards to arrive.

You've done some great work so far though so it'll totally be worth going through the PCB design process and ending up with a more polished result! Will you be open sourcing this project for the rest of the community? Just curious.
 

JacksonS

.
.
.
Joined
Feb 17, 2016
Messages
308
Likes
624
Location
Chicago, IL
Portables
6
I'm a bit late to the welcoming party welcoming you back, but it's great to see you back! Your Wii SP consoles were always an inspiration to me! And I totally get the whole not wanting to design your own board thing, I personally don't like having to wait for the boards to arrive.

You've done some great work so far though so it'll totally be worth going through the PCB design process and ending up with a more polished result! Will you be open sourcing this project for the rest of the community? Just curious.
That’s kind, thank you!

Yes, I’ll post the project on GitHub and link it here once I have that PCB made and tested. It could also be a standalone N64 controller emulator so I’m thinking of adding that as a software feature for anyone interested.
 

JacksonS

.
.
.
Joined
Feb 17, 2016
Messages
308
Likes
624
Location
Chicago, IL
Portables
6
Finished the next PCB design, now with Twice the USB-C™ B|

PCB_top.PNG


It seems extra but the second USB-C port at the top will let me power the board externally, allowing the bottom USB-C to work as a host port without having to boost the N64's 3.3V up to 5V. The bottom USB-C port will still work for programming the board as well.

This board includes both the RP2040 and the CYW43439, a Bluetooth+WiFi package. I thought it'd be cool to have an all in one board that can do Bluetooth or USB. External power shouldn't be needed when using Bluetooth since it draws less current than a controller trying to charge over USB.
 
Top