Wii Nunchuck Arduino Tutorial

Why Wii Nunchuck Arduino? You can create and build endless electronics design and project with the powerful Arduino, for example a robot. By attaching Wii Nunchuck Arduino, you create the opportunity to control Arduino using the way we most familiar with, the way we play games!

Not to mention, Wii Nunchuck is cheap (~5 US dollars), and it also has three-axis accelerometer for motion sensing, which is very useful.

What hardware are required?

  • Arduino
  • Wii Nunchuck
  • Nunchuck – Arduino Adapter

wiichuck_adapter2

wiichuck_adapter3

Wii Nunchuck Arduino Hardware Explained

Wiimote Bus Pins (6-pin proprietary connector on Wiimote)

The wii nunchuck accessory bus is a 6 pin data connector. However, only 4 pins are useful to us.

Looking into Wiimote:

|   135   |
|   246   |
|___---___|
  • 1 (Red wire) – +3V
  • 2 (Yellow wire) – Clk
  • 3 (Red wire) – Attachment detection?
  • 4 (No wire) – Unknown (unconnected at Nunchuk connector)
  • 5 (Green wire) – Data
  • 6 (White wire) – GND

Hook up Arduino Uno and Wii Nunchuck

wiichuck-diag

wiichuck_adapter1

Hook up Arduino Mega and Wii Nunchuck

  • + to +3.3V
  • – to GND
  • d to SDA (pin 20)
  • c to SCL (pin 21)

wii-nunchuk-arduino-mega

That’s the hardware part, let’s have a look at the software side (library)

If you want to discuss or share your ideas, you can post something in our forum here.

Wii Nunchuck Arduino Library

I have attached the library we need, and explain the the functions we use, and how to use these functions. This is a modified version of the original source code from Here

How to use them?

you need to include these libraries:

    #ifndef NUNCHUCK_H
        #define NUNCHUK_H
        #include <Wire.h>
        #include "nunchuck.h"
    #endif

Then initialize nunchuck in the Void setup() function

Serial.begin (19200); // Required by Wii Nunchuck
NunC_Init(0); // initialize nunchuck

In Loop() function, you check whether there is a new command, after the if loop, you send a signal back the Wii Nunchuck to tell it you are ready for next input, and add a little delay to allow transmissions to happen:

if (NunC_RecieveCommand() == true)
{
    // Get the input values, and do your stuff here
}
NunC_SendNextByteRequest(); // send the request for next bytes
delay(10);

To get the inputs, you can use these global variables

// NunC_JX - 0 to 255
// NunC_JY - 0 to 255
// NunC_AX - 0 to 255
// NunC_AY - 0 to 255
// NunC_AZ - 0 to 255
// NunC_BC - 0, 1
// NunC_BZ - 0, 1

For example, if I want to get the accelerometer X-axis value, I do this

X = map(NunC_AX, 0, 255, -13, 13);

I map it to a range that is useful to the application.

Here is the Library source code:


#define POWER_VIA_PORT_C2_C3 1

#define USE_NEW_WAY_INIT 1 
#define WII_IDENT_LEN ((byte)6)
#define WII_TELEGRAM_LEN ((byte)6)
#define WII_NUNCHUCK_TWI_ADR ((byte)0x52)

#include "WProgram.h"
#include <string.h> 
#include <utilitytwi.h>
#undef int
#include <stdio.h>

static uint8_t outbuf[WII_TELEGRAM_LEN];	 // array to store arduino output
int cnt = 0;

// -------- output -------
byte NunC_JX = 0;
byte NunC_JY = 0;
byte NunC_AX = 0;
byte NunC_AY = 0;
byte NunC_AZ = 0;
boolean NunC_BC = 0;
boolean NunC_BZ = 0;

// ===========================================================
// ================  Private Functions  ======================
// ==========================================================

byte readControllerIdent(byte* pIdent)
{
	byte rc = 1;

	// read identification
	Wire.beginTransmission (WII_NUNCHUCK_TWI_ADR);	// transmit to device 0x52
	Wire.send (0xFA);	 // sends memory address of ident in controller
	if(Wire.endTransmission () == 0)	// stop transmitting
	{
		byte i;
		Wire.requestFrom (WII_NUNCHUCK_TWI_ADR, WII_TELEGRAM_LEN);	// request data from nunchuck
		for (i = 0; (i < WII_TELEGRAM_LEN) && Wire.available (); i++) 		{ 			pIdent[i] = Wire.receive();	// receive byte as an integer 		} 		if(i == WII_TELEGRAM_LEN) 		{ 			rc = 0; 		} 	} 	return rc; } void clearTwiInputBuffer(void) { 	// clear the receive buffer from any partial data 	while( Wire.available ()) 		Wire.receive (); } // Decode data format that original Nunchuck uses with old init sequence. This never worked with // other controllers (e.g. wireless Nunchuck from other vendors) char nunchuk_decode_byte (char x) { #ifndef USE_NEW_WAY_INIT 	x = (x ^ 0x17) + 0x17; #endif 	return x; } void ConvertOutputs(){      /*   Joy - X 0 137 255      [0]         Y 0 142 255      [1]   Acc - X 290  560  715  [2]  (*2*2)         Y 320  535  750  [3]  (*2*2)   */   // will use Joy X, Y for Coxa and femur, Acc Y for Tibia   /*   PosX = outbuf[1]/4;   PosZ = (outbuf[0]-128)/2;   PosY = (outbuf[2] - 72)/2;   */      NunC_JX = outbuf[0];   NunC_JY = outbuf[1];   NunC_AX = map(outbuf[2]*2*2, 256, 768, 0, 255);   NunC_AY = map(outbuf[3]*2*2, 256, 768, 0, 255);   NunC_AZ = map(outbuf[4]*2*2, 256, 768, 0, 255);   if ((outbuf[5] >> 0) & 1)  NunC_BZ = 0;
  else                       NunC_BZ = 1;
  if ((outbuf[5] >> 1) & 1)  NunC_BC = 0;
  else                       NunC_BC = 1;

  // buffer 5 also contains the least significant bit of accelerometer data
  if ((outbuf[5] >> 2) & 1)  NunC_AX += 2;
  if ((outbuf[5] >> 3) & 1)  NunC_AX += 1;
  if ((outbuf[5] >> 4) & 1)  NunC_AY += 2;
  if ((outbuf[5] >> 5) & 1)  NunC_AY += 1;
  if ((outbuf[5] >> 6) & 1)  NunC_AZ += 2;
  if ((outbuf[5] >> 7) & 1)  NunC_AZ += 1;

}

// ===========================================================
// ================  Public Functions  ======================
// ==========================================================

// params:
// timeout: abort when timeout (in ms) expires, 0 for unlimited timeout
// return: 0 == ok, 1 == timeout
void NunC_Init (unsigned short timeout)
{

  // ----------------------- setup power pins -----------------------

    #ifdef POWER_VIA_PORT_C2_C3 // power supply of the Nunchuck via port C2 and C3
	PORTC &=~ _BV(PORTC2);
	PORTC |= _BV(PORTC3);
	DDRC |= _BV(PORTC2) | _BV(PORTC3); // make outputs
	delay(100); // wait for things to stabilize
    #endif

  // ----------------------- timer speed? -----------------------
	Wire.begin(); // initialize i2c
	// we need to switch the TWI speed, because the nunchuck uses Fast-TWI
	// normally set in hardwarelibrariesWireutilitytwi.c twi_init()
	// this is the way of doing it without modifying the original files
    #define TWI_FREQ_NUNCHUCK 400000L
	TWBR = ((CPU_FREQ / TWI_FREQ_NUNCHUCK) - 16) / 2;

	byte rc = 1;

  // ----------------------- initialize nunchuck -----------------------
    #ifndef USE_NEW_WAY_INIT
	// look at <http://wiibrew.org/wiki/Wiimote#The_Old_Way> at "The Old Way"
	Wire.beginTransmission (WII_NUNCHUCK_TWI_ADR);	// transmit to device 0x52
	Wire.send (0x40);	 // sends memory address
	Wire.send (0x00);	 // sends sent a zero.
	Wire.endTransmission ();	// stop transmitting
    #else
	// disable encryption
	// look at <http://wiibrew.org/wiki/Wiimote#The_New_Way> at "The New Way"

	unsigned long time = millis();
	do
	{
		Wire.beginTransmission (WII_NUNCHUCK_TWI_ADR);	// transmit to device 0x52
		Wire.send (0xF0);	 // sends memory address
		Wire.send (0x55);	 // sends data.
		if(Wire.endTransmission() == 0) // stop transmitting
		{
			Wire.beginTransmission (WII_NUNCHUCK_TWI_ADR);	// transmit to device 0x52
			Wire.send (0xFB);	 // sends memory address
			Wire.send (0x00);	 // sends sent a zero.
			if(Wire.endTransmission () == 0)	// stop transmitting
			{
				rc = 0;
			}
		}
	}
	while (rc != 0 && (!timeout || ((millis() - time) < timeout)));
      #endif

  // ----------------------- finish setup -----------------------
	// display the identification bytes, must be "00 00 A4 20 00 00" for the Nunchuck
	byte i;
	if(readControllerIdent(outbuf) == 0)
	{
		Serial.print("Ident=");
		for (i = 0; i < WII_TELEGRAM_LEN; i++)
		{
			Serial.print(outbuf[i], HEX);
			Serial.print(' ');
		}
		Serial.println();
	}

	Serial.println("Finished setup");

}

boolean NunC_RecieveCommand(){
	Wire.requestFrom (WII_NUNCHUCK_TWI_ADR, WII_TELEGRAM_LEN);	// request data from nunchuck

	for (cnt = 0; (cnt < WII_TELEGRAM_LEN) && Wire.available (); cnt++) 		outbuf[cnt] = nunchuk_decode_byte (Wire.receive ());	// receive byte as an integer 	clearTwiInputBuffer(); 	// If we recieved the 6 bytes, then go print them 	if (cnt >= WII_TELEGRAM_LEN){
          ConvertOutputs();
          return true;
        }
        else                          
          return false;
}

void NunC_SendNextByteRequest()
{
	// I don't know why, but it only works correct when doing this exactly 3 times
	// otherwise only each 3rd call reads data from the controller (cnt will be 0 the other times)
	for(byte i = 0; i < 3; i++)
	{
		Wire.beginTransmission (WII_NUNCHUCK_TWI_ADR);	// transmit to device 0x52
		Wire.send (0x00);	 // sends one byte
		Wire.endTransmission ();	// stop transmitting
	}
}

void print ()
{

     Serial.print (NunC_JX, DEC);
     Serial.print ("t");

     Serial.print (NunC_JY, DEC);
     Serial.print ("t");

     Serial.print (NunC_AX, DEC);
     Serial.print ("t");

     Serial.print (NunC_AY, DEC);
     Serial.print ("t");

     Serial.print (NunC_AZ, DEC);
     Serial.print ("t");

     Serial.print (NunC_BZ, DEC);
     Serial.print ("t");

     Serial.print (NunC_BC, DEC);
     Serial.print ("t");

     Serial.print ("rn");
}

Testing Wii Nunchuck with Arduino

To test the Wii Nunchuck and making sure it’s working properly, we can verify it’s input in the Arduino Serial Monitor. In the Serial Monitor you should be getting data like this.

testing-arduino-with-wii-nunchuck-remote-baud rate

[sourcecode language=”cpp”]

#ifndef NUNCHUCK_H
#define NUNCHUK_H
#include <Wire.h>
#include “nunchuck.h”
#endif

void setup(){

Serial.begin (19200); // Required by Wii Nunchuck
NunC_Init(0); // initialize nunchuck

}

void loop(){

if (NunC_RecieveCommand() == true){

NunC_print ();

}
NunC_SendNextByteRequest(); // send the request for next bytes
delay(10);

}

[/sourcecode]

3 thoughts on “Wii Nunchuck Arduino Tutorial

  1. zegarfield

    OMG :) I heard that we can use some spare parts from Wii tu put them into an arduino project but at this state is it very intresting , and what about sensors in the Wii ? ca we use them too ? I think about gyro , acc …
    Best regards from france :)

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Are you Robot? *

I am moving all supports to this forum IntoFPV.com (at least for now)... So if you are looking for help, or want to help people, I strongly recommend joining this community. Feel free to share your projects, builds and knowledge too!