Wii NunChuck controller, Arduino and servos

The Wii nunchuck controller contains a 3 axis accelerometer, one joystick and 2 buttons for only 20 euro and you can easly use it to manage your Arduino applications.
In this article we’ll see how to move two servo motors using this controller (providing two videos, prototype images and complete source code)

I have read a lot about Arduino and Wii Nunchuck, so I have decided to buy a wiichuck adapter from Sparkfun, a Wii controller and I was very excited to do my first tests.
I was surprised because I only spent 30 minutes to put it in place and create this demo.

REQUIRED HARDWARE:

- 1 Nintendo Wii Nunchuck Controller (20 euro and you can buy it everywhere)

- 1 WiiChuck Adapter – 3 dollars on Sparkfun.com
NOTE: you can also cut the cable and directly connect the controller wires (search on Google and you’ll find a lot of articles)

- 2 Servo motors HS-422 (or similar, about 15/20 euro each one) and a Lynx Tilt kit (about 15/20 euro) – from robot-italy.com

- 1 Arduino (i have used Duemilanove version)

- 1 BreadBoard

CONNECTIONS
The Wii Controller requires only 4 pins: ground, 5 volt and 2 Arduino analog pins.
Each servo motor require 3 connections: ground, 5V and a Arduino PWM pin.

Check following images to get inspiration. It’s very simple
wiicontrollervoodoo

wiicontrollerbreadboard

CODE (wiring/Arduino):
I have found a lot of source code samples inside books and looking around the web but this is the best one and the more accurate I have found for my purpose (check following video to understand what I mean with ‘accurate’).
I don’t remember where I got it so I’m sorry if I didn’t post the author’s website.

#include <wire.h>
#include <string.h>
#include <stdio.h>
 
uint8_t outbuf[6];
 
int cnt = 0;
int ledPin = 13;
 
int servoPin = 9;
int servoPin2 = 10;
 
int pulseWidth = 0;
int pulseWidth2 = 0;
 
long lastPulse = 0;
long lastPulse2 = 0;
 
int z_button = 0;
int c_button = 0;
 
int refreshTime = 20;
 
int minPulse = 1000;
int minPulse2 = 500;
 
int dtime=10;
 
#define pwbuffsize 10
long pwbuff[pwbuffsize];
long pwbuffpos = 0;
long pwbuff2[pwbuffsize];
long pwbuffpos2 = 0;
 
void setup()
{
    Serial.begin (9600);
    Wire.begin ();
    nunchuck_init ();
    pinMode(servoPin, OUTPUT);
    pinMode(servoPin2, OUTPUT);
 
    pulseWidth = minPulse;
    pulseWidth2 = minPulse2;
    Serial.print ("Finished setupn");
}
 
void nunchuck_init()
{
    Wire.beginTransmission (0x52);
    Wire.send (0x40);
    Wire.send (0x00);
    Wire.endTransmission ();
}
 
void send_zero()
{
    Wire.beginTransmission (0x52);
    Wire.send (0x00);
    Wire.endTransmission ();
}
 
int t = 0;
 
void loop()
{
    t++;
    long last = millis();
 
    if( t == 1) {
 
        t = 0;
 
        Wire.requestFrom (0x52, 6);
 
        while (Wire.available ()) {
            outbuf[cnt] = nunchuk_decode_byte (Wire.receive ());
            digitalWrite (ledPin, HIGH);
            cnt++;
        }
 
        if (cnt >= 5) {
 
            //            printNunchuckData();
 
            int z_button = 0;
            int c_button = 0;
 
            if ((outbuf[5] >> 0) & 1)
                z_button = 1;
            if ((outbuf[5] >> 1) & 1)
                c_button = 1;
 
            switch (c_button) {
            case 1:
                switch (z_button) {
                case 0:
                    break;
                case 1:
                    muovi();
                    break;
                }
                break;
            case 0:
                switch (z_button) {
                case 0:
                    delay(10000);
                    break;
                case 1:
                    delay(3000);
                    break;
                }
                break;
            }
        }
 
        cnt = 0;
        send_zero();
 
    } // if(t==)
 
    updateServo();
 
    delay(dtime);
}
 
 
void updateServo() {
 
    if (millis() - lastPulse >= refreshTime) {
 
        digitalWrite(servoPin, HIGH);
        delayMicroseconds(pulseWidth);
        digitalWrite(servoPin, LOW);
 
        digitalWrite(servoPin2, HIGH);
        delayMicroseconds(pulseWidth2);
        digitalWrite(servoPin2, LOW);
 
        lastPulse = millis();
    }
}
 
