What's new

Worklog [2024 Contest Entry] WavePhoenix - Open-source implementation of the Nintendo WaveBird protocol

loopj

.
2024 2nd Place
Joined
Feb 24, 2020
Messages
90
Likes
400
Location
Bay Area, CA
WavePhoenix
1723824010418.png

Was keeping this under wraps until I was pretty confident it was doable, but wanted to share what I've been working on for the past month or so...

WavePhoenix is an open-source implementation of the Nintendo WaveBird protocol using off-the-shelf Silicon Labs Wireless Gecko SoCs. I plan on sharing the firmware source and reference hardware designs as soon as I'm confident the performance is close to, or exceeds, the OEM WaveBird receivers.

History

The WaveBird controller is, in my opinion, the best controller ever made. It is wireless, has a insane battery life, and is very comfortable to hold. Unfortunately, Nintendo stopped production of the WaveBird more than a decade ago, causing an dwindling supply of controllers, and especially the receivers.

Given the decreasing supply of receivers, and the increasing resale prices, I decided to see if I could design my own from scratch.

Screenshot 2024-08-16 at 9.11.36 AM.png

I was already familiar with the wired GameCube controller protocol, but I had never worked with the wireless side before. In mid 2020 I began researching the WaveBird protocol, and came across Sam Edwards' incredibly well written WaveBird reverse-engineering documentation. Sam's docs aren't quite perfect, but were pretty damn essential in getting this working, thanks Sam!

The most challenging part of this project was finding a wireless SoC that supported the modulation used by the WaveBird. What Sam describes in the line coding and framing section of his document is actually FSK-DSSS (Frequency Shift Keying with Direct Sequence Spread Spectrum), one which uses 15 chips per bit.

DSSS is a modulation technique that spreads the signal over a wide frequency band, making it more robust to interference and noise, here's a great breakdown by GeekyMuch.

While it would certainly be possible to implement 15-chip DSSS demodulation in a software defined radio, I wanted to find a SoC that could do this in hardware. After reading more datasheets than I care to admit, I discovered the SiLabs EFR32FG1 family of SoCs, the datasheet contains the magic incatation:

1723824604258.png


Chapter One: Receiving a Packet

I spent quite a while theory-crafting on this project, but finding a compatible SoC was the essential unblocking step. I bought a devkit for a Silicon Labs EFR32FG1 SoC, and started playing around with radio settings in the (godawful) Silicon Labs IDE. As I mentioned above, Sam's WaveBird reverse engineering documentation isn't /quite/ right on a few things. Most importantly the DSSS chip length I don't believe is accurate. After setting the DSSS chip length to 15, I got my first packet!

1723824817003.png


Chapter Two: Decoding a Packet

A bunch of hex isn't that helpful, so we need to turn that into actual controller input data. As a proof of concept, I wrote some C code to decode these packets on my laptop. Decoding these packets requires shuffling bits around, and decoding BCH(31,21) codewords, which are used for forward error correction:

1723825030456.png

Now we're talking!

Once I had the basic packet decoding, I then had to implement the CRC checking, which was also fairly straightforward. Surprisingly the full packet decoding logic fits in under 100 lines of C.

Chapter Three: Decoding Packets on the SoC

Decoding on my laptop helped me confirm the algorithms were solid, but is useless for an actual WaveBird receiver. Next step was to get the packets decoding in on the actual SoC. The WaveBird sends 480 packets per second, and we need to decode them in real time.

Turns out I was able to use my proof of concept C code almost exactly as-is on the SoC itself. Did I tell you I love C?



Chapter Four: Communicating with a GameCube

Now we have real-time packet decoding, the next step is do send/receive commands on the SI bus. The SI bus is how GameCube/Wii consoles communicate with connected controllers. @jefflongo has an insanely detailed and clear writeup about the GameCube controller protocol which you should definitely check out!

This part of the project was a big learning curve for me, since you have to communicate hella fast as we say here in northern California. Bits are clocked in and out at speeds between 250kHz (OEM controllers) and 200kHz (consoles). It is essential to do this without tying up the CPU, so I had to take full advantage of the peripherals on the EFR32 SoCs.

