Skip to content

Commit 37084d2

Browse files
committed
Update README
1 parent 14b788e commit 37084d2

3 files changed

Lines changed: 127 additions & 134 deletions

File tree

README.md

Lines changed: 31 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
</h1>
44

55
<p align="center">
6-
An open-source implementation of the Nintendo WaveBird protocol using Silicon Labs Wireless Gecko SoCs
6+
Open-source implementation of the Nintendo WaveBird receiver using Silicon Labs Wireless Gecko SoCs
77
</p>
88
<p align="center">
99
<a href="https://discord.gg/W44D5uUq4v"><img src="https://img.shields.io/discord/1377009241654689932?style=for-the-badge&logo=discord&logoColor=%23FFFFFF&label=DISCORD&labelColor=%235865F2&color=%234954c9" alt="Discord"></a>
@@ -17,141 +17,48 @@
1717

1818
The WaveBird controller is, in my opinion, one of the best controllers ever made. It is wireless, has an insane battery life, and is very comfortable to hold. The WaveBird set a new standard for wireless controllers. It was the first major console controller to use radio frequency (RF) technology, providing a reliable, lag-free connection that didn't require line-of-sight, unlike infrared controllers.
1919

20-
Unfortunately, Nintendo stopped producing the WaveBird more than a decade ago, causing a dwindling supply of controllers and, especially, receivers. Given the decreasing supply of receivers and the increasing resale prices, I decided to see if I could design my own from scratch.
20+
Unfortunately, Nintendo stopped producing the WaveBird more than a decade ago, causing a dwindling supply of controllers and, especially, receivers. Given the decreasing supply of receivers and the increasing resale prices, I decided to design my own from scratch.
2121

22-
## Firmware
22+
## Features
2323

24-
The firmware for WavePhoenix is split into several components:
24+
The reference implementation of WavePhoenix includes the following features:
2525

