Arduino Nunchuk reading

Hi!
Some of you might know the Code from Windmeadow

http://www.windmeadow.com/node/42

#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;
}

I tried it (and many similar versions from the comment people)
and it didn't work for me.
It showed me the error Code:

o: In function setup': C:\Users\Florian\AppData\Local\Temp\build5836836406551647758.tmp/sketch_mar29a.cpp:46: undefined reference to beginSerial'

I changed "beginSerial into Serial.begin and the error was gone....but still nothing worked.
Just the "finished setup" showed up, nothing more.
The "finished setup" comes when the nunchuk is disconected.
i use 5V and two 1.5K(i tried 1.65K too) pullup res. from clock and data to 5V.

I changed the tiwi.h a little but still nothing changed.
in arduino 18 there is no tiwi.o. to deled, right?

Does some has an idea and can help me?

I am lost... :cry:

Florian

Does noone has any idea?

The Nunchuk uses 3V3 and the arduino (most of them) uses 5V.
Have you powered the Nunchuk separately?
Have you something in the I2C lines to translate the voltages?
Or is it all working off 3v3?

I tried it with the 3.3 volt output and the 5 volt ouput of the arduino.
The arduino 2009 itself uses 5V.
eitherway didn't work out.

But yay I have an update!!!

When I tried this Code it showed the axis and the other information...
33 joy:0,0 acc:0,0,0 but:0,0
but only If the nunchuck isn't pluged in.
It stops as soon the clock or Data cable is pluged into pin 4 or pin5

any ideas???

/*
 * NunchuckPrint
 *
 * 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 <Wire.h>

void setup()
{
  Serial.begin(19200);
  nunchuck_setpowerpins(); // use analog pins 2&3 as fake gnd & pwr
  nunchuck_init(); // send the initilization handshake
  Serial.print ("Finished setup\n");
}

void loop()
{
  nunchuck_get_data();
  nunchuck_print_data();
  delay(100);
}


//
// Nunchuck functions
//

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
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:");
  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++;
}

// 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;
}

I have the nunchuck running at 5 volts. I read somewhere that it can handle it, and it seems to be true.

But I was also struggling for a while to get it talking. I'm trying to remember what made it work finally. Might post again when the blackout is over :wink:

If you have an ocilloscope, try to find out what's going on on the pins.

Alright, let's get this working:

  1. it's not tiwi.h, it's twi.h
  2. Windmeadow's twi.h change was for older arduino versions. For arduino 18, I've found it's not needed anymore (keep twi_freq at 100000)
  3. I have a wii nunchuck library that will simplify everything for you, I'll have it posted by tonight. Give it a try and see what happens.
  4. I've never successfuly used any analog/digital pins on the arduino as a Ground source when wrting a 0v/LOW value, might want to avoid that (I saw that in your code somewhere).

Double check your Data/Clock wires. Data(green) should be on arduino's analog pin 4. Clock(yellow) should be on analog pin 5.

And if your using the wiichuck adaptor, SDL is Data, SCL is clock.
I'll have that library up shortly.

By the way, my twi_freq looks like this:

#ifndef TWI_FREQ
#define TWI_FREQ 100000L
#endif

This first code is the library. Save this code in a file called 'WiiChuck.h' then place the file accordingly:

arduino 18\libraries\WiiChuck\WiiChuck.h

/*
 * Nunchuck -- Use a Wii Nunchuck
 * Tim Hirzel http://www.growdown.com
 * 
 notes on Wii Nunchuck Behavior.
 This library provides an improved derivation of rotation angles from the nunchuck accelerometer data.
 The biggest different over existing libraries (that I know of ) is the full 360 degrees of Roll data
 from teh combination of the x and z axis accelerometer data using the math library atan2. 

 It is accurate with 360 degrees of roll (rotation around axis coming out of the c button, the front of the wii),
 and about 180 degrees of pitch (rotation about the axis coming out of the side of the wii).  (read more below)

 In terms of mapping the wii position to angles, its important to note that while the Nunchuck
 sense Pitch, and Roll, it does not sense Yaw, or the compass direction.  This creates an important
 disparity where the nunchuck only works within one hemisphere.  At a result, when the pitch values are 
 less than about 10, and greater than about 170, the Roll data gets very unstable.  essentially, the roll
 data flips over 180 degrees very quickly.   To understand this property better, rotate the wii around the
 axis of the joystick.  You see the sensor data stays constant (with noise).  Because of this, it cant know
 the difference between arriving upside via 180 degree Roll, or 180 degree pitch.  It just assumes its always
 180 roll.


 * 
 * This file is an adaptation of the code by these authors:
 * Tod E. Kurt, http://todbot.com/blog/
 *
 * The Wii Nunchuck reading code is taken from Windmeadow Labs
 * http://www.windmeadow.com/node/42
 */

#ifndef WiiChuck_h
#define WiiChuck_h

#include "WProgram.h" 
#include <Wire.h>
#include <math.h>


// these may need to be adjusted for each nunchuck for calibration
#define ZEROX 515  
#define ZEROY 515
#define ZEROZ 515
#define RADIUS 210  // probably pretty universal

#define DEFAULT_ZERO_JOY_X 124
#define DEFAULT_ZERO_JOY_Y 132



class WiiChuck {
    private:
        byte cnt;
        uint8_t status[6];            // array to store wiichuck output
        byte averageCounter;
        //int accelArray[3][AVERAGE_N];  // X,Y,Z
        int i;
        int total;
        uint8_t zeroJoyX;   // these are about where mine are
        uint8_t zeroJoyY; // use calibrateJoy when the stick is at zero to correct
        int lastJoyX;
        int lastJoyY;
       // int angles[3];

        boolean lastZ, lastC;


    public:
int angles[3];
        byte joyX;
        byte joyY;
        boolean buttonZ;
        boolean buttonC;
        int quadXY, quadXZ,quadYZ, quadYX, quadZX, quadZY;
        

      void begin() 
        {
            Wire.begin();
            cnt = 0;
            averageCounter = 0;
            Wire.beginTransmission (0x52);      // transmit to device 0x52
            Wire.send (0x40);            // sends memory address
            Wire.send (0x00);            // sends memory address
            Wire.endTransmission ();      // stop transmitting
            update();            
            for (i = 0; i<3;i++) {
                angles[i] = 0;
            }
            zeroJoyX = DEFAULT_ZERO_JOY_X;
            zeroJoyY = DEFAULT_ZERO_JOY_Y;
        }


        void calibrateJoy() {
            zeroJoyX = joyX;
            zeroJoyY = joyY;
        }

        void update() {

            Wire.requestFrom (0x52, 6);      // request data from nunchuck
            while (Wire.available ()) {
                // receive byte as an integer
                status[cnt] = _nunchuk_decode_byte (Wire.receive()); //
                cnt++;
            }

            if (cnt > 5) {
                lastZ = buttonZ;
                lastC = buttonC;
                //lastJoyX = readJoyX();
                //lastJoyY = readJoyY();


                cnt = 0;
                //joyX = (status[0]);
                //joyY = (status[1]);
                for (i = 0; i < 3; i++) 
                    //accelArray[i][averageCounter] = ((int)status[i+2] << 2) + ((status[5] & (B00000011 << ((i+1)*2) ) >> ((i+1)*2))); 
                    angles[i] = (status[i+2] << 2) + ((status[5] & (B00000011 << ((i+1)*2) ) >> ((i+1)*2))); 
                        
                //accelYArray[averageCounter] = ((int)status[3] << 2) + ((status[5] & B00110000) >> 4); 
                //accelZArray[averageCounter] = ((int)status[4] << 2) + ((status[5] & B11000000) >> 6); 

                buttonZ = !( status[5] & B00000001);
                buttonC = !((status[5] & B00000010) >> 1);
                


                _send_zero(); // send the request for next bytes

            }
        }


    // UNCOMMENT FOR DEBUGGING
    //byte * getStatus() {
    //    return status;
    //}

    float readAccelX() {
      
        return (float)angles[0] - ZEROX;
    }
    float readAccelY() {
                return (float)angles[1] - ZEROY;
    }
    float readAccelZ() {
                return (float)angles[2] - ZEROZ;
    }

    boolean zPressed() {
        return (buttonZ && ! lastZ);
    }
    boolean cPressed() {
        return (buttonC && ! lastC);
    }

    // for using the joystick like a directional button
    boolean rightJoy(int thresh=60) {
        return (readJoyX() > thresh and lastJoyX <= thresh);
    }

    // for using the joystick like a directional button
    boolean leftJoy(int thresh=60) {
        return (readJoyX() < -thresh and lastJoyX >= -thresh);
    }


    int readJoyX() {
        return (int) joyX - zeroJoyX;
    }

    int readJoyY() {
        return (int)joyY - zeroJoyY;
    }




    // R, the radius, generally hovers around 210 (at least it does with mine)
    int R() {
        return sqrt(readAccelX() * readAccelX() +readAccelY() * readAccelY() + readAccelZ() * readAccelZ());  
    }

    // returns roll degrees

int readRoll()
{
return (int) (atan2(readAccelX(), readAccelZ())/ M_PI * 180.0);

}
int readPitch()
{

      return (int) (180/M_PI)*acos(readAccelY()/R()); 
      

}



// returns yaw in degrees
    int readyaw() {        
return (int) (180/M_PI)*atan2(sqrt(readAccelY()*readAccelY()+readAccelZ()*readAccelZ()), readAccelX());
  
}
    private:
        byte _nunchuk_decode_byte (byte x)
        {
            x = (x ^ 0x17) + 0x17;
            return x;
        }

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

};


#endif

The 2nd code will help you find out if it is working. When you push the Z button on the wii nunchuck, the on-board led located at pin-13 on the arduino will turn off. when Z button is not pressed the button will stay lit.

//Very simple demonstration of the WiiChuck Class Library
//pushing the Z button will turn the onboard LED on pin 13 OFF
//JohnnyOnTheSPot
//ohmwardbond.blogspot.com


#include <WiiChuck.h>
#include <Wire.h>

WiiChuck chuck = WiiChuck(); //Initiates the object name shoulder as a WiiChuck class


void setup()
{
 chuck.begin(); // Initiates both sensors
 chuck.update();
}


void loop()
{
delay(1);
 chuck.update(); // Updates the Arduino to the current Nunchuck reading

   if (chuck.buttonZ == 1)
  {
    digitalWrite(13, LOW);
  }

  else
  {
   digitalWrite(13, HIGH); 
  }
  
}

This library makes it easier to use the nunchuck in my opinion. For example, if you wanted to read the accelerometer's X-axis, do this in your arduino sketch:

int accel_x;
accel_x = chuck.readAccelX();

reading some of the functions in the library will help

Looks quiete nice :slight_smile:
I tried it and it the LED just lid up when the pins are disconected.
I will ask a friend if I can borrow his to see if mine is brocken. I have a strong feeling that it is....

As soon I have news I will tell you !

Thanks a lot for that nice code anyway :slight_smile:

Okay...
I tried an other nunchuck and It was totally the same outcome :cry:

still the same:

have the clock and date pluged in and nothing works. If I unplug it the serial monotior does this:

accx: 0 accy: 0 zbut: 1 cbut: 1
accx: 0 accy: 0 zbut: 1 cbut: 1
accx: 0 accy: 0 zbut: 1 cbut: 1
accx: 0 accy: 0 zbut: 1 cbut: 1

I tried 4 diffrent codes and all were mostly the same.

I am getting creasy!
Some one help plz!!

the german nunchuk shouldn't be so much diffrent, shouldn't it??

I bought the nunchuck in Switzerland, so the German one should work as well.
Some people say you have to use pull up resistors. I didn't have to, but you could try.
Did you look at the wires with an oscilloscope?

tried it with and without pullups.
There is sadly no oscilloscope in my reach.
Could it have anything to do with my Arduino 18 IDE?

I also use the 18 IDE, once installed manually, and once through the ubuntu ppa.
Do you have another i2c device that you could test with? A BlinkM is very good for that purpose.

I did conect a ATMega8 over the I²C and that worked fine. I did some blink LED and did send the ON-OFF comands from the atmega328 to the atmega8.

And that still works when the nunchuck is connected parallel?
Maybe the nunchuck works better if there is another device parallel to it.

I'm trying to remember what problems I had to get the nunchuck running. And I have the feeling it started to get better once I connected other devices parallel.

At the moment I have an electronic compass and the nunchuck in parallel. The setup seems pretty reliable.

No, I haven't tried running two I²C objetcs together.
I did put the atmega8 to the I²C . It has right now a standard blink on it.
Nothing changed but the code still runs if I only put the atmega8 on the line.

Is it important to have the wire code one the atmeg8?

Hi,
I did this last year so I did it again and the code you posted simply works (arduino 0018). You don't need pullups, and there is also no need to change anything in the Wire lib.

Are you absolutely sure you got the wiring right?

I can't think of anything else than a broken Nunchuck.

Eberhard

If you are using a clone nunchuck, perhaps the wire colors used differ from the official wii version.

This diagram shows the physical connections:

edit: changed the link to show the nunchuck end instead of the wiimote end.

grrrr...
I always used this instructable to wire the nunchuck because i didn't know the cable colors.

Now it does run while the nunchuck is pluged in.
but sadly just like that:

accx: 255 accy: 255 zbut: 0 cbut: 0
accx: 255 accy: 255 zbut: 0 cbut: 0
accx: 255 accy: 255 zbut: 0 cbut: 0
accx: 255 accy: 255 zbut: 0 cbut: 0

it is one step better :slight_smile:

hahaha now I have 3diffrent ways to wire the nunchuck...
isn't there a danger that it get's fried?

;DI am totally happy now!!!
no Idea what did change but it works!!!!
thank you all !!

now let's make some robojostic !!

:smiley: