When the Nintendo Wii was released in 2006, there was a lot of talk about their new weird control system. In place of a typical control pad, players would use a one-handed “remote” with infrared sensors and accelerometers in place of a joystick. For those games that required additional controls, players would use an accessory controller in their off-hand.
This ‘accessory’ controller is the Nunchuk. A strange, bean-shaped attachment with a joystick, two buttons, and a three-axis accelerometer. Although the Nunchuk had a lukewarm response when it was first released, it’s the perfect controller for makers who want to add some fine control to their projects.
In this tutorial I’m going to show you how to use a Wii Nunchuk with an Arduino: how to wire it, how to talk to it, and how to easily build programs using it and the NintendoExtensionCtrl library. Let’s get started!
Getting Connected
While the Wii remote itself is wireless, the Nunchuk was designed to plug into an accessory port at the bottom of the remote. This is actually better for DIY-ers, because it makes it easier to connect to a microcontroller.
To connect to the Nunchuk I purchased a “Nunchuky” breakout board from Adafruit for a cool $3. The Nunchuk’s extension connector snaps right into the board, which exposes the power and data lines so I can plug it into a breadboard. You can also cut the connector off and use the bare wires, but there’s something to be said for keeping the controller intact.
From the Nunchuk’s 6-pin connector, these breakout boards expose 4 pins:
- Gnd: Ground.
- 3.3V: VCC
- Data: SDA, I2C serial data.
- Clk: SCL, I2C serial clock line.
Of the remaining two pins, one is not-populated on the connector and one is a power return used for controller detection. Unfortunately the breakout doesn’t expose this return pin, but you can solder to it if you need a controller-detect feature for your project.
Data Lines
As it turns out, the communication protocol used by the Nunchuk is none-other than standard I2C! Most microcontrollers have hardware support for I2C, so you can connect the data and clock lines from the Nunchuk directly to the supported pins on the microcontroller.
For the Arduino Uno, Nano, and other 328P-based boards the connection pins for SDA and SCL are A4 and A5, respectively. For the Arduino Leonardo and other 32U4-based boards, you use pins 2 and 3 for data and clock. If you’re not sure which pins to use, do a search for your microcontroller and “I2c”.
If you’re familiar with I2C, note that you do not need to add any pull-up resistors. The Nunchuk already has pull-up resistors onboard. (Or at least it should – some knockoffs are missing them!) You can test the pull up resistors yourself by measuring the resistance between one of the I2C wires and 3V3 while the controller is unplugged.
Voltage Levels
Power is trickier. The Wii remote operates at 3.3V, which means it outputs 3.3V to power the Nunchuk and pulls the I2C lines (data and clock) up to 3.3V when idle. Nunchuks are not designed to work with voltages over 3.3V, and higher voltages may damage the controller. This presents a problem for many Arduinos, the majority of which operate at a 5V logic level.
If your Arduino has a 3.3V regulator / pin, you should use that to power the Nunchuk (connecting to its ‘3V3’ pin). If your Arduino does not have a 3.3V regulator, such as with an Arduino Pro Micro, it’s highly recommended that you use a separate 3.3V regulator to power the Nunchuk.
Similarly, if your Arduino operates at 5V (Uno, Nano, Mega, Leonardo, etc.) and not 3.3V (Due, Zero) you should use a level shifter for the I2C data and clock lines. This prevents the Arduino from pulling the lines to 5V and makes the data stream more reliable. If your Arduino operates at 3.3V you can connect everything directly. (This is one of the reasons I recommend using a Teensy with a Nunchuk).
All of that being said, some Nunchuks appear to tolerate 5V. My Nintendo-made Nunchuks seem to work fine with an Uno and 5V power, while the third-party Nunchuks I have flip out and report bad data if the joystick is pushed too close to the edge. Your mileage may vary.
As with any electrical component, using a higher voltage level than expected – if it works at all – will usually lower the lifespan of the part. If your Nunchuk does work perfectly with +5V power, beware that it might fail prematurely.
Reading Nunchuk Data
With the Nunchuk connected to the Arduino it’s time to start reading data from the controller!
Thankfully a lot of smart people have messed around with these Wii extension controllers before me, so there is plenty of information online about how they work. After fighting with most of the existing libraries out there I decided to create my own called NintendoExtensionCtrl, which works with the Nunchuk and a few other controllers. You can download it from GitHub or by using the libraries manager in the Arduino IDE.
Setup and Initialization
Before we can start grabbing data from the controller we need to do a bit of setup. Create a new ‘sketch’ in the Arduino IDE, and at the top #include
the NintendoExtensionCtrl
library. Then create a new Nunchuk
object that we’re going to call nchuk
:
#include <NintendoExtensionCtrl.h> Nunchuk nchuk;
Now at the start of the setup()
function we’re going to initialize the I²C bus used by the Nunchuk by calling nchuk.begin()
. This sets up the microcontroller’s hardware I²C functionality and prepares it to send and receive data from the controller. We’re also going to initialize the Serial
bus so we can send mesages back to the PC and see what’s going on:
void setup() { Serial.begin(115200); nchuk.begin();
Last but not least we need to ‘connect’ to the controller. This library function initializes the controller in ‘unencrypted’ mode and retrieves the controller’s identity string. If the controller responds back, the function returns ‘true’ and we can start reading data! We’re going to stick this in a while
loop so that the Arduino keeps trying to talk to the controller until it’s connected, and add a delay so it only tries once per second:
while (!nchuk.connect()) { Serial.println("Nunchuk not detected!"); delay(1000); } }
Polling for Data
Because the Nunchuk works over the two-wire I2C bus, updates need to be polled from the device. Calling nchuk.update()
requests 6 bytes of data from the controller. These 6 bytes contain all of the information for the Nunchuk’s controls.
If the update was successful, the function returns ‘true’. If it’s unsuccessful, the function returns ‘false’ and you may need to reconnect to the controller (connect()
function, as above):
void loop() { boolean success = nchuk.update(); // Get new data from the controller
After updating with the most recent data, you can then retrieve the state of the Nunchuk’s controls. The library has built-in functions that do all of the bit-shifting and masking for you, and return the data either as an unsigned integer (joysticks / accelerometer) or as a boolean (button):
if(success == true) { // We have new data! nunchuk.update(); nunchuk.joyX(); // 0 - 255 nunchuk.joyY(); nunchuk.accelX(); // 0 - 1023 nunchuk.accelY(); nunchuk.accelZ(); nunchuk.buttonZ(); // 1 = On nunchuk.buttonC(); } }
For more examples of how these functions are used, check out the ‘Nunchuk_Demo’ example from the library.
Using Multiple Nunchuks
This is all fine-and-dandy for using a single controller, but what if you want to use two or more Nunchuks?
This is possible, but unfortunately it isn’t quite as easy. The system was designed to only use one device per Wii remote, and the limitation here is the I2C address.
The I2C bus used by the Nunchuk (and other extension controllers) is addressable. This means that it’s possible to have dozens of devices all communicating on the same bus using just two wires. To talk to a specific device the controller makes a request to that address. This is the problem, as all Wii extension controllers have the same address – 0x52
. If you put two controllers on the same bus, they will both try to respond at the same time and you’ll have a conflict.
The solution is to use two I2C buses. Or, if you only have one bus on the microcontroller, to use an I2C multiplexer like the TCA9548A. Adafruit sells a nice breakout that works well. I’m currently using this multiplexer to have two Nunchuks work on an Arduino Pro Micro. It also conveniently acts as a level-shifter!
Example Project: LED Controller
Now it’s time to put this knowledge to work and make something! My typical go-to is something with RGB LEDs because they are quick to set up, have a lot of parameters, and offer clear visual feedback. As the Nunchuk is a controller, why not set it up to control a few LEDs?
I’m going to set this up as follows:
- Joystick X: Set number of LEDs (left to right)
- Joystick Y: Set brightness
- Accel XYZ: Set RGB values
- C Button: Flicker on/off
- Z Button: Turn on / off all
For my LEDs I’m going to use a small strip of WS2812B addressable LEDs, or what Adafruit calls “NeoPixels”. They’re inexpensive and easy to work with if you use a library like FastLED.
Setup
First a few housecleaning things. Import the NintendoExtensionCtrl
and FastLED
libraries, set a few global variables, and start both libraries in the setup
function.
#include <NintendoExtensionCtrl.h> #include <FastLED.h> #define r 0 #define g 1 #define b 2 const uint8_t NumLEDs = 7; const uint8_t LedPin = A0; const uint8_t MinBright = 10; const uint8_t MaxBright = 180; uint8_t ledsOn = NumLEDs; Nunchuk nunchuk; CRGB leds[NumLEDs]; void setup() { FastLED.addLeds<WS2812B, LedPin, GRB>(leds, NumLEDs).setCorrection( TypicalLEDStrip ); nunchuk.begin(); while(!nunchuk.connect()){ delay(1000); } } void loop(){ clear(); // Clear LED data for new loop if(!nunchuk.update()){ FastLED.show(); // write cleared LEDs delay(500); return; // Bad data read } ... } void clear(){ for(int i = 0; i < NumLEDs; i++){ leds[i] = 0; } }
Once we get to the loop, the code clears the LED data and polls the controller for new information. Each control is then handled in-turn.
JoyX: Number of LEDs
The ‘X’ value of the joystick controls how many LEDs are on. The value is divided by a parameter so the max possible value is the maximum number of LEDs in the strip.
ledsOn = nunchuk.joyX() / (255 / numLEDs);
This value is stored in a global variable. It’s going to be used to set the loop length when the LED colors are set.
JoyY: Brightness
The ‘Y’ value of the joystick controls the brightness of the LEDs.
uint8_t brightness = map(nunchuk.joyY(), 0, 255, MinBright, MaxBright);
The brightness is scaled depending on the ‘min’ and ‘max’ brightness values, set at the top of the code. This is just a quality of life thing – the LEDs can get crazy bright. It also works using the joystick value directly.
Accel XYZ: Color Values
The accelerometer values for X, Y, and Z control the RGB color of the strip.
void loop() { ... setColors(r, nunchuk.accelX()); setColors(g, nunchuk.accelY()); setColors(b, nunchuk.accelZ()); ... } void setColors(uint8_t rgb, uint16_t val){ if(rgb >= 3) return; for(int i = 0; i < ledsOn; i++){ leds[i].raw[rgb] = val / 4; } }
The accelerometer value is 10 bits (0 – 1023), so it’s divided by 4 to get an 8-bit value that fits in the array. The LEDs are assigned from the start of the strip through the number dictated by the ‘X’ joystick value stored earlier.
Z Button: Turn off
The large ‘Z’ button turns off the entire strip.
if(nunchuk.buttonZ()){ clear(); }
This just calls the ‘clear’ function from earlier if the button is pressed. Nice and easy.
Z Button: Flicker
The smaller ‘C’ button makes the strip flicker.
void loop(){ ... else if(nunchuk.buttonC()){ blink(150); } ... } void blink(long blinkTime){ static unsigned long lastMillis = 0; static boolean blinkState = 0; if(blinkState == true){ clear(); } if(millis() - blinkTime/2 >= lastMillis){ blinkState = !blinkState; lastMillis = millis(); } }
The function takes a millisecond input of the total blinking period and flips a boolean every half period. If that boolean is true, the function clears the LEDs.
Writing to the Strip
At the end of the loop, the program updates the brightness and writes the LED data to the strip.
void loop() { ... FastLED.setBrightness(brightness); FastLED.show(); }
And just like that, I have a working LED controller from a Wii Nunchuk!
Here’s the full sketch. Click below to expand the code.
#include <NintendoExtensionCtrl.h> #include <FastLED.h> #define r 0 #define g 1 #define b 2 const uint8_t NumLEDs = 7; const uint8_t LedPin = A0; const uint8_t MinBright = 10; const uint8_t MaxBright = 180; uint8_t ledsOn = NumLEDs; Nunchuk nunchuk; CRGB leds[NumLEDs]; void setup() { FastLED.addLeds<WS2812B, LedPin, GRB>(leds, NumLEDs).setCorrection( TypicalLEDStrip ); nunchuk.begin(); while(!nunchuk.connect()){ delay(1000); } } void loop() { clear(); // Clear LED data for new loop if(!nunchuk.update()){ FastLED.show(); // write cleared LEDs delay(500); return; // Bad data read } ledsOn = nunchuk.joyX() / (255 / NumLEDs); uint8_t brightness = map(nunchuk.joyY(), 0, 255, MinBright, MaxBright); setColors(r, nunchuk.accelX()); setColors(g, nunchuk.accelY()); setColors(b, nunchuk.accelZ()); if(nunchuk.buttonZ()){ clear(); } else if(nunchuk.buttonC()){ blink(150); } FastLED.setBrightness(brightness); FastLED.show(); } void setColors(uint8_t rgb, uint16_t val){ if(rgb >= 3) return; for(int i = 0; i < ledsOn; i++){ leds[i].raw[rgb] = val / 4; } } void clear(){ for(int i = 0; i < NumLEDs; i++){ leds[i] = 0; } } void blink(long blinkTime){ static unsigned long lastMillis = 0; static boolean blinkState = 0; if(blinkState == true){ clear(); } if(millis() - blinkTime/2 >= lastMillis){ blinkState = !blinkState; lastMillis = millis(); } }
Conclusion
These are really cool little controllers, and they’re surprisingly easy to use with Arduino thanks to the I²C interface and the NintendoExtensionCtrl library.
Have you used a Nunchuk in your Arduino project? Share in the comments below!
Under the Hood
In case you’re curious or want to try this without using my library, let’s briefly cover how the Nunchuk I²C communication works at a lower level.
The following code snippets have been borrowed from the NintendoExtensionCtrl library and simplified for explanatory purposes. To look at what the library is actually doing, check out the communications header and the static communications functions.
Initialization
Before it will send any data, the Nunchuk needs to be initialized. The library makes this easy with a connect
method, but let’s look at what’s going on under the hood.
boolean i2c_writeRegister(byte addr, byte reg, byte value) { Wire.beginTransmission(addr); Wire.write(reg); Wire.write(value); if (Wire.endTransmission() != 0) return false; // 0 = No Error return true; } boolean ExtensionController::initialize() { if (!i2c_writeRegister(0x52, 0xF0, 0x55)) return false; delay(10); if (!i2c_writeRegister(0x52, 0xFB, 0x00)) return false; delay(20); return true; }
The Nunchuk acts as a typical register-based I²C device. The library joins the hardware I2C bus as a master and then writes two bytes to the Nunchuk: 0x55
to register 0xF0
, and 0x00
to register 0xFB
. According to information I found online the Nunchuk normally encrypts its output using an XOR transformation. By initializing the device with these two commands, the Nunchuk operates in an “unencrypted communication mode”. This means the data it sends in response to a polling request, while bit-packed for transfer, is plainly formatted and easily parsed.
The connect() method for the library also has a function to identify what type of controller is connected, as the Wii remote’s port also supports other controllers like the Wii classic controller and musical instruments for games like Guitar Hero. The identity function requests 6 bytes from register 0xFA
and then compares them against a known list. The Nunchuk, for example, is identified by the last two bytes being 0x00
. See the library’s identity header for more info.
Control Data
boolean i2c_writePointer(byte addr, byte ptr) { Wire.beginTransmission(addr); Wire.write(ptr); if (Wire.endTransmission() != 0) return false; // 0 = No Error delayMicroseconds(175); // Wait for data conversion return true; } boolean ExtensionController::requestControlData(size_t requestSize, uint8_t* controlData) { if (!i2c_writePointer(0x52, 0x00)) return false; // Set start for data read uint8_t nBytesRecv = Wire.readBytes(controlData, Wire.requestFrom(0x52, requestSize)); return (nBytesRecv == requestSize); // Success if all bytes received }
To poll for an update, the Arduino first sends 0x00
to the Nunchuk to set the register pointer at the start of the data block. It then requests 6 bytes of data from the Nunchuk. These 6 bytes contain all of the information for the Nunchuk’s controls.
The data from the Nunchuk comes packaged in a specific way to save bandwidth on the bus. This allows Nintendo to squeeze a little more range out of the accelerometer while keeping the max polling rate the same.
The first two bytes are the joystick X and Y values, respectively. The next three bytes are the most significant bits for the accelerometer values (XYZ). The last byte contains the two least significant bits for all three accelerometer values, as well as single bits for the on/off states of both buttons. These button values are inverted, so a ‘0’ means the button is pressed.
The bit-depth for the controls are as follows:
- Joystick XY: 8 bit (0 – 255)
- Accelerometer XYZ: 10 bit (0 – 1023)
- Buttons C/Z: 1 bit (0 – 1)
After retrieval from the bus the controls can be extracted by using bitshifting and masking on the data array.
This article contains affiliate links. As an Amazon Associate I earn from qualifying purchases.
15 Comments
rich942 · January 19, 2019 at 2:13 am
Thanks much for this article. Just ordered a wireless Nunchuk from eBay to replace the IR remote on one of my remixed EEZYbot robot arms. IR is OK, but too directional, and Arduino Bluetooth cheap controllers not ideal for a 4 axis robot. Can’t wait to try this, thanks again !
ryan3473 · February 28, 2019 at 6:13 am
I’m trying to hook a Nunchuck up with an Arduino Nano but I can’t get the two to connect for the life of me. I have all the hardware connected properly, but I can’t get any data to show up on the serial monitor. I’ve tried multiple different libraries, and I still can’t get it to work. For your Nunchuk_DebugPrint example, I cannot get anything to appear over the serial bus besides, “Nunchuk not detected!”. I’m not getting any errors when compiling/uploading either.
I’m using a generic Nunchuck made by a company called PowerA. I’ve also tried using an original white one too, but didn’t get any further. Am I not doing something right?
Dave · February 28, 2019 at 11:44 am
If you’ve tried multiple libraries that all don’t work then my guess is that your hardware *isn’t* connected properly. 3.3V to power, SDA to pin A4, SCL to pin A5, GND to GND. If you’re not using a breakout board, double-check your wiring using a multimeter.
Generic / knockoff devices can potentially be more troublesome. If you’re having issues, I would try getting the genuine device working before the generic.
ryan3473 · March 1, 2019 at 12:58 am
I was able to get it working properly. Some connections wiggled lose but were still close enough to touching that it looked fine. Thanks for the time you took to reply.
ectokon · June 25, 2019 at 9:24 am
Thanks for creating this library! I recently came across an old Guitar Hero controller and after looking at several Wii controller libraries yours seems like the easiest to use and most responsive.
My plan was to combine this controller with the Mozzi wavetable synthesizer library but when I load the two libraries they conflict…it appears that Mozzi blocks the i2c bus to better free up the processor for audio. I found a reference to this issue on their forums and they’ve written a utility that allows for i2c to run in a non-blocking way and they suggest replacing the Wire functions in the controller code with their non-blocking version, so that’s what I’m trying to do. Unfortunately, I’m not very strong in C++ (or i2c for that matter) so I’m a bit in the weeds and could use some advice. As far as I can tell, I’ll need to sub out the Wire library references in the NXC_Comms.h with Mozzi’s twi_nonblocking equivalent functions. Do you think I’m on the right track?
Here’s what I’ve been referencing…the Mozzi utility is twi_nonblock:
https://github.com/sensorium/Mozzi
and a forum reference to the same issue:
https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!msg/mozzi-users/0YMdH0y0I1Q/91VT8YRYBgAJ;context-place=msg/mozzi-users/Kcq8we8wAek/9V31EDHM3lQJ
Any advice would be appreciated.
Dave · June 25, 2019 at 10:33 am
Hey there!
It definitely sounds like you’re on the right track, although you’ll have your work cut out for you.
From what I remember I collected all of the I2C functionality into that ‘comms’ header, so you should only need to edit that file. If you’re not importing the I2C library (Wire) at all you’ll also have to edit the preprocessor define at the top to provide an alternate object ‘type’ for the rest of the library. A reference to the I2C object is used elsewhere, but almost exclusively to pass back to the ‘comms’ functions for multiple bus support. The only exception to that is the initial ‘begin’ call (see ExtensionController.cpp). You can ignore the object in the functions, but it’s going to be easier to provide a dummy than it is to rewrite the function arguments everywhere.
There are three delays used in the I2C functions that you’ll also have to account for – two for the initial ‘connect’ function (total of 30 ms) and one 200 us delay in the data update. If the guitar is the only input to the synth you can probably leave the ‘connect’ function as-is, but you’ll likely have to write some non-blocking time check code for the update function to make sure the read is spaced out. If you try to read the data too soon after requesting it the data will be corrupt.
The good news is that if the Mozzi I2C implementation matches the Wire library in layout and functionality you shouldn’t have to mess with any lower level I2C stuff. Just make sure you do your due diligence testing the robustness of the non-blocking controller communications before you try adding the synth stuff on top.
I’ve never used Mozzi myself so I can’t help there, but it sounds like a fun project. I hope that helps – let me know how it goes!
ectokon · June 26, 2019 at 8:18 am
Perfect! I’ll give it a shot. Thanks for the quick reply.
Vitor Bergamaschi dos Santos · November 11, 2019 at 6:42 pm
Hello! I was searching about using nunchuk with arduino and found that are diferent ways to initilize the nunchuk:
-On a white Wii Nunchuk ,send 0x40, 0x00 followed by 0x00.
and
-On a black Wii Nunchuk, send 2 bytes 0xF0, 0x55 to initialize the first register and 0xFB, 0x00 toinitialize the second register of the Nunchuk
But the problems is that my nunchuk is red 😀 so i don’t know how to proced ;/ If you could answer I would be very happy!
Dave · November 11, 2019 at 7:16 pm
Did you try the library referenced in the post?
The `0x40` method is the encrypted initialization code. The `0xF0` method is the unencrypted initialization code. These are the same regardless of color.
Vitor Bergamaschi dos Santos · November 11, 2019 at 7:30 pm
So, in the truth i just read a little the library beacause I am seeing the use of nunchuk with arduino to “copy” the way to initializate the nunchuk but with 8051 uC, cause we learned 8051 in school so I would like to show that to my teacher in the final project exam, but I am not understand very well somethings, like: the nunchuk uses I2C bus, so we have to send a start bit, then the nunchuk adress (0x52), then the ack, then the byte data we want to move and finally the stop bit. But how I will address a register after address the nunchuk? ( I know the library does that for us, but I need to program in assembly so.. :D). Or the only thing I need to do is address the nunchuk as 0x52 and then send the 0xf0, 0x55 bytes directly? Sorry for the long question, but I am just a begginner :/ 😀 but thx a lot for answer!
Dave · November 11, 2019 at 7:39 pm
The register address is the first data byte sent, the value to store is the second data byte sent. You treat it just like any other register-based I2C device.
Do you know how to read C? I’d recommend looking at the `NXC_Comms.h` file in the library source code, it should be easy to follow.
Vitor Bergamaschi dos Santos · November 11, 2019 at 7:48 pm
But how I address the register 0xF0 if I need to addres the nunchuk as 0X52? I know C so so, like I just read “while” “if” “else” and try to make it logical in the code. I am reading the libraries to understandig the things “under the hood”
Vitor Bergamaschi dos Santos · November 11, 2019 at 8:10 pm
I know that I am asking to much for a school project that I should make for my self, but I am not considering this a “cheat” cause in the course, the master didn’t teach us I2C communication so I feel like I can make some questions to learn the max possible.
Kushagra · September 16, 2020 at 9:10 am
I feel so lucky to stumble across this page. I was trying to get a newly-bought Wii Nunchuck with an Arduino Leonardo, I was wiring the I2C wires to the pins A4 AND A5 and got nothing on the Serial Monitor. After reading your guide, I finally got the Nunchuck to work with my Arduino board.
Thanks a lot for sharing!
Dave · September 16, 2020 at 11:41 am
No problem, I’m glad you found it helpful!