26-
- [`libwavebird`](firmware/libwavebird) - implementation of the WaveBird protocol
27-
- [`libsi`](firmware/libsi/) - implementation of the SI protocol, used by controllers to communicate with GameCube/Wii consoles
28-
- [`receiver`](firmware/receiver) - reference implementation of firmware for a WaveBird receiver
29-
- [`bootloader`](bootloader) - bootloader for updating application firmware via Bluetooth
30-
31-
## Hardware
32-
33-
The hardware for a reference WavePhoenix receiver is available in the [`hardware/mini-receiver`](hardware/mini-receiver) directory. The receiver is designed to be as cheap as possible and easy to build.
34-
35-
<img src="hardware/mini-receiver/images/mini-receiver.png" alt="WavePhoenix Mini Receiver" width="49%"/> <img src="hardware/mini-receiver/images/mini-receiver-pcb.png" alt="WavePhoenix Mini Receiver" width="49%"/>
36-
37-
The hardware folder contains a simple PCB which hosts a [RF-BM-BG22C3](https://www.rfstariot.com/rf-bm-bg22c3-efr32bg22-bluetooth-module_p93.html) module, a pairing button, a status LED, and a JST connector for connecting to a GameCube controller port, as well as a 3D printable case.
38-
39-
- [`case`](hardware/mini-receiver/case) - 3D printable case for the receiver
40-
- [`gerbers`](hardware/mini-receiver/gerbers) - Gerber files for ordering the PCB
41-
- [`KiCad`](hardware/mini-receiver/KiCad) - KiCad project files for the PCB
42-
43-
Check out the [`hardware/mini-receiver/README.md`](hardware/mini-receiver/README.md) for more information on how to build your own receiver.
44-
45-
## Protocol
46-
47-
I have documented the key parts of WaveBird protocol in the headers of [`libwavebird`](firmware/libwavebird):
48-
49-
- [radio.h](firmware/libwavebird/include/wavebird/radio.h) - describes the radio timings and modulation
50-
- [packet.h](firmware/libwavebird/include/wavebird/packet.h) - describes the packet format, encoding, and crc
51-
- [message.h](firmware/libwavebird/include/wavebird/message.h) - describes the structure of the decoded packets, aka the *input state* and *origin* messages
52-
53-
Sam Edwards' excellent [WaveBird reversing](https://github.com/CFSworks/wavebird-reversing) documentation contain the most detailed information about the protocol, but I have tried to fill in the gaps where possible.
54-
55-
## Development
56-
57-
### Finding an SoC
58-
59-
In mid-2020 I began researching the WaveBird protocol and came across Sam Edwards' incredibly well-written [WaveBird reverse-engineering](https://github.com/CFSworks/wavebird-reversing) documentation. Sam's docs aren't quite perfect, but they were **essential** in getting this working, thanks, Sam!
60-
61-
The most challenging part of this project was finding a wireless SoC that supported the specific modulation used by the WaveBird. What Sam describes in the [line coding and framing](https://github.com/CFSworks/wavebird-reversing/tree/master/05_line_coding_and_framing#a-closer-look-at-the-wavebirds-line-code) section of his document is actually a form of DSSS (Direct Sequence Spread Spectrum), one which uses *15 chips per bit*.
62-
63-
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](https://medium.com/networks-security/dsss-direct-sequence-spread-spectrum-a31005f281cc).
64-
65-
While it would certainly be possible to implement 15-chip DSSS demodulation using a software-defined radio, I wanted to find an SoC that could do this in hardware *in real-time*. After reading more datasheets than I care to admit, I discovered the [Silicon Labs Wireless Gecko EFR32FG1](https://www.silabs.com/wireless/proprietary/efr32fg1-series-1-sub-ghz-2-4-ghz-socs) family of SoCs, the [datasheet](https://www.silabs.com/documents/public/data-sheets/efr32fg1-datasheet.pdf) contains the magic incantation:
66-
67-
![15 chip DSSS](images/efr32fg1-datasheet-dsss.png)
68-
69-
Once I saw this, I had enough confidence to order a development kit and start working on the firmware.
70-
71-
> [!NOTE]
72-
> Did you know that the codename for the GameCube CPU was *Gekko*? How fitting that we are using a *Gecko* SoC!
73-
74-
### Receiving and decoding packets
75-
76-
As I mentioned above, Sam's WaveBird reverse engineering documentation isn't *quite* right on a few things. Most importantly, I don't believe the DSSS chip length is accurate. After setting the DSSS chip length to 15 and configuring the many other radio settings, I got my first packet!
77-
78-
![First packet](images/first-packets.png)
26+
- Full compatibility with the original WaveBird controller
27+
- One-button virtual pairing, like modern wireless devices
28+
- Status LED to indicate pairing status and radio activity
29+
- Over-the-air firmware updates via Bluetooth
30+
- Open source hardware and firmware
31+
- 3D printable case
7932

80-
Decoding the packets was now simply a matter of writing some C code to de-interleave the packet, decode the [BCH(31,21)](https://en.wikipedia.org/wiki/BCH_code) messages, and perform the CRC check. After doing this, we finally had a decoded *input state* message containing the state of all the buttons and analog inputs on the controller!
33+
## Buying a WavePhoenix
8134

82-
![Packet decoding](images/packet-decoding.png)
35+
Don't want to build your own? A number of vendors are now selling pre-assembled WavePhoenix receivers!
8336

84-
But wait. Every now and then, I was getting a "corrupted" packet that somehow was still passing the CRC check. What could this be? It turns out that in addition to broadcasting *input state* messages 250 times per second, the WaveBird controller also broadcasts *origin* messages. These are sent as soon as the controller is powered on and then repeated every second after that. The origin message contains the state of the analog sticks when the console was powered on, which we'll need later.
37+
- [Laser Bear Industries](https://www.laserbear.net/products/wavephoenix-replacement-wavebird-gamecube-receiver) - US-based seller, custom enclosure and PCB by Greg
38+
- [Colester Productions](https://shop.colesterproductions.com/product/wave-phoenix) - US-based seller, uses my original PCB and [
39+
SadSnifit's fantastic case](https://makerworld.com/en/models/1463984-wavephoenix-wavebird-shell)
40+
- [Various AliExpress sellers](https://www.aliexpress.us/w/wholesale-%22wavephoenix%22.html)
8541

86-
### Communicating with a GameCube
42+
## Building a WavePhoenix
8743

88-
Now we have real-time packet decoding; the next step is to send/receive commands on the *SI bus*. The SI bus is how GameCube/Wii consoles communicate with connected controllers. Jeff Longo has an insanely detailed and precise writeup about the [GameCube controller protocol](https://jefflongo.dev/posts/gc-controller-reverse-engineering-part-1/), which you should check out!
44+
The bare minimum hardware needed to build a WavePhoenix is a EFR32BG22 radio module (such as the [RF-BM-BG22C3](https://www.rfstariot.com/rf-bm-bg22c3-efr32bg22-bluetooth-module_p93.html)), a GameCube controller connector, and 3 wires!
8945

90-
Bits are clocked in and out at speeds between 250kHz (wired OEM controllers) and 200kHz (consoles). To do this without tying up the CPU, I had to take full advantage of the peripherals on the EFR32 SoCs.
46+
Since most people prefer a more streamlined solution, I designed a custom PCB and 3D-printable case called the *Mini Receiver*.
9147

92-
> [!NOTE]
93-
> An OEM WaveBird receiver clocks data out at 225kHz, slightly slower than wired controllers!
48+
You can find detailed build instructions in the [hardware/mini-receiver](hardware/mini-receiver) directory.
9449

95-
#### Listen for incoming commands
50+
## Firmware Components
9651

97-
The first step is to listen for the various SI commands from the console.
52+
The firmware for WavePhoenix is composed of the following components:
9853

99-
To read these commands, we do the following:
100-
101-
- Configure a `TIMER` peripheral in capture/compare mode and attach it to the SI GPIO
102-
- Connect the `TIMER` to an `LDMA` peripheral, filling the DMA buffer with 16 "edge timings" at a time
103-
- Listen for a "DMA full" interrupt
104-
- Determine the length of each "low" pulse by measuring the time between pairs of edges
105-
- Based on the length of a low pulse, we store either a `0` or a `1` in our incoming byte buffer
106-
- The first byte identifies the SI command type; this tells us how many bits to expect in the rest of the SI transmission
107-
- Repeat until we have a complete SI command
108-
109-
#### Replying to commands
110-
111-
Once we have a complete SI command, we need to respond (quickly!). We do this as follows:
112-
113-
- Use a `USART` peripheral to clock out pulses with precise timing without tying up the CPU
114-
- Split each outgoing bit into 4 chips, so `0` becomes `0001`, `1` becomes `1110`, and stop becomes `0011`
115-
- Configure the `USART` to output onto the SI GPIO, and set the baud rate to 1MHz (4 chips per bit @ 250kHz bitrate)
116-
- Feed the chips into the `USART` transmit buffer, and wait for the "TX complete" interrupt to be triggered
117-
118-
#### Command types
119-
120-
The most important command is the *get input state* command, which the console requests every frame. Since we should already have the latest input state packet from the WaveBird controller, we can simply send it back to the console after converting it to the SI format.
121-
122-
There are a bunch of other commands that we need to respond to, such as the *info* and *get origin* commands, but I'll leave that as an exercise for the reader to discover in the code.
123-
124-
### Wireless ID pinning
125-
126-
Since the WaveBird has no true concept of pairing, it is possible for multiple WaveBird controllers to be on the same channel at the same time. This is a problem, since the receiver will receive packets from all controllers on this channel. The WaveBird uses a simple "ID pinning" mechanism to solve this problem.
127-
128-
An OEM WaveBird receiver will look at the 10-bit controller ID present in all packets, and "fix" the ID to the first controller it receives a packet from.
129-
130-
### Channel selection
131-
132-
As well as supporting a channel wheel just like the original WaveBird receiver, I also wanted to add one-button "virtual pairing" support. This allows you to press a button on the receiver to enter pairing mode, and then hold a combination of buttons on a controller to "pair" it with the receiver. Under the hood, this scans through all 16 potential channels looking for wireless packets, and then checks if the appropriate buttons are being held on that channel.
133-
134-
### A more modern SoC
135-
136-
Although it doesn't explicitly call this out in the datasheet, I discovered that the EFR32xG22 series of SoCs *also* support 15-chip DSSS. This is great news since they are cheaper and more modern than the EFR32xG1 series. The EFR32BG22 SoC in particular has many pre-built modules which host the SoC, crystal, antenna, and RF matching network - such as the tiny [RF-BM-BG22C3 from RF-Star](https://www.rfstariot.com/rf-bm-bg22c3-efr32bg22-bluetooth-module_p93.html)
137-
138-
### Tuning, tuning, tuning
139-
140-
The hardest part of this project *by far* was tuning the radio settings.
141-
142-
The original WaveBird receiver has excellent range and wireless sensitivity. It is able to receive the full 250 packets per second at a distance of 10m+, so my goal was always to match this performance.
143-
144-
Dropping an occasional packet here and there is not a huge problem. Most GameCube games run at 60 frames per second so we have a little bit of leeway for the occasional dropped packet. However, if we drop too many packets this will result in missed inputs.
145-
146-
My initial radio configuration only managed to receive around 60 packets per second, so I spent a *lot* of time tuning the settings to get better performance. I'm now measuring 230+ packets per second at distances of around 5m, which is pretty close, and I think finally good enough to release.
147-
148-
I still think there is *a ton* of room for improvement here, pull requests would be very welcome!
54+
- [`libwavebird`](https://github.com/loopj/libwavebird) - my implementation of Nintendo's WaveBird protocol for Silicon Labs Gecko SoCs
55+
- [`libjoybus`](https://github.com/loopj/libjoybus) - my implementation of the Joybus protocol used by N64 and GameCube controllers
56+
- [`firmware`](firmware) - reference implementation of firmware for a WaveBird receiver
57+
- [`bootloader`](bootloader) - bootloader for updating application firmware via Bluetooth
14958

150-
## Future ideas
59+
## Worklog
15160

152-
- **Transmitter firmware** - `libwavebird` includes packet encoding functions, so with a little work you could "build your own" WaveBird controller!
153-
- **N64 WaveBird receiver** - the N64 uses the same SI protocol as the GameCube, with just a slightly different input polling command, so designing a WaveBird receiver for the N64 is within reach!
154-
- **USB HID dongle** - instead of sending input data over the SI bus, we could instead incorporate something like a [CH9329](https://www.wch-ic.com/products/CH9329.html) and send the input data over USB HID. This would allow you to use a WaveBird controller on any device that supports USB HID, such as a PC running Dolphin or RetroArch!
61+
Check out the [worklog](WORKLOG.md) for a detailed history of the development of the WavePhoenix project.
15562

15663
## Special Thanks
15764

@@ -164,6 +71,8 @@ I still think there is *a ton* of room for improvement here, pull requests would
16471

16572
## License
16673

167-
The firmware in this repository is licensed under the [MIT License](firmware/LICENSE).
74+
WavePhoenix is a permissively licensed open source project.
75+
76+
The firmware is licensed under the [MIT License](firmware/LICENSE).
16877

16978
The hardware is licensed under the [Solderpad Hardware License v2.1](hardware/LICENSE).

0 commit comments

Comments
 (0)