Receiving commands:
  • First we listen for SI commands by configuring a TIMER peripheral in capture/compare mode, and attaching it to the SI GPIO
  • Then we connect this to an LDMA peripheral, and fill the DMA buffer with 16 "edges" at a time
  • Once we have 16 edges, an interrupt fires, and we measure the time between edges to determine how long each "low" pulse was
  • Based on the length of a low pulse, we store either a 0 or a 1 in our incoming byte buffer
  • Repeat until we have a complete SI command
Responding to commands:
  • Again, we'll use a TIMER peripheral, but this time in PWM mode
  • Based on the byte data we want to send, we'll fill a buffer with pulse widths which correspond to the correct SI pulses
  • We then activate another LDMA channel to move to the next pulse width in the buffer, after every clock cycle
Once you put those two together, you have fast SI communication! Clocking in commands only "wakes up" the CPU after every complete byte is received, and clocking out responses doesn't wake up the CPU at all!

1723826704952.png

Putting this all together, we finally get:



Chapter Five: A new hope cheaper SoC

So the EFR32F1 SoC is great, but it is fairly old, and not the cheapest SoC out there. I really wanted to be able to run this on the Silicon Labs Gecko Series 2 SoCs, since they are cheaper and have really nice modules available which have onboard crystals and chip antennae.

Almost all of my code ported across as-is, but the radio on these devices is slightly different - different enough that I'm still working on tuning it as we speak.

One of the cool things about the Series 2 SoCs is they have a new compact (and reasonably priced) dev board - the EFR32xG22E Explorer Kit. Abusing the onboard qwiic connector gives us something that looks familiar:
1723824922556.png


The Road to Release
In order for this to be initially releasable, I still have some work to do...
  • Tune the radio settings to ensure little-to-no packets get dropped​
  • Move to using proper BCH(31,21) decoding to get the benefits of actual error correction​
  • Build a reference hardware design with the MGM210P module (and/or raw SoC+xtal+antenna)​
For the summer contest, however, I have another deliverable in mind...

I'd ideally love to have a version of GC+ which can (optionally) work as a WaveBird receiver. Since I've done the work of implementing the SI bus communications already on this SoC, and this module has (just!) enough GPIOs for all the digital and analog inputs, in theory I should be able to make a footprint compatible board which adds WaveBird support to portables.

Here's a photo of the SiliconLabs Gecko SoC module on top of a GC+ for scale.
IMG_2749.jpg


Epilogue: The Far Future

This SoC not only supports proprietary 2.4GHz protocols, but also has a full Bluetooth stack on it! In theory it should be possible to have this not only act as a WaveBird receiver, but also as a Bluetooth HID receiver too!

I'd also really like to make a USB-HID wavebird dongle, so you can play Dolphin with a real WaveBird without any additional adapters or cables.

I'd love to make both of these a reality down the line.

Thanks for sticking around for the long read! Keep your fingers crossed I can get the radio tuning nailed down.
 

Attachments

Last edited:
Joined
Jul 31, 2022
Messages
69
Likes
93
Location
Oregon
I always love reading loopjs posts they're always super easy to follow and fun to read! Great work its great everything you've been able to do so far!
 
Joined
Jul 27, 2017
Messages
12
Likes
25
I don't know if this helps at all, but awhile back I worked out a way to handle the nearly identical N64 line protocol by abusing a UART module to send/receive 2 bits at a time, and it seems a bit more efficient (though sliiiightly mis-timed on the Tx) than decoding a single bit at a time the way you're doing. If you're able to keep up as is, probably no need, but might be worth trying if you need a bit more breathing room on the timing.

 

loopj

.
2024 2nd Place
Joined
Feb 24, 2020
Messages
90
Likes
400
Location
Bay Area, CA
Thanks @qwertymodo!

So far the SI side has been fairly robust using the timers/dma/batch processing approach. For RX, since I'm processing the pulse timings a byte at a time, it gives me plenty of cycles before the next byte comes in (so far!).

Using the hardware timers/dma approach for TX is particularly clean, the timings are bang on spec:
Screenshot 2024-08-17 at 10.54.25 PM.png
 
Last edited:

loopj

.
2024 2nd Place
Joined
Feb 24, 2020
Messages
90
Likes
400
Location
Bay Area, CA
Quick performance tuning update - I've managed to get the packets per second to an acceptable rate on the Series 2 SoCs! There is still a lot of tuning I want to do, but as of right now, I'm really close to the OEM performance in terms of throughput.

The WaveBird sends 480 packets per second
It turns out I was wrong about this, the packet rate is actually 250 packets per second, which is a ~2x oversampling of the maximum SI polling rate (120 polls/second). My misunderstanding came from some bad math, each packet transmission has around ~2ms of silence after sending the data which I hadn't factored into my calculations.

This is actually somewhat good news for me, because I'm much closer to the OEM throughput than I thought I was!
 
Joined
Oct 13, 2024
Messages
2
Likes
3
Location
Australia
Signed up to BitBuilt just for updates on this project!

Sorry, just checked the rules for the summer build contest. Seems you have to do everything. I don't want to get you disqualified, but I definitely do want to put my own together at some point haha. Good luck for the comp!

If there is anything that another pair of hands could help with, let me know. I'm a mechatronics engineer, currently working in software development. Done plenty of CAD, 3D printing, PCB design/layout/assembly, and a bit of embedded development and mucking about.

I haven't done much with RF, but willing to learn.


20241013_120127.jpg
 
Last edited:

loopj

.
2024 2nd Place
Joined
Feb 24, 2020
Messages
90
Likes
400
Location
Bay Area, CA
Update time! Lots of progress since my last post.

Performance
The performance is now pretty solid. The packet rate and range is comparable to the OEM WaveBird receiver. I still think there is a lot of room for improvement.

Firmware Updating
We now have support for firmware updating! The should make it nice and easy to send out updates without people having to disassemble their receivers.

Adding support for firmware updates meant I had to write a bootloader, but the wireless chip's SDKs actually do a lot of this work for us.

The main use-case for firmware updates is so I can release radio tuning improvements (better range, packet rate, etc) over time, but since the wireless modules support the Bluetooth LE stack I could potentially add support for Bluetooth LE controllers in future.

There are two ways to update the firmware:
  • Using a homebrew app - the firmware is downloaded over SI (via the gamecube port) from the WavePhoenix GC/Wii homebrew
  • Over-the-air (Bluetooth) - the firmware is sent from an app on your phone (and maybe also from your browser using the Web Bluetooth API, TBD)
Mini Receiver
V1 of the custom receiver PCB arrived! You may notice a little magnet wire on the board - v1.0 had a routing bug, which I've fixed in v1.1.

I'm using the EFR32BG22 in all my designs now, since it is cheap, ubiquitous, and has a bunch of off-the-shelf wireless modules with built in antennae, crystals, and wireless matching networks. My PCB is a *really* simple 2-layer board, it is more just a rigid carrier board for all the components.

Screenshot 2024-10-17 at 10.53.03 AM.png


I also have a near-final model for the mini receiver shell:
Screenshot 2024-10-17 at 10.55.57 AM.png


Here's what they look like full assembled in a GameCube:
1729187830667.png


WavePhoenix+
I've designed and ordered the wireless "GC+" board, that I'm calling WavePhoenix+ (how original!).
Screenshot 2024-10-17 at 11.15.10 AM.png
Screenshot 2024-10-17 at 11.15.20 AM.png


Features:
  • Footprint compatible with GC+ 2
  • GPIO expander IC, since the wireless module is limited in GPIOs
  • Rumble motor driver
  • Optional external antenna support
  • Pad to enable/disable wireless support, eg. to enable wireless only when docked
Once the board is in-hand I still have to write the firmware, most of it will be identical to the mini receiver, but I have to add support for grabbing the analog/digital inputs and making those available via SI.

For launch, I won't be implementing the button remapping or calibration features that GC+ supports, but will add these later if there's any interest.

If there's a non-bodgeable bug with the board, that'll mean I won't have WavePhoenix+ ready in time for the contest deadline, so fingers crossed!
 

k44du2

.
Joined
Nov 7, 2024
Messages
1
Likes
3
Location
Germany
I created an account just to thank you for this.
I just ordered a Wavebird controller hoping somebody would complete an open souce project soon. You just made my day. I cannot wait for your github repo.
 
Last edited:
Top