Cached at:
08/03/26, 01:37 AM
TL;DR: A Raspberry Pi can be turned into a Wii U gamepad clone running Vanilla, but Wi-Fi chip limitations, decoder latency, and performance issues make it impractical—while an Orange Pi Zero 2W surprisingly works much better.
## Recap: Vanilla and the Wi-Fi Problem
The Wii U gamepad was basically a tiny computer. It connected to the console over Wi-Fi, used the same video codec as Blu-Rays and YouTube, and in theory should have been easy to reproduce. The one big obstacle was Nintendo’s obfuscation of the Wi-Fi protocol.
Previous work produced Vanilla, a reimplementation of the Wii U gamepad based on a ten-year-old proof-of-concept called drc-sim, developed by memahaxx and later rolandoislas. Vanilla primarily ran on Linux because Linux gives enough low-level control to perform Nintendo’s obfuscated Wi-Fi handshake. But desktop Linux isn’t a great gamepad substitute, so the real goal was porting Vanilla to hardware that could actually look and behave like a handheld controller.
That meant confronting a messy hardware compatibility problem.
## Not All Wi-Fi Adapters Are Equal
Early testing showed that many Wi-Fi adapters worked, but some inexplicably didn’t. drc-sim only recommended one chipset: the Ralink RT2800, famous for being one of the most Linux-friendly Wi-Fi chipsets around. Vanilla aimed to support more.
As user reports came in, two patterns emerged:
- Realtek chipsets generally didn’t work.
- Broadcom chipsets generally didn’t work.
That was annoying because Realtek and Broadcom are two of the most common Wi-Fi vendors in the world. Realtek dominates USB Wi-Fi adapters; Broadcom dominates portable devices—phones, tablets, laptops, Raspberry Pis, even the Nintendo Switch.
### Realtek: A Driver Problem
Realtek adapters are unusual on Linux because they require installing a driver. Most Linux hardware is supported directly by the kernel, but Realtek’s most popular chipset was not. Worse, the driver wasn’t some random community project—it was made by Realtek themselves, and still never made it into the kernel.
Testing Vanilla with a Realtek adapter failed early with a cryptic `CTRL-EVENT-ASSOC-REJECT` error when trying to sync with the Wii U.
Digging through the driver source revealed many forks, including one from rolandoislas, one of drc-sim’s maintainers, apparently attempting to get it working with the Wii U. It only compiled on Linux versions over ten years old and wasn’t compatible with any modern adapters. Porting its patches to a newer driver didn’t help.
Then came the key discovery: one Realtek driver was completely different. It wasn’t a fork of Realtek’s code—it was new code written from scratch, part of `wireless-next`, a Linux kernel branch where new drivers are developed before being merged into mainline. GitHub user lwfinger maintains a backport so it can be used on current Linux versions.
That driver worked perfectly with Vanilla. So Realtek’s own driver was written so incorrectly that kernel developers rewrote it from scratch, and the original also broke Vanilla. With the right driver installed, those extremely common Realtek adapters were compatible after all.
### Broadcom and the FullMAC Wall
Broadcom presented a different problem. A Broadcom adapter would actually sync, then throw the same `ASSOC-REJECT` error at connection time. But unlike Realtek, the Broadcom driver was already in the kernel and seemingly passed the standards the Realtek driver didn’t. Sifting through the driver showed it wasn’t blocking Vanilla—it didn’t really do much at all.
The issue comes down to two types of Wi-Fi adapter:
- **SoftMAC adapters** are just dumb radios. They convert between bits and airwaves; all Wi-Fi protocol work—security handshakes, parsing incoming data, formatting outgoing data—is done in software on the host CPU, usually by something like `wpa_supplicant`. SoftMAC devices are the cheapest, easiest, and most common design.
- **FullMAC adapters** have their own CPU, memory, and firmware. They are essentially a whole separate system-on-a-chip that handles Wi-Fi protocol processing on its own.
FullMAC is a problem for Vanilla because the authentication handshake is baked into the chip. There is no way to modify it to be compatible with the Wii U. Even trying to hack the chip’s binary blob would mean wading through completely undocumented code, and doing that for every FullMAC chip is impractical.
Broadcom is a seasoned veteran of FullMAC chips—that’s essentially all they do now—making them a near-impenetrable wall for the Wii U.
Realtek adapters were only immune because they made SoftMAC hardware but wrote a FullMAC-style driver with its own self-contained implementation of the Wi-Fi standard. They literally wrote the driver wrong, which is why a better driver could immediately fix it.
## Porting Vanilla to the Raspberry Pi
For an actual gamepad-shaped device, viewers voted for the Raspberry Pi. On paper it made sense: it runs Linux, and even the cheapest Raspberry Pi Zero had Wi-Fi, sensors, displays, and webcams.
The concern: the Pi’s Wi-Fi chip is Broadcom. Also, the gamepad operates on 5GHz, while the Pi’s onboard Wi-Fi is only 2.4GHz. So the onboard Wi-Fi was useless either way. But because the Pi runs Linux, a compatible SoftMAC USB Wi-Fi adapter could be plugged in instead.
Looking at other single-board computers, the Orange Pi Zero 3 and 2W—ironically colored blue—were strong contenders because they provided 5GHz Wi-Fi and a small antenna as standard. But testing one showed yet another FullMAC Wi-Fi chip giving the familiar `ASSOC-REJECT` error. That left the Raspberry Pi as the main target.
## Making the Pi Zero Fast Enough
The main challenge was getting Vanilla to run well on modest hardware. Out of the box, it ran poorly. The chosen baseline was the original Raspberry Pi Zero from 2015—ten-year-old hardware the size of a thumb. It was slow, but if Vanilla could run there, it could run anywhere.
The plan was to strip everything back:
- No Qt interface
- No desktop environment
- No window manager
- Draw straight to the GPU with as little overhead as possible
Over fifteen days of live-streamed development, a new frontend was written in SDL, a much lower-level library than Qt. SDL provides a path to draw directly to the GPU using a Linux kernel feature called **KMSDRM**—Kernel Mode Setting Direct Rendering Manager.
Displaying video was harder. The Pi Zero’s CPU can’t decode the Wii U’s 480p video feed in real time. But the Pi Zero has a separate hardware decoder chip capable of decoding up to 1080p30, more than enough. The goal was to send hardware-decoded frames straight to the GPU without a roundtrip through the CPU.
This was surprisingly complicated. The Raspberry Pi supports it, but official documentation was hard to find. A sample project on GitHub served as a reference, and FFmpeg had to be replaced with a fork because even FFmpeg couldn’t do this out of the box. After several days, the video feed finally came through.
Against expectations, the video feed was smooth. Everything shown on screen, except the presenter’s face, was running on a $5 computer from 2015.
## The Half-Second Latency Problem
The video did have an enormous problem: nearly half a second of delay, enough to make games effectively unplayable. On PC there was no delay at all.
An obscure forum thread from 2013 described exactly the same issue: latency between half a second and multiple seconds. One reply even mentioned the Wii U gamepad while wondering how to achieve zero-latency video. The original poster eventually found that tweaking one of H.264’s SPS parameters eliminated all perceptible delay.
H.264 is one of the biggest video compression algorithms in the world, used in Blu-Rays, web video, and the Wii U gamepad. SPS, or **Sequence Parameter Set**, is a component embedded in all H.264 video streams that stores how the video was encoded and how to decode it. The crucial parameter here is `max_dec_frame_buffering`, which tells the decoder how many frames to buffer.
Vanilla didn’t provide this option at all, so decoders used their best judgment. FFmpeg’s software decoder on PC chose not to buffer frames, but the Raspberry Pi decoder did. Buffering frames exists because H.264 uses temporal compression—storing differences between frames, including bidirectional frames that reference both past and upcoming frames. The Wii U doesn’t use bidirectional frames, but the Pi decoder added delay anyway just in case—unless explicitly told not to via `max_dec_frame_buffering`.
The problem: the Wii U never actually sends SPS parameters. Nintendo hardcoded them into the gamepad. Vanilla had to make them up. And SPS parameters aren’t stored conventionally—they’re compressed using **Exp-Golomb** encoding, making them hard to modify without writing a full parser.
The solution was to have Vanilla generate its SPS parameters on the fly. That took an entire day of figuring out the encoding, understanding each parameter, and working around byte endianness. It worked: the half-second latency was finally gone.
But 4 frames of delay remained—better, but still noticeable during gameplay.
## Optimizing Vanilla for Weak Hardware
The performance work was more fun. Simple profiling tools can identify the slow parts of a program, though the Pi Zero is so primitive that the tools don’t actually run on it. So testing happened on the main PC, on the theory that slow code is slow code everywhere.
### Bit Reversal in Packet Processing
The packets sent by the Wii U have headers with reversed bits. drc-sim and Vanilla were bit-reversing the entire packet, then un-reversing the data section later. Bit reversal is expensive on x86, and the Pi Zero is too old for ARM’s dedicated bit-reversing instruction. This amounted to several milliseconds per frame—significant when a 60 FPS frame is only 16 milliseconds.
Limiting bit reversal to only the header fields actually read skipped over 99% of that processing. Testing different reversal methods found a clear winner several times faster than drc-sim’s manual approach, bringing bit reversal on the Pi to sub-1 millisecond.
### The Battery State Bug
Vanilla attempted to send Linux’s actual battery state to the Wii U, but only once every two seconds. The limiter contained a fatal bug that did nothing, so Vanilla pulled battery state every single frame. That hammered the CPU, even though neither the PC nor the Raspberry Pi was running on battery.
Months later, the bug was rediscovered, and a second limiter was written in another part of the code. Only while putting the video together did the presenter realize both limiters existed. The broken one was removed.
### Bypassing the Pipe
Vanilla is split into two parts: the frontend and the pipe. The pipe handles the Wii U connection and relays packets to the frontend. This has benefits—root permissions are limited to the pipe, and the pipe can run in a VM or another computer—but on the same machine it just wastes time copying data around. The optimized version lets the frontend receive packets directly, bypassing the pipe entirely when running on a single machine.
### The wpa_ctrl_recv Bug
The pipe had a central loop to detect disconnects, calling `wpa_supplicant`’s `wpa_ctrl_recv` function. The presenter assumed it was a blocking function—that it would wait until a message arrived. It wasn’t. Spamming it in a while loop maxed out an entire CPU thread, which is a big deal on the Pi Zero. Since Wi-Fi disconnects don’t need millisecond response times, the check was changed to once every second, making it negligible processing-wise.
## A New Universal Frontend
The next step was a menu system. The original plan was to write different frontends for each platform, but maintaining feature parity across them would be a nightmare. Instead, one frontend was built in SDL to work everywhere, designed for small touch screens and fitting the Wii U aesthetic.
This meant discontinuing the original Qt interface. Desktop PCs are the least sensible platform for a Wii U gamepad, and maintaining a separate frontend for them didn’t make sense. The new SDL frontend became the main Vanilla frontend, usable on the Pi and any other platform.
## Pi Zero 2: Still Not Good Enough
All the optimization made Vanilla run smoother, but it didn’t solve the 4-frame video latency. The Pi Zero 2, released in 2022, is significantly faster and costs the same as the original Pi Zero. It eliminated the occasional lag spots, but its decoder still had the same 4-frame latency.
A tiny test program that sent and received frames from the decoder showed why: three frames had to be sent before the decoder sent anything back, and the feed stayed three frames behind forever. Adding another frame for vsync explained the 4-frame total.
FFmpeg’s software decoder has no latency, but it was too slow for the Pi Zero 1. The Pi Zero 2 could handle it better but not well enough—the CPU couldn’t quite keep up, resulting in heavy video corruption.
Conclusion: neither Raspberry Pi Zero is really a suitable gamepad replacement. They can do it, but the experience is noticeably worse.
## The Orange Pi Surprise
The Orange Pi stuck in a drawer had no documentation about decoder latency, but it was worth testing. The test program was reworked for the Orange Pi’s decoder—and it sent frames back immediately. No forced delay.
After several days of getting the Orange Pi decoder-display pipeline working in Vanilla, there was a new problem: the Orange Pi’s GPU requires texture widths evenly divisible by 64. The Wii U’s video width didn’t meet that requirement. The decoder already padded widths to be divisible by 16, but that wasn’t enough. The decoder’s alignment could be configured, but only by editing the Linux driver’s source code.
The payoff: a latency-free gamepad clone. Not literally zero latency, but effectively on par with the original gamepad. And the Orange Pi Zero is considerably more powerful than the Raspberry Pi Zero, with no significant lag spots.
Ironically, the 5GHz Wi-Fi that made the Orange Pi attractive didn’t work out. The unadvertised zero-latency decoder put it head and shoulders above the competition.
## Is This Actually Practical?
Even with the Orange Pi working, there are major caveats. The Orange Pi Zero 2W was easy to buy two years ago at $13, but the current RAM crisis has driven some listings as high as $150.
A single-board computer alone isn’t a gamepad. It still needs:
- A screen
- Speakers
- A microphone
- Motion sensors
- A webcam
- A case
That’s at least $100 worth of parts on top of whatever board you use