Read wii nunchuck data into arduino

With a little hacking, data can be read from a wii nunchuck directly into an Arduino, using TWI (aka I2C). The nunchuck contains a 3 axis accelerometer, joystick and buttons for only $19.95. The same accelerometer in kit form, cost $34.95 at Sparkfun. Plus the nunchuck is already wired up in a nice clean case! So the wii nunchuck should fit nicely into anyones robotic project.

Arduino Nunchuck

The nunchuck uses a proprietary connector. I just cut the end off of my nunchuck cable. The cable has 4 wires.
white - ground
red - 3.3+v
green - data
yellow - clock

Attach white to the Arduino's ground, red to 5 volt+, green to analog pin 4, yellow to analog pin 5. The nunchuck is only supposed to get 3.3+ volts. So far it has worked fine at 5 volts, but be warned. I am guessing that using the higher voltage will shorten the nunchucks lifespan.

My Arduino has an Atmel atmega168 chip. The atmega168 can read the TWI protocol. The "Wire" library is bundled with the Arduino IDE and contains a number of libraries that will be used for reading the TWI protocol. A few small changes have to be made to the default wire library. The Arduino's twi.h header needs two changes. Look in lib/targets/libraries/Wire/utility. Then delete twi.o. Open up twi.h. Uncomment line:
// #define ATMEGA8.

Since the nunchuck uses "Fast" I2C, we will need to change the default speed:
#define TWI_FREQ 400000L.

For the Arduino to communicate with the nunchuck, it must send a handshake. So first send 2 bytes "0x40,0x00". Then send one byte "0x00" each time you request data from the nunchuck. The data from the nunchuck will come back in 6 byte chunks.

Byte Description Values of sample Nunchuk
1 X-axis value of the analog stick Min(Full Left):0x1E / Medium(Center):0x7E / Max(Full Right):0xE1
2 Y-axis value of the analog stick Min(Full Down):0x1D / Medium(Center):0x7B / Max(Full Right):0xDF
3 X-axis acceleration value Min(at 1G):0x48 / Medium(at 1G):0x7D / Max(at 1G):0xB0
4 Y-axis acceleration value Min(at 1G):0x46 / Medium(at 1G):0x7A / Max(at 1G):0xAF
5 Z-axis acceleration value Min(at 1G):0x4A / Medium(at 1G):0x7E / Max(at 1G):0xB1
6 Button state (Bits 0/1) / acceleration LSB Bit 0: "Z"-Button (0 = pressed, 1 = released) / Bit 1: "C" button (0 = pressed, 1 = released) / Bits 2-3: X acceleration LSB / Bits 4-5: Y acceleration LSB / Bits 6-7: Z acceleration LSB

see wiili.org

Here is a simple program to read data from the nunchuck. It will read in data from the nunchuck then send it out over the Arduino's serial connection to your PC. On the PC, I just read it in with minicom, but you can use your favorite serial terminal program. The Arduino can't keep up with reading both serial and TWI at the same time. I have the Arduino read in about 50 bytes from the nunchuck then print that back to the PC over the serial connection. This means there will be a slight delay. Also you may see some strange characters every now and then. I think that is because of the serial connection interfering with the TWI connection.

Update: I updated the code some to slow down the serial data transmit and TWI requests. That seems to have fixed the bad data I was seeing.
If you are able to reproduce this hack, please drop me an email at chad AT chadphillips . org

#include <Wire.h>
#include <string.h>

#undef int
#include <stdio.h>

uint8_t outbuf[6];		// array to store arduino output
int cnt = 0;
int ledPin = 13;

void
setup ()
{
  beginSerial (19200);
  Serial.print ("Finished setup\n");
  Wire.begin ();		// join i2c bus with address 0x52
  nunchuck_init (); // send the initilization handshake
}

void
nunchuck_init ()
{
  Wire.beginTransmission (0x52);	// transmit to device 0x52
  Wire.send (0x40);		// sends memory address
  Wire.send (0x00);		// sends sent a zero.  
  Wire.endTransmission ();	// stop transmitting
}

void
send_zero ()
{
  Wire.beginTransmission (0x52);	// transmit to device 0x52
  Wire.send (0x00);		// sends one byte
  Wire.endTransmission ();	// stop transmitting
}

void
loop ()
{
  Wire.requestFrom (0x52, 6);	// request data from nunchuck
  while (Wire.available ())
    {
      outbuf[cnt] = nunchuk_decode_byte (Wire.receive ());	// receive byte as an integer
      digitalWrite (ledPin, HIGH);	// sets the LED on
      cnt++;
    }

  // If we recieved the 6 bytes, then go print them
  if (cnt >= 5)
    {
      print ();
    }

  cnt = 0;
  send_zero (); // send the request for next bytes
  delay (100);
}

// 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
void
print ()
{
  int joy_x_axis = outbuf[0];
  int joy_y_axis = outbuf[1];
  int accel_x_axis = outbuf[2] * 2 * 2; 
  int accel_y_axis = outbuf[3] * 2 * 2;
  int accel_z_axis = outbuf[4] * 2 * 2;

  int z_button = 0;
  int c_button = 0;

 // byte outbuf[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 ((outbuf[5] >> 0) & 1)
    {
      z_button = 1;
    }
  if ((outbuf[5] >> 1) & 1)
    {
      c_button = 1;
    }

  if ((outbuf[5] >> 2) & 1)
    {
      accel_x_axis += 2;
    }
  if ((outbuf[5] >> 3) & 1)
    {
      accel_x_axis += 1;
    }

  if ((outbuf[5] >> 4) & 1)
    {
      accel_y_axis += 2;
    }
  if ((outbuf[5] >> 5) & 1)
    {
      accel_y_axis += 1;
    }

  if ((outbuf[5] >> 6) & 1)
    {
      accel_z_axis += 2;
    }
  if ((outbuf[5] >> 7) & 1)
    {
      accel_z_axis += 1;
    }

  Serial.print (joy_x_axis, DEC);
  Serial.print ("\t");

  Serial.print (joy_y_axis, DEC);
  Serial.print ("\t");

  Serial.print (accel_x_axis, DEC);
  Serial.print ("\t");

  Serial.print (accel_y_axis, DEC);
  Serial.print ("\t");

  Serial.print (accel_z_axis, DEC);
  Serial.print ("\t");

  Serial.print (z_button, DEC);
  Serial.print ("\t");

  Serial.print (c_button, DEC);
  Serial.print ("\t");

  Serial.print ("\r\n");
}

// Encode data to format that most wiimote drivers except
// only needed if you use one of the regular wiimote drivers
char
nunchuk_decode_byte (char x)
{
  x = (x ^ 0x17) + 0x17;
  return x;
}

Update I just tried running my code under Arduino 10. I had to make a different change to twi.h. Here are the first few lines of twi.h
#define ATMEGA8

#ifndef CPU_FREQ^M
#define CPU_FREQ 16000000L
#endif

#ifndef TWI_FREQ^M
#define TWI_FREQ 100000L
#endif

Comments

und3rt4k3r:

Don't know why but i get 255, too...Address 0xA4 should be right. Yesterday it had worked....

Kenn Sebesta:

I'm using a bit-banged i2c driver (found here: http://homepage.hispeed.ch/peterfleury/group__pfleury__ic2master.html#ga4 ) with an STK500 running an Atmega5815, but it doesn't seem to work. Using a logic analyzer, I can see that the Atmega is putting out the right bit pattern, according to my understanding of the i2c docs, but the Wiichuck never responds. Not even with an 'ack' (pulling the data line to low after the master finishes speaking) after sending 0x52. I've tried varying the speed of the data transmission, but to no avail.

I had the same problem using both the internal pull-up resistors in the wiichuck and external 4.7kOhm resistors. It seems that the wiichuck is dead to my i2c world. Except it still works just fine with the wiimote (i.e. I didn't fry nuttin').

Anyone have any ideas? This is really driving me crazy.

Anonymous:

When using I2C bus we address 7 bit devices. if you look at the code again at the hash defines near top youll see your device has address defined as 0xA2..therefore they already have the address shifted one tot he left here berfore adding the read or right bit at the end so what you must do is change this to 0xA4.When reading from the device you should see 0xA5 for the first byte sent and writing to the device will have 0xA4. This is because normally when reading and writing i2c our code handles the shift left before adding the read or write bit which id say yours is not doing.

Anonymous:

I figured out that it was 0xA4 by looking at the wiimote<--wiichuck data flow with the logic analyzer. I decided to google for why this was, and came across your answer! Dang, google is fast.

Strangely, when I monitor the wiimote<--wiichuck data response, there are 8 bytes, not 6, sent after 0xA5. I've taken a picture of the logic analyzer, and hope to have it posted to the wiki at wiili.org as soon as the admin gets around to seeing my email. I'd be interested if anyone can verify this and tell me what this is.

In any case, for the moment, I can't get the bit-banged code to do what I want it to, and assembly is too much of a headache for the moment (I'm new to microprocessors since last Friday) so one thing at a time. I ordered a couple Atmega644p's that have a twi interface built in, so I'll finally get it working.

kelly:

Im using a blackfin evaluation system at present and connected to nunchuck through I2C. I noticed that there maybe errors in the code or posts on your site and was wondering could someone check and see if they are experiencing same issue:

when decoding buttons:

On osccilloscope i get the below truth table for Z and C buttons.

When not active the lsbs of the 6th register read 11.
When Z active the lsbs of the 6th register read 00.
When C active the lsbs of the 6th register read 01.
When Z.C active the lsbs of the 6th register read 10.

therefore the above coding will not give you correct button presses.

Also i would like to know has anyone correctly decoded the acceleration bits. is the 6th register made up to msbs or lsbs and in what order as the outputs dont match what is posted.

kelly

chad:

The button logic should be right on. Debugging the code back to screen I can see for sure when I press the Z and C buttons.

I took my notes from:
http://www.wiili.org/index.php/Wiimote/Extension_Controllers/Nunchuk

Someone at wiili.org did get results like you are saying. If I had the buttons wrong, I would have thought I would have noticed it (I don't have my arduino handy to check).

You may be correct on the acceleration bits. I think someone else reports I had the LSBs reversed on acceleration.

Can you look at the data description at:
http://www.wiili.org/index.php/Wiimote/Extension_Controllers/Nunchuk

and see if it matches what you found?

thanks
chad

kelly:

yes i still have same issue so ill decode them the way im saying above. I managed to get the data for the other five bytes correct after decoding them . They basically drop the data in the 6th byte and dont use it. there for no i get a value on x axis of 74 - 178 y axis 72 - 184 and z 110 - 220 consistently . the joy values are consistent to . ill be happy enough to use these. However i have tried another processor and 8051 based and cant seem to get any data from it.

Back to the drawing board.

ongissim:

I got this working, and have outputted the code to be interfaced with Visual Basic. If anyone is interested, I'll post a tutorial on my website.

Mike:

Great write-up Chad, and it gave me all I needed to use the Nunchuk in my own project. I can control leaning in Call of Duty 4 using the Nunchuk (on my head). You can see a write-up on it here if you're interested. I reference this page a fair bit.

Thanks again!

Joyce:

Thank you for your code. It is great.

I am doing a project which need to have two wiimotes at the same time. Data and clock wire of one wiimotes are attach to analog pin 4 and 5 respectively. However, data and clock wire of another wiimotes need to attach to another analog pin.

I would like to ask how to change the code for the data and clock wire which are not attached to analog pin 4 and 5.

Thanks

evilBunny:

little optimization of the print() function would be something like this :

int joy_x_axis = outbuf[0];
int joy_y_axis = outbuf[1];
int accel_x_axis = (outbuf[2] << 2) + ((outbuf[5] >> 2) & 0x03);
int accel_y_axis = (outbuf[3] << 2) + ((outbuf[5] >> 4) & 0x03);
int accel_z_axis = (outbuf[4] << 2) + ((outbuf[5] >> 6) & 0x03);
int z_button = outbuf[5] & 1;
int c_button = outbuf[5] & 2;

geting a nunchuck tomorrow and write some plain avrlibc examples :D

Steven Hepting:

Wow, I got *exactly* the same code as you (down to the brackets even). Same shift, same AND with 0x03. Nice job.

Anyways, the only thing you want to do different is that c_button code. Right now it'll assign the value 0x02 to c_button if it's true and you want 1. Just use:

c_button = (outbuf[5] >> 1) & 0x01;

chad:

Sounds cool. Be sure and post a link when you have your examples done.

evilBunny:

well :) got my nunchuk today and got a test version of some software running. must say out put is pretty jittery.
ow and there is no need to cut the connector off. made a neat male part using a sawed of piece of a ISA card 3 contacts wide and a bit of sanding. will post some pictures somewhere in a bit , but it works really great :)

BTW what is the time "delay(100);" cause ?

hmm and in your code (almost got the winavr code done)

doesn't

if ((outbuf[5] >> 2) & 1)
{
accel_x_axis += 2;
}
if ((outbuf[5] >> 3) & 1)
{
accel_x_axis += 1;
}

reverse the lower lsb and msb ?

Paul:

Hi,

does the code now calculate some values wrong because lower lsb and msb are reversed? If so, how should the code be?

Paul

Mike:

Yeah, his LSBs on acceleration are reversed.

weak:

So, this means that somebody could manufacture adaptors that connect the nunchuk or the classic controller to usb as a gamepad, right? Or maybe an adaptor that connects the classic controller directly to the gamecube port on the wii? Are such devices availible comercially?

Anonymous:

I have a project that I would like to use the Wiimote on however, I would like to use the Wiimote without a PC, using a microcontroller instead.

Has anyone tested the I2C port on the Wiimote to see if you can get the IR camera information directly? Or does anyone have any other ideas on how to interface the Wiimote with a microcontroller? I know you can directly interface the Nunchuck to a microcontroller, but I have not found anything that allows me to receive the IR camera data via I2C.

chad:

You can talk to the wiimote over I2C. But I have only been able to get the wiimote to relay commands from a PC to the expansion port/microcontroller. Basically the PC tells the wiimote to send data to the expansion port.

I haven't figured out a way to directly read the IR camera data over the expansion port. My guess is it is not possible. From everyones reverse engineering, the expansion port is only able to read a small memory area in the wiimote. ( http://www.wiili.org/index.php/Wiimote/Extension_Controllers/Nunchuk )

Without a PC, you can't tell the wiimote what to put into that memory area. Hardware wise it is probably possible, but software wise, I don't think the wiimotes firmware has anything built in to send the IR data out through the expansion port.

evilBunny:

The wiimote is the master on the i2c bus and its unlikely they built in support for multimasters on this bus. So then there will be no way to start any data transfer to it because a slave just isn't allowed to start a transfer. :(

chad:

Right. What you would need to do is somehow rewrite the wiimotes firmware. Then have the wiimote initiate the communication every so often.

I did something similar with my firefighting robot . My PC commands the wiimote to start a communication to the expansion port. So all communication has to start with the wiimote because it is the master. So far the only known way to control the wiimote, is to send blue tooth commands to the wiimote.

evilBunny:

seems the cam could be on the i2c bus to some sources. great now i also need a remote ;) and all the ninchuk's are out of stock around here in the Netherlands :(

ow well we will have to wait ^^

chad:

If you took the wiimote apart, you might could read the camera directly over its I2C bus. Would probably be tricky and the odds of completely killing the wiimote high.

Take a look at
http://www.wiili.org/index.php/Talk:Wiimote

evilBunny:

yeah did some reading up the cam is i2c BUT on a different bus . so unless one could run your own code on the broadcom the only way around it would be to merge the bus via some soldering ^^ . mostlikely they used to buses because of the high throughput of data that the camera generates.
bit of a shame :)

Jon McPhalen:

I've been able to run Chad's code on the Arduino but have had no success duplicating it on the SX28. The SX28 is very fast and has incredibly-tight timing so I'm wondering if the device needs some kind of timing between bytes. Any suggestions or comments in this regard are appreciated.

chad:

I don't know for sure. I am guessing that the timeing issue is really only important for the I2C communications. I am pretty sure the SX28 does I2C. So that timing should be ok.

My guess is that it is a code problem on the PIC. I know another got it working on a PIC. You might look at his comment: http://www.windmeadow.com/comment/reply/42/1794

If you do get it working, can I post your code along with my arduino code?

Jon McPhalen:

After reviewing Edward Holets' code and connecting a logic analyzer to the [working] Arduino, I decided to pad between bytes when reading from the nunchuck and that helps, though only the Y joystick value seems reliable. Thoughts?

' Use: GET_NUNCHUCK
' -- read nunchuck values into array "joystick"

SUB GET_NUNCHUCK
   I2C_START
   I2C_OUT $A4
   I2C_OUT $00
   I2C_STOP

   DELAY_MS 2

   I2C_START
   I2C_OUT $A5
   DELAY_US 10
   FOR tmpB1 = 0 TO 4
   DELAY_US 10
      joystick(tmpB1) = I2C_IN Ack
   NEXT
   DELAY_US 10
   joystick(5) = I2C_IN Nak
   I2C_STOP
   ENDSUB

Ryan:

I am also trying to get this working on a SX28. Everything seems fine, but with one major exception: only the first of the 6 bytes, the stick x value, is accurate. The second byte (stick y) changes when touching the stick, but with no pattern (it appears as if only bit 6 is actually changing). The other bytes are always constant. In the neutral position i will get "D9 7F BF FF FF FF". The "D9" is the good value (Byte 0). Some (fruitless) debugging steps i have tried are: (1) Buffering all 6 bytes before sending it to my computer via serial (as opposed to sending each byte right after i read it from the nunchuck). I figured it might be some timing issue where the serial commands were taking too much time between bytes. (2) Sending a nAck after reading my sixth byte (like the i2c spec says). Nothing related to Ack's and nAck's seemed to change anything. (3) Reading more than 6 bytes per block (just curious). All additional bytes were FF. (4) Changing the value of my pull-up resistors (why not try, right?).

Here's my code. Its in SX-Basic.

DEVICE SX28, OSCHS2, TURBO, STACKX, OPTIONX
FREQ 50_000_000
PROGRAM Main

writeAddr con $A4
readAddr con $A5

readBuf var byte
byteCtr var byte

Init sub 0
BeginRead sub 0
DoRead sub 0

Init:
i2cstart ra.0
i2csend ra.0, writeAddr
i2csend ra.0, $40
i2csend ra.0, $00
i2cstop ra.0
return

BeginRead:
i2cstart ra.0
i2csend ra.0, writeAddr
i2csend ra.0, $00
i2cstop ra.0
return

DoRead:
i2cstart ra.0
i2csend ra.0, readAddr

byteCtr = 6
\:byteLoop
i2crecv ra.0, readBuf, 1
serout rb.0, T115200, readBuf
djnz byteCtr, :byteLoop

i2cstop ra.0
return

Main:
Init
pause 100
do
BeginRead
pause 1
DoRead
pause 500
loop
goto Main

chad:

What speed is the I2C bus running at? I think the nunchuck likes 400kz.

Jon McPhalen:

I've tried at 100 kHz and 400 kHz -- the results are the same. And on connecting a logic analyzer to my Arduino I find it's running at 100 kHz and having no trouble. I'm sure it's a weird timing thing between commands, I just don't know where that is. The SX is much faster the Arduino, and that is somehow contributing to its success with this device.

FWIW, I connected loads of I2C devices to the SX28 without any problem at all. Things get tricky with devices like this that aren't officially documented. Still, I'd like to make it work and include it -- with credit to you, of course, Chad -- in my book, "Practical SX/B" that I'm writing for Parallax.

chad:

Do you have a link to some documentation for the SX? I looked on the parrallax site but didn't see docs on I2C for the SX. Maybe I missed them.

littleliu:

0xa4 write f0 55
0xa4 write fb 00
0xa4 write fa
0xa4 read 00 00 a4 20 00 00
0xa4 write f0 aa
0xa4 write 40 7a f7 1b 99 03 a2
0xa4 write 46 ab f9 44 3d e7 68
0xa4 write 4c 8a 75 5c fa
0xa4 write 20
0xa4 read 0x80,0xd4,0xa2,0x70,0xa1,0xd8,0x29,0x30
0xa4 read 0x2d,0x79,0x98,0x56,0x32,0x8d,0xeb,0x4d
0xa4 write 30
0xa4 read 0x80,0xd4,0xa2,0x70,0xa1,0xd8,0x29,0x30
0xa4 read 0x2d,0x79,0x98,0x56,0x32,0x8d,0xeb,0x4d

0xa4 write 0x00
0xa4 read 0x8f,0xd0,0xcc,0xeb,0x9c,0x13

Hi. Dear All. Above is the data what I get the nunchuk and wiimote I2C. I don't know the relation between the 0x40(wii sent 16byte key) and the return 0x20(nunchuk return 16 bytes). Below is the 0x00-0xff data what use the logic analyse write the nunchuk 00-ff data. Who know this relation? Let's discuss together! cokeliu@ev-sparkle.com

0x 0---0x81, 0x81, 0xad, 0x7f, 0x7b, 0xbf, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0,
0x10---0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7c,
0x20---0x7c, 0x7d, 0x7b, 0x 9, 0xaf, 0xae, 0xad, 0x13, 0xdf, 0x18, 0x81, 0xe3, 0x20, 0x81, 0xeb, 0x40,
0x30---0x7c, 0x7d, 0x7b, 0x 9, 0xaf, 0xae, 0xad, 0x13, 0xdf, 0x18, 0x81, 0xe3, 0x20, 0x81, 0xeb, 0x40,
0x40---0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x50---0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x60---0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x70---0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x80---0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x90---0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xa0---0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xb0---0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xc0---0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xd0---0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xe0---0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xf0---0x55, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0xa4, 0x20, 0x 0, 0x 0,

Anonymous:

Hi there.

Your dump is very useful. My understanding:

> 0xa4 write f0 55
Wiimote wrote 55 -> Register F0H (not sure the exact meaning, but must be part of initial sequence)

> 0xa4 write fb 00
Wiimote wrote 00 -> Register FBH (same as above)

> 0xa4 write fa
Wiimote requested to read 6 bytes starting from FAH for device ID (no need to specify length but always stop at 16-byte boundary?)

0xa4 read 00 00 a4 20 00 00
> Nunchuck returned its device ID (at least for last two bytes 00 00)

> 0xa4 write f0 aa
Wiimote wrote AA -> Register F0H (mode switching?)

> 0xa4 write 40 7a f7 1b 99 03 a2
> 0xa4 write 46 ab f9 44 3d e7 68
> 0xa4 write 4c 8a 75 5c fa
Wiimote wrote 16 bytes into Registers starting from 40H (key?)

> 0xa4 write 20
Wiimote wanted to read calibration data

> 0xa4 read 0x80,0xd4,0xa2,0x70,0xa1,0xd8,0x29,0x30
> 0xa4 read 0x2d,0x79,0x98,0x56,0x32,0x8d,0xeb,0x4d
Nunchuk returned Calibration data

> 0xa4 write 30
Wiimote wanted to read calibration data (copy 2?)

> 0xa4 read 0x80,0xd4,0xa2,0x70,0xa1,0xd8,0x29,0x30
> 0xa4 read 0x2d,0x79,0x98,0x56,0x32,0x8d,0xeb,0x4d
Nunchuk returned Calibration data (copy 2?)

As a side note: I had difficulty getting various wiimote driver to work with Nyko Frontman Guitar. But with the initialization sequence you dumped, I am able to get the correct device ID just like with official Wii guitars. Very excited after two weeks of struggling.

Aristotle:

I found a pinout here http://www.hardwarebook.info/Wiimote_Expansion_Port so rather than cut the cable I plugged jumper wires into the connector.

Depending on how I wire it, it either hangs, or returns the following values
46 46 187 185 184 0 1
over and over

I'm using arduino decimilia, arduino alpha 0010

Jospfh:

I bought a Nunchuck extension cable from ebay. So I now have a Male and female connector and do not have to open the wiimote or hack the nunchuck for my experiments!

chad:

Excellent! I didn't even know they made those.

Aristotle:

read my post below, the ascii art is pretty poor, but I have the pinout corrected.

I'm using alpha 0010 as well

Aristotle:

I decided to go the less invasive route, no cutting. I opened up the connector to figure out the pinout, and then put it back together.

On the connector there is a small hole by each pin, perfect for jumpers. Here is the pinout, with the connector facing you, like you are going to plug it into your eyeball ;)

|~~_____~~|
| 6 4 2 |
| ------ |
| 5 3 1 |
\_________/

1 = green
2 = white
3 = red
6 = yellow

Beyond that the only change I made to Chads code as setting. beginSerial (115200), however 19200 seems to work as well.

No changes to the twi.h, I'm using 3.3v on the red lead.

Thanks Chad for this cool code.

Is there a way to translate the acceleration data into tilt data?

littleliu:

Address data
-------------------------------------
0xa4 write f0 55
0xa4 write fb 00
0xa4 write fa
0xa4 read 00 00 a4 20 00 00
0xa4 write f0 aa
0xa4 write 40 7a f7 1b 99 03 a2
0xa4 write 46 ab f9 44 3d e7 68
0xa4 write 4c 8a 75 5c fa
0xa4 write 20
0xa4 read 0x80,0xd4,0xa2,0x70,0xa1,0xd8,0x29,0x30
0xa4 read 0x2d,0x79,0x98,0x56,0x32,0x8d,0xeb,0x4d
0xa4 write 30
0xa4 read 0x80,0xd4,0xa2,0x70,0xa1,0xd8,0x29,0x30
0xa4 read 0x2d,0x79,0x98,0x56,0x32,0x8d,0xeb,0x4d

0xa4 write 0x00
0xa4 read 0x8f,0xd0,0xcc,0xeb,0x9c,0x13

Hi. Dear All. Above is the data what I get the nunchuk and wiimote I2C. I don't know the relation between the 0x40(wii sent 16byte key) and the return 0x20(nunchuk return 16 bytes). Below is the 0x00-0xff data what use the logic analyse write the nunchuk 00-ff data. Who know this relation? Let's discuss together! cokeliu@ev-sparkle.com

0x 0---0x81, 0x81, 0xad, 0x7f, 0x7b, 0xbf, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0,
0x10---0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7c,
0x20---0x7c, 0x7d, 0x7b, 0x 9, 0xaf, 0xae, 0xad, 0x13, 0xdf, 0x18, 0x81, 0xe3, 0x20, 0x81, 0xeb, 0x40,
0x30---0x7c, 0x7d, 0x7b, 0x 9, 0xaf, 0xae, 0xad, 0x13, 0xdf, 0x18, 0x81, 0xe3, 0x20, 0x81, 0xeb, 0x40,
0x40---0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x50---0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x60---0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x70---0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x80---0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x90---0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xa0---0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xb0---0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xc0---0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xd0---0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xe0---0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xf0---0x55, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0x 0, 0xa4, 0x20, 0x 0, 0x 0,

Xo W.:

I got the code to work on a PIC 18F2550 running at 48MHz on 3.3V and 300kHz I2C. I used Microchip's Student Edition C18 compiler and its library (the documentation is horrible) for controlling the hardware I2C on the chip. At 3.3V (I'm using a PICKit 2 programmer, which provides power from USB at 2.5V to 5.0V), the highest speed I got was 300kHz. Any higher and it would begin to output garbage. Also, I used only 1900 cycles, or about 158 microseconds, of delay between sending the 0x00 and reading data. 1800 cycles would not work.

Huge thanks to chad for the wiring, protocol, and sample code and Edward for the required delays and the lower-level code I needed for the PIC's MMSP module.

I can not post my code; I apologize in advance for anyone who needs it.

-Xo W.

sean:

I'm a bit of a newbie to all this, however I've successfully got this hack to work and am now keen on sending out the nunchuck's sensor data via midi. Tod Kurt did some great work with his recent 'Bionic Arduino' class and so I contacted him and he graciously replied. The probelm is that I may not have made it clear enough how much of a beginner I am. Anyway, I've begun modifying his version of the above code but I was wondering if anyone else has tried to send MIDI data from the nunchuck? and if so would they like to help me achieve results?

/* Nunchuck to MIDI
*
* Based upon:
* NunchuckServo 2007 Tod E. Kurt, http://todbot.com/blog/
*
* The Wii Nunchuck reading code is taken from Windmeadow Labs
* http://www.windmeadow.com/node/42
*/

#include

#define middleC 60
#define LEDpin 13

//Variables
int ledPin = 13;

void setup()
{
Serial.begin(31250); //baud rate for MIDI

nunchuck_init(); // send the initilization handshake
Serial.print ("Finished setup\n");
}

void loop()
{

//THIS IS WHERE I NEED TO 'CAPTURE' THE NUNCHUCK VALUES

nunchuck_get_data();
nunchuck_print_data();
delay(100);
}

//
// Nunchuck functions
//

static uint8_t nunchuck_buf[6]; // array to store nunchuck data,

// initialize the I2C system, join the I2C bus,
// and tell the nunchuck we're talking to it
void nunchuck_init()
{
Wire.begin(); // join i2c bus as master
Wire.beginTransmission(0x52); // transmit to device 0x52
Wire.send(0x40); // sends memory address
Wire.send(0x00); // sends sent a zero.
Wire.endTransmission(); // stop transmitting
}

// Send a request for data to the nunchuck
// was "send_zero()"
void nunchuck_send_request()
{
Wire.beginTransmission(0x52); // transmit to device 0x52
Wire.send(0x00); // sends one byte
Wire.endTransmission(); // stop transmitting
}

// Receive data back from the nunchuck,
int nunchuck_get_data()
{
int cnt=0;
Wire.requestFrom (0x52, 6); // request data from nunchuck
while (Wire.available ()) {
// receive byte as an integer
nunchuck_buf[cnt] = nunchuk_decode_byte(Wire.receive());
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
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 += 2;
if ((nunchuck_buf[5] >> 3) & 1)
accel_x_axis += 1;

if ((nunchuck_buf[5] >> 4) & 1)
accel_y_axis += 2;
if ((nunchuck_buf[5] >> 5) & 1)
accel_y_axis += 1;

if ((nunchuck_buf[5] >> 6) & 1)
accel_z_axis += 2;
if ((nunchuck_buf[5] >> 7) & 1)
accel_z_axis += 1;

Serial.print(i,DEC);
Serial.print("\t");

Serial.print("joy:"); //tag
Serial.print(joy_x_axis,DEC); //x axis value
Serial.print(","); //next value
Serial.print(joy_y_axis, DEC); //y axis value
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++;
}

// Encode data to format that most wiimote drivers except
// only needed if you use one of the regular wiimote drivers
char nunchuk_decode_byte (char x)
{
x = (x ^ 0x17) + 0x17;
return x;
}

// returns zbutton state: 1=pressed, 0=notpressed
int nunchuck_zbutton()
{
return ((nunchuck_buf[5] >> 0) & 1) ? 0 : 1; // voodoo
}

// returns zbutton state: 1=pressed, 0=notpressed
int nunchuck_cbutton()
{
return ((nunchuck_buf[5] >> 1) & 1) ? 0 : 1; // voodoo
}

// returns value of x-axis joystick
int nunchuck_joyx()
{
return nunchuck_buf[0];
}

// returns value of y-axis joystick
int nunchuck_joyy()
{
return nunchuck_buf[1];
}

// returns value of x-axis accelerometer
int nunchuck_accelx()
{
return nunchuck_buf[2]; // FIXME: this leaves out 2-bits of the data
}

// returns value of y-axis accelerometer
int nunchuck_accely()
{
return nunchuck_buf[3]; // FIXME: this leaves out 2-bits of the data
}

// returns value of z-axis accelerometer
int nunchuck_accelz()
{
return nunchuck_buf[4]; // FIXME: this leaves out 2-bits of the data
}

chad:

I don't know anything about MIDI, but here is my shot in the dark. If you want to use just the nunchuck and Arduino, here is the data flow. The nunchuck sends its sensor data to the Arduino over I2C. The Arduino then relays this data to your PC over its serial connection. You would then have to write your own program on the PC side to read that in an put it into a MIDI program. The last part I have no idea how to do, since I have never worked with MIDI.

Some other people have already got the wiimote to work with MIDI, you could probably look at their code for an example. Of course they get their data from blue tooth rather than the Arduinos serial connection.
http://crustea.vjfrance.com/article-130714.html
http://createdigitalmusic.com/2006/12/07/midi-control-for-music-with-wii-remote-teaser/
http://screenfashion.org/2007/04/the_wiinstrument.html (uses both nunchuck and wiimote)

good luck!

Sean:

Yeah, thats the way I was beginning to lean. To be honest though I just want to interpret the serial data in Max/MSP. I can convert it into midi data from there quite easily as, in contrast to my arduino programing skills, I'm quite good with Max!

The trouble is, if I print the serial data in Max it just sends out a flood of numbers that are meaningless. Values for each sensor on the 'chuck'. So I need to 'label' each of the sensors data (accelerometer z axis, accelerometer x axis... etc) within the arduino program in a way that Max can read.

Somebody asked that exact question on the arduino forums here:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1134496621
and the solution appears to be to use the 'serialWrite' command. The trouble is, I'm so crap with writing arduino code that I can't figure out how to implement it into this sketch.

I'm guessing it means replacing this somehow:

Serial.print(i,DEC);
Serial.print("\t");

Serial.print("joy:"); //tag
Serial.print(joy_x_axis, DEC); //x axis value
Serial.print(","); //next value
Serial.print(joy_y_axis, DEC); //y axis value
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++;

what say you?

chad:

Yep, that is the code you would need to update. so for this piece of code:
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");

Something like "acc:190,200,233" would be sent across the serial connections. To make the serial data easier to parse you might change that code to:
Serial.print("accel_x_axis=");
Serial.print(accel_x_axis, DEC);

Serial.print(",accel_y_axis=");
Serial.print(accel_y_axis, DEC);

Serial.print(",accel_z_axis=");
Serial.print(accel_z_axis, DEC);

Then you could parse each key/value pair. Is that kind of what you were asking for?

I think that Serial.print and serialWrite do that same thing or pretty similar.

Edward:

So I just finished getting this hack working on a PIC24F. Thanks a lot chad and everyone else for sharing your knowledge!

For a while I was having the same problem others have reported here with the nunchuck always replying with 0xFF. The solution was a ~20ms delay prior to sending the initialization request (0x40, 0x00), and then a ~1 ms delay between sending the request byte (0x00) and reading the data was required. Those times may be high, as I haven't spent much time trying to find the true minimums. The Arduino code probably runs slow enough between those routines that it fulfills the timing requirements w/o extra delay.

Anyways, I wanted to share that, in case it helps someone avoid hours of needless debuggin. Also, I will post my sample code later today or tomorrow in case anyone is interested.

Jerry:

Hi Edward,
I would certainly be interested in seeing that sample code for PIC.
Thanks in advance!

Edward:

Here ya go:
http://www.edwardholets.com/downloads/src/nunchuck_sample_pic24f/main

Nothing fancy. 100kHz I2C at 3.3v. Uses the nunchuck data to control some LEDs. Hope it helps.

Jerry:

Thanks a lot! I always helps to have more sources to study :-)

I did some more searching and came up with this:
http://uncledim.narod.ru/wcc2gcen.html
Complete nunchuck & classic controller intrafacing
using PIC and converting to GameCube protocol
at the same time. Interesting stuff!

Anonymous:

Anyone get this to work on a PIC? I'm on the CCS forums trying to get the I2C stuff to work (http://www.ccsinfo.com/forum/viewtopic.php?t=32753)

If anyone has suggestions please post them!
-Daniel

Anonymous:

When using CCS PIC-C use address 0xA4 to write and 0xA5 to read.

Post new comment

The content of this field is kept private and will not be shown publicly.
CAPTCHA
This question is used to make sure you are a human visitor and to prevent spam submissions.
Image CAPTCHA
Enter the characters shown in the image.