int i=0;
void printNunchuckData()
{
    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;
 
    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 (i,DEC);
    Serial.print ("t");
 
    Serial.print ("X: ");
    Serial.print (joy_x_axis, DEC);
    Serial.print ("t");
 
    Serial.print ("Y: ");
    Serial.print (joy_y_axis, DEC);
    Serial.print ("t");
 
    Serial.print ("AccX: ");
    Serial.print (accel_x_axis, DEC);
    Serial.print ("t");
 
    Serial.print ("AccY: ");
    Serial.print (accel_y_axis, DEC);
    Serial.print ("t");
 
    Serial.print ("AccZ: ");
    Serial.print (accel_z_axis, DEC);
    Serial.print ("t");
 
    Serial.print (z_button, DEC);
    Serial.print (" ");
    Serial.print (c_button, DEC);
    Serial.print ("rn");
    i++;
}
 
char nunchuk_decode_byte (char x)
{
    x = (x ^ 0x17) + 0x17;
    return x;
}
 
void muovi (){
    float tilt = (700 - outbuf[3]*2*2);
    float tilt2 = outbuf[2]*2*2;
 
    tilt = (tilt);
    pulseWidth = (tilt * 5) + minPulse;
 
    tilt2 = (tilt2-288);
    pulseWidth2 = (tilt2 * 5) + minPulse2;
 
    pwbuff[pwbuffpos] = pulseWidth;
    pwbuff2[pwbuffpos2] = pulseWidth2;
 
    if( ++pwbuffpos == pwbuffsize ) pwbuffpos = 0;
    if( ++pwbuffpos2 == pwbuffsize ) pwbuffpos2 = 0;
 
 
    pulseWidth=0;
    pulseWidth2=0;
 
    for( int p=0; p<pwbuffsize; p++ ){
        pulseWidth += pwbuff[p];
        pulseWidth2 += pwbuff2[p];
    }
 
    pulseWidth /= pwbuffsize;
    pulseWidth2 /= pwbuffsize;
 
}

Comments (7)

  1. Chris Rojas (reply)

    December 9, 2009 at 4:51 am

    Great work, great use of the wii-chuck. Also I like the little guy strapped to the front of it.

  2. Fabio Biondi (reply)

    December 9, 2009 at 11:46 am

    Thanx Chris, i only got and merged some info from more resources but I love it too… it seems work very well.
    If you are interested the puppet collection name is VOODOO dools : P

  3. blake (reply)

    January 17, 2011 at 12:56 am

    I F’ing love you!! i’ve been searching for days for this code that didn’t have errors after compiling. if you could give me some pointers though ( cause im still a noob), i’m not sure how to assign the other buttons on the nunchuck. Like two more servos for the joystick! Thanks Fabio! You made my day

  4. Stephanie (reply)

    November 21, 2012 at 2:49 am

    Hi Fabio! Your project looks great! I am trying to do the same, but I just can’t make it work. I think I have all the connections and the program the same way you have them. I hope you can help me. Im using an Arduino Duemilanove like you and the program is Arduino 1.0.1
    Thanks!

    1. Ben Horstmann (reply)

      December 9, 2012 at 8:30 pm

      Stephanie,
      I have the Duemilanove and the UNO also and I am using 1.0.1 as well. I just got this working a few days ago with this same code but I had to make some modifications to the code. It only only works on older versions of Arduino software. Here are the changes I made. It works on both boards. I will just put the code in the message so you can copy/paste.

      Changes:
      #include
      #include
      #include

      uint8_t outbuf[6];

      int cnt = 0;
      int ledPin = 13;

      int servoPin = 7; //TOP SERVO
      int servoPin2 = 6; // BOTTOM SERVO

      int pulseWidth = 0;
      int pulseWidth2 = 0;

      long lastPulse = 0;
      long lastPulse2 = 0;

      int z_button = 0;
      int c_button = 0;

      int refreshTime = 20;

      int minPulse = 1000;
      int minPulse2 = 200; <—-change to 200 from 500

      int dtime=10;

      #define pwbuffsize 10
      long pwbuff[pwbuffsize];
      long pwbuffpos = 0;
      long pwbuff2[pwbuffsize];
      long pwbuffpos2 = 0;

      void setup()
      {
      Serial.begin(19200); <——works better at 19200
      Wire.begin ();
      nunchuck_init ();
      pinMode(servoPin, OUTPUT);
      pinMode(servoPin2, OUTPUT);

      pulseWidth = minPulse;
      pulseWidth2 = minPulse2;
      Serial.print ("Finished setup\n");
      }

      void nunchuck_init()
      {
      Wire.beginTransmission (0×52);
      Wire.write (0×40); <——change to write
      Wire.write(byte (0×00)); <—–change to write
      Wire.endTransmission ();
      }

      void send_zero()
      {
      Wire.beginTransmission (0×52);
      Wire.write (byte (0×00)); = 5) {

      // printNunchuckData();

      int z_button = 0;
      int c_button = 0;

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

      switch (c_button) {
      case 1:
      switch (z_button) {
      case 0:
      break;
      case 1:
      muovi();
      break;
      }
      break;
      case 0:
      switch (z_button) {
      case 0:
      delay(10000);
      break;
      case 1:
      delay(3000);
      break;
      }
      break;
      }
      }

      cnt = 0;
      send_zero();

      } // if(t==)

      updateServo();

      delay(dtime);
      }

      void updateServo() {

      if (millis() – lastPulse >= refreshTime) {

      digitalWrite(servoPin, HIGH);
      delayMicroseconds(pulseWidth);
      digitalWrite(servoPin, LOW);

      digitalWrite(servoPin2, HIGH);
      delayMicroseconds(pulseWidth2);
      digitalWrite(servoPin2, LOW);

      lastPulse = millis();
      }
      }

      int i=0;
      void printNunchuckData()
      {
      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;

      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 (i,DEC);
      Serial.print (“\t”);

      Serial.print (“X: “);
      Serial.print (joy_x_axis, DEC);
      Serial.print (“\t”);

      Serial.print (“Y: “);
      Serial.print (joy_y_axis, DEC);
      Serial.print (“\t”);

      Serial.print (“AccX: “);
      Serial.print (accel_x_axis, DEC);
      Serial.print (“\t”);

      Serial.print (“AccY: “);
      Serial.print (accel_y_axis, DEC);
      Serial.print (“\t”);

      Serial.print (“AccZ: “);
      Serial.print (accel_z_axis, DEC);
      Serial.print (“\t”);

      Serial.print (z_button, DEC);
      Serial.print (” “);
      Serial.print (c_button, DEC);
      Serial.print (“\r\n”);
      i++;
      }

      char nunchuk_decode_byte (char x)
      {
      x = (x ^ 0×17) + 0×17;
      return x;
      }

      void muovi (){
      float tilt = (700 – outbuf[3]*2*2);
      float tilt2 = (700 – outbuf[2]*2*2); <—-changed

      tilt = (tilt);
      pulseWidth = (tilt * 5) + minPulse;

      tilt2 = (tilt2);
      pulseWidth2 = (tilt2 * 5) + minPulse2;

      pwbuff[pwbuffpos] = pulseWidth;
      pwbuff2[pwbuffpos2] = pulseWidth2;

      if( ++pwbuffpos == pwbuffsize ) pwbuffpos = 0;
      if( ++pwbuffpos2 == pwbuffsize ) pwbuffpos2 = 0;

      pulseWidth=0;
      pulseWidth2=0;

      for( int p=0; p<pwbuffsize; p++ ){
      pulseWidth += pwbuff[p];
      pulseWidth2 += pwbuff2[p];
      }

      pulseWidth /= pwbuffsize;
      pulseWidth2 /= pwbuffsize;

      }

  5. Fabio Biondi (reply)

    December 10, 2012 at 12:44 am

    Hi Ben, thank you to answer Stephanie.
    It’s too much time I don’t use Arduino and I’m not able to provide support so any help is really appreciated.

    Fabio

Leave a Reply

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

Published on: 9 December 2009
Posted by:
Discussion: 7 Comments