Arduino: Wii Nunchuck Full Functionality

Arduino: Wii Nunchuck Full Functionality © CC BY-NC-SA

This combines a two-axis joystick, a three-axis accelerometer, and two buttons into one package.

  • 3,736 views
  • 1 comment
  • 21 respects

Components and supplies

Apps and online services

About this project

View this project on my website!

The Wii nunchuck is an attachment for the Nintendo Wii controller. It combines a two-axis joystick, a three-axis accelerometer, and two buttons into one package. Tod Kurt previously created a library for the nunchuck intended for use with his product, the WiiChuck Adapter. Since this product has been discontinued at SparkFun, I will show you how to use a Wii nunchuck with Arduino without the use of an adapter.

Materials:

As an Amazon Associate I earn from qualifying purchases.

Step 1: Plug Into the Controller

The Wii nunchuck has 6 pins, but we only need to use four of them. Plug some jumper wires directly into the corner pins of the nunchuck.

Step 2: Connect to the Arduino

The nunchuck pins are all plugged directly into pins A2 - A5 on the Arduino board. They go as follows:

  • Top left to A5
  • Top right to A2
  • Bottom left to A3
  • Bottom right to A4

Step 3: Upload Code

To eliminate the need to download and install a new library, I have essentially unpacked Tod Kurt's library and placed it directly into the code. However, if you still want to install it to reduce clutter you can look in step 4.After uploading this code, try pressing the Z button on the controller to make the Arduino's onboard led blink.

#include <Wire.h>
#if (ARDUINO >= 100)
#include <Arduino.h>
#else
#include <WProgram.h>
//#define Wire.write(x) Wire.send(x)
//#define Wire.read() Wire.receive()
#endif
static uint8_t nunchuck_buf[6];   // array to store nunchuck data,
// Uses port C (analog in) pins as power & ground for Nunchuck
static void nunchuck_setpowerpins()
{
#define pwrpin PORTC3
#define gndpin PORTC2
 DDRC |= _BV(pwrpin) | _BV(gndpin);
 PORTC &= ~ _BV(gndpin);
 PORTC |=  _BV(pwrpin);
 delay(100);  // wait for things to stabilize
}
// initialize the I2C system, join the I2C bus,
// and tell the nunchuck we're talking to it
static void nunchuck_init()
{
 Wire.begin();                // join i2c bus as master
 Wire.beginTransmission(0x52);// transmit to device 0x52
#if (ARDUINO >= 100)
 Wire.write((uint8_t)0x40);// sends memory address
 Wire.write((uint8_t)0x00);// sends sent a zero.
#else
 Wire.send((uint8_t)0x40);// sends memory address
 Wire.send((uint8_t)0x00);// sends sent a zero.
#endif
 Wire.endTransmission();// stop transmitting
}
// Send a request for data to the nunchuck
// was "send_zero()"
static void nunchuck_send_request()
{
 Wire.beginTransmission(0x52);// transmit to device 0x52
#if (ARDUINO >= 100)
 Wire.write((uint8_t)0x00);// sends one byte
#else
 Wire.send((uint8_t)0x00);// sends one byte
#endif
 Wire.endTransmission();// stop transmitting
}
// Encode data to format that most wiimote drivers except
// only needed if you use one of the regular wiimote drivers
static char nunchuk_decode_byte (char x)
{
 x = (x ^ 0x17) + 0x17;
 return x;
}
// Receive data back from the nunchuck,
// returns 1 on successful read. returns 0 on failure
static int nunchuck_get_data()
{
 int cnt = 0;
 Wire.requestFrom (0x52, 6);// request data from nunchuck
 while (Wire.available ()) {
   // receive byte as an integer
#if (ARDUINO >= 100)
   nunchuck_buf[cnt] = nunchuk_decode_byte( Wire.read() );
#else
   nunchuck_buf[cnt] = nunchuk_decode_byte( Wire.receive() );
#endif
   cnt++;
 }
 nunchuck_send_request();  // send request for next data payload
 // If we recieved the 6 bytes, then go print them
 if (cnt >= 5) {
   return 1;   // success
 }
 return 0; //failure
}
// Print the input data we have recieved
// accel data is 10 bits long
// so we read 8 bits, then we have to add
// on the last 2 bits.  That is why I
// multiply them by 2 * 2
static void nunchuck_print_data()
{
 static int i = 0;
 int joy_x_axis = nunchuck_buf[0];
 int joy_y_axis = nunchuck_buf[1];
 int accel_x_axis = nunchuck_buf[2]; // * 2 * 2;
 int accel_y_axis = nunchuck_buf[3]; // * 2 * 2;
 int accel_z_axis = nunchuck_buf[4]; // * 2 * 2;
 int z_button = 0;
 int c_button = 0;
 // byte nunchuck_buf[5] contains bits for z and c buttons
 // it also contains the least significant bits for the accelerometer data
 // so we have to check each bit of byte outbuf[5]
 if ((nunchuck_buf[5] >> 0) & 1)
   z_button = 1;
 if ((nunchuck_buf[5] >> 1) & 1)
   c_button = 1;
 if ((nunchuck_buf[5] >> 2) & 1)
   accel_x_axis += 1;
 if ((nunchuck_buf[5] >> 3) & 1)
   accel_x_axis += 2;
 if ((nunchuck_buf[5] >> 4) & 1)
   accel_y_axis += 1;
 if ((nunchuck_buf[5] >> 5) & 1)
   accel_y_axis += 2;
 if ((nunchuck_buf[5] >> 6) & 1)
   accel_z_axis += 1;
 if ((nunchuck_buf[5] >> 7) & 1)
   accel_z_axis += 2;
 Serial.print(i, DEC);
 Serial.print("\t");
 Serial.print("joy:");
 Serial.print(joy_x_axis, DEC);
 Serial.print(",");
 Serial.print(joy_y_axis, DEC);
 Serial.print("  \t");
 Serial.print("acc:");
 Serial.print(accel_x_axis, DEC);
 Serial.print(",");
 Serial.print(accel_y_axis, DEC);
 Serial.print(",");
 Serial.print(accel_z_axis, DEC);
 Serial.print("\t");
 Serial.print("but:");
 Serial.print(z_button, DEC);
 Serial.print(",");
 Serial.print(c_button, DEC);
 Serial.print("\r\n");  // newline
 i++;
}
// returns zbutton state: 1=pressed, 0=notpressed
static int nunchuck_zbutton()
{
 return ((nunchuck_buf[5] >> 0) & 1) ? 0 : 1;  // voodoo
}
// returns zbutton state: 1=pressed, 0=notpressed
static int nunchuck_cbutton()
{
 return ((nunchuck_buf[5] >> 1) & 1) ? 0 : 1;  // voodoo
}
// returns value of x-axis joystick
static int nunchuck_joyx()
{
 return nunchuck_buf[0];
}
// returns value of y-axis joystick
static int nunchuck_joyy()
{
 return nunchuck_buf[1];
}
// returns value of x-axis accelerometer
static int nunchuck_accelx()
{
 return nunchuck_buf[2];   // FIXME: this leaves out 2-bits of the data
}
// returns value of y-axis accelerometer
static int nunchuck_accely()
{
 return nunchuck_buf[3];   // FIXME: this leaves out 2-bits of the data
}
// returns value of z-axis accelerometer
static int nunchuck_accelz()
{
 return nunchuck_buf[4];   // FIXME: this leaves out 2-bits of the data
}
int loop_cnt = 0;
byte zbut;
void setup() {
 nunchuck_setpowerpins();
 nunchuck_init(); // send the initilization handshake
 pinMode(13, OUTPUT);
}
void loop() {
 if ( loop_cnt > 10 ) { // every 100 msecs get new data
   loop_cnt = 0;
   nunchuck_get_data();
   zbut = nunchuck_zbutton();  //  0 - 1
 }
 loop_cnt++;
 delay(1);
 digitalWrite(13, zbut);
}

The nunchuck has far more potential than just a simple button controller. Here is the code for full funcionality (you can view all of the incoming data in the Serial Monitor):

#include <Wire.h>
#if (ARDUINO >= 100)
#include <Arduino.h>
#else
#include <WProgram.h>
//#define Wire.write(x) Wire.send(x)
//#define Wire.read() Wire.receive()
#endif
static uint8_t nunchuck_buf[6];   // array to store nunchuck data,
// Uses port C (analog in) pins as power & ground for Nunchuck
static void nunchuck_setpowerpins()
{
#define pwrpin PORTC3
#define gndpin PORTC2
 DDRC |= _BV(pwrpin) | _BV(gndpin);
 PORTC &= ~ _BV(gndpin);
 PORTC |=  _BV(pwrpin);
 delay(100);  // wait for things to stabilize
}
// initialize the I2C system, join the I2C bus,
// and tell the nunchuck we're talking to it
static void nunchuck_init()
{
 Wire.begin();                // join i2c bus as master
 Wire.beginTransmission(0x52);// transmit to device 0x52
#if (ARDUINO >= 100)
 Wire.write((uint8_t)0x40);// sends memory address
 Wire.write((uint8_t)0x00);// sends sent a zero.
#else
 Wire.send((uint8_t)0x40);// sends memory address
 Wire.send((uint8_t)0x00);// sends sent a zero.
#endif
 Wire.endTransmission();// stop transmitting
}
// Send a request for data to the nunchuck
// was "send_zero()"
static void nunchuck_send_request()
{
 Wire.beginTransmission(0x52);// transmit to device 0x52
#if (ARDUINO >= 100)
 Wire.write((uint8_t)0x00);// sends one byte
#else
 Wire.send((uint8_t)0x00);// sends one byte
#endif
 Wire.endTransmission();// stop transmitting
}
// Encode data to format that most wiimote drivers except
// only needed if you use one of the regular wiimote drivers
static char nunchuk_decode_byte (char x)
{
 x = (x ^ 0x17) + 0x17;
 return x;
}
// Receive data back from the nunchuck,
// returns 1 on successful read. returns 0 on failure
static int nunchuck_get_data()
{
 int cnt = 0;
 Wire.requestFrom (0x52, 6);// request data from nunchuck
 while (Wire.available ()) {
   // receive byte as an integer
#if (ARDUINO >= 100)
   nunchuck_buf[cnt] = nunchuk_decode_byte( Wire.read() );
#else
   nunchuck_buf[cnt] = nunchuk_decode_byte( Wire.receive() );
#endif
   cnt++;
 }
 nunchuck_send_request();  // send request for next data payload
 // If we recieved the 6 bytes, then go print them
 if (cnt >= 5) {
   return 1;   // success
 }
 return 0; //failure
}
// Print the input data we have recieved
// accel data is 10 bits long
// so we read 8 bits, then we have to add
// on the last 2 bits.  That is why I
// multiply them by 2 * 2
static void nunchuck_print_data()
{
 static int i = 0;
 int joy_x_axis = nunchuck_buf[0];
 int joy_y_axis = nunchuck_buf[1];
 int accel_x_axis = nunchuck_buf[2]; // * 2 * 2;
 int accel_y_axis = nunchuck_buf[3]; // * 2 * 2;
 int accel_z_axis = nunchuck_buf[4]; // * 2 * 2;
 int z_button = 0;
 int c_button = 0;
 // byte nunchuck_buf[5] contains bits for z and c buttons
 // it also contains the least significant bits for the accelerometer data
 // so we have to check each bit of byte outbuf[5]
 if ((nunchuck_buf[5] >> 0) & 1)
   z_button = 1;
 if ((nunchuck_buf[5] >> 1) & 1)
   c_button = 1;
 if ((nunchuck_buf[5] >> 2) & 1)
   accel_x_axis += 1;
 if ((nunchuck_buf[5] >> 3) & 1)
   accel_x_axis += 2;
 if ((nunchuck_buf[5] >> 4) & 1)
   accel_y_axis += 1;
 if ((nunchuck_buf[5] >> 5) & 1)
   accel_y_axis += 2;
 if ((nunchuck_buf[5] >> 6) & 1)
   accel_z_axis += 1;
 if ((nunchuck_buf[5] >> 7) & 1)
   accel_z_axis += 2;
 Serial.print(i, DEC);
 Serial.print("\t");
 Serial.print("joy:");
 Serial.print(joy_x_axis, DEC);
 Serial.print(",");
 Serial.print(joy_y_axis, DEC);
 Serial.print("  \t");
 Serial.print("acc:");
 Serial.print(accel_x_axis, DEC);
 Serial.print(",");
 Serial.print(accel_y_axis, DEC);
 Serial.print(",");
 Serial.print(accel_z_axis, DEC);
 Serial.print("\t");
 Serial.print("but:");
 Serial.print(z_button, DEC);
 Serial.print(",");
 Serial.print(c_button, DEC);
 Serial.print("\r\n");  // newline
 i++;
}
// returns zbutton state: 1=pressed, 0=notpressed
static int nunchuck_zbutton()
{
 return ((nunchuck_buf[5] >> 0) & 1) ? 0 : 1;  // voodoo
}
// returns zbutton state: 1=pressed, 0=notpressed
static int nunchuck_cbutton()
{
 return ((nunchuck_buf[5] >> 1) & 1) ? 0 : 1;  // voodoo
}
// returns value of x-axis joystick
static int nunchuck_joyx()
{
 return nunchuck_buf[0];
}
// returns value of y-axis joystick
static int nunchuck_joyy()
{
 return nunchuck_buf[1];
}
// returns value of x-axis accelerometer
static int nunchuck_accelx()
{
 return nunchuck_buf[2];   // FIXME: this leaves out 2-bits of the data
}
// returns value of y-axis accelerometer
static int nunchuck_accely()
{
 return nunchuck_buf[3];   // FIXME: this leaves out 2-bits of the data
}
// returns value of z-axis accelerometer
static int nunchuck_accelz()
{
 return nunchuck_buf[4];   // FIXME: this leaves out 2-bits of the data
}
int loop_cnt = 0;
byte joyx, joyy, zbut, cbut, accx, accy, accz;
void _print() {
 Serial.print("Z Button:  ");
 Serial.print(zbut);
 Serial.print("\tC Button:  ");
 Serial.print(cbut);
 Serial.print("\tX Joy:  ");
 Serial.print(map(joyx, 15, 221, 0, 255));
 Serial.print("\tY Joy:  ");
 Serial.print(map(joyy, 29, 229, 0, 255));
 Serial.print("\tX Accel:  ");
 Serial.print(accx);
 Serial.print("\tY Accel:  ");
 Serial.print(accy);
 Serial.println("\tZ Accel:  ");
}
void setup() {
 Serial.begin(9600);
 nunchuck_setpowerpins();
 nunchuck_init(); // send the initilization handshake
 Serial.println("Wii Nunchuck Ready");
}
void loop() {
 if ( loop_cnt > 10 ) { // every 100 msecs get new data
   loop_cnt = 0;
   nunchuck_get_data();
   zbut = nunchuck_zbutton();  //  0 - 1
   cbut = nunchuck_cbutton();  //  0 - 1
   joyx = nunchuck_joyx();     //  15 - 221
   joyy = nunchuck_joyy();     //  29 - 229
   accx = nunchuck_accelx();   //  70 - 182
   accy = nunchuck_accely();   //  65 - 173
   accz = nunchuck_accelz();   //  0 - 255
   _print();
 }
 loop_cnt++;
 delay(1);
}

Step 4: Optional Library Installation

Visit Github to download Tod Kurk's original library. Below is the example code for once you do so:

#include <Wire.h>
#include <nunchuck_funcs.h>
int loop_cnt = 0;
byte joyx, joyy, zbut, cbut, accx, accy, accz;
void _print() {
 Serial.print("Z Button:  ");
 Serial.print(zbut);
 Serial.print("\tC Button:  ");
 Serial.print(cbut);
 Serial.print("\tX Joy:  ");
 Serial.print(map(joyx, 15, 221, 0, 255));
 Serial.print("\tY Joy:  ");
 Serial.print(map(joyy, 29, 229, 0, 255));
 Serial.print("\tX Accel:  ");
 Serial.print(accx);
 Serial.print("\tY Accel:  ");
 Serial.print(accy);
 Serial.println("\tZ Accel:  ");
}
void setup() {
 Serial.begin(9600);
 nunchuck_setpowerpins();
 nunchuck_init(); // send the initilization handshake
 Serial.println("Wii Nunchuck Ready");
}
void loop() {
 if ( loop_cnt > 10 ) { // every 100 msecs get new data
   loop_cnt = 0;
   nunchuck_get_data();
   zbut = nunchuck_zbutton();  //  0 - 1
   cbut = nunchuck_cbutton();  //  0 - 1
   joyx = nunchuck_joyx();     //  15 - 221
   joyy = nunchuck_joyy();     //  29 - 229
   accx = nunchuck_accelx();   //  70 - 182
   accy = nunchuck_accely();   //  65 - 173
   accz = nunchuck_accelz();   //  0 - 255
   _print();
 }
 loop_cnt++;
 delay(1);
}

Again, open the Serial Monitor to watch what happens.

Code

Code after library installatonArduino
#include <Wire.h>
#include <nunchuck_funcs.h>

int loop_cnt = 0;

byte joyx, joyy, zbut, cbut, accx, accy, accz;

void _print() {
  Serial.print("Z Button:  ");
  Serial.print(zbut);
  Serial.print("\tC Button:  ");
  Serial.print(cbut);
  Serial.print("\tX Joy:  ");
  Serial.print(map(joyx, 15, 221, 0, 255));
  Serial.print("\tY Joy:  ");
  Serial.print(map(joyy, 29, 229, 0, 255));
  Serial.print("\tX Accel:  ");
  Serial.print(accx);
  Serial.print("\tY Accel:  ");
  Serial.print(accy);
  Serial.println("\tZ Accel:  ");
}

void setup() {
  Serial.begin(9600);
  nunchuck_setpowerpins();
  nunchuck_init(); // send the initilization handshake
  Serial.println("Wii Nunchuck Ready");
}

void loop() {
  if ( loop_cnt > 10 ) { // every 100 msecs get new data
    loop_cnt = 0;

    nunchuck_get_data();

    zbut = nunchuck_zbutton();  //  0 - 1
    cbut = nunchuck_cbutton();  //  0 - 1
    joyx = nunchuck_joyx();     //  15 - 221
    joyy = nunchuck_joyy();     //  29 - 229
    accx = nunchuck_accelx();   //  70 - 182
    accy = nunchuck_accely();   //  65 - 173
    accz = nunchuck_accelz();   //  0 - 255

    _print();
  }
  loop_cnt++;
  delay(1);

}

Schematics

Schematic
Ftkwb2pjawts3cb large 89jx4yu16r

Comments

Similar projects you might like

Neopixel LED Eyeball Controlled by Nunchuck

Project tutorial by HomeMadeGarbage

  • 5,273 views
  • 7 comments
  • 28 respects

Arduino Paper Controller (Buttons + Slider)

Project tutorial by millerman4487

  • 6,779 views
  • 1 comment
  • 48 respects

Full Animatronic Sorting Hat With Custom Software -$22

Project tutorial by jegatheesan

  • 10,198 views
  • 11 comments
  • 80 respects

Control Servos using Wii Nunchuk

Project tutorial by Mark Tashiro

  • 20,313 views
  • 10 comments
  • 70 respects

GameBox - The Arduino LCD Console

Project tutorial by Team Mattia Colasanto

  • 2,460 views
  • 0 comments
  • 6 respects
Add projectSign up / Login