12b) Wireless Wii Nunchuck Arduino interface for twin-wheeler

PLAN A: Use a wired conventional NIntendo Wii Nunchuck
The first plan was to get this to work with a "hacked" wired Nunchuck, once this was done the plan was to go up to a wireless 'chuck.
Code modified from that developed by others using the Arduino microcontroller to "hack" the data coming out of a Wii Nunchuck and use the Arduino output pins to send control information to my main microcontroller. The Nunchuck is low cost, fits the hand perfectly, has a 3 axis accelerometer and a proper proportional 2 axis joystick on top. In addition it has 2 buttons I can use as the motor "kill" switch if they are released. The Arduino is also very low cost. I now have it working (see base of this page) - I can steer board by just tipping handcontroller left or right, or I can use the thumb joystick on the top..

YouTube Video

YouTube Video

 
 
 
There are some other websites referenced below where you can find more info on wiring in the Nunchuck (4 wires).
The idea is that I will use the joystick forward/back control to fine-tune the skateboard balance point. If you press the lower button on front of the Nunchuck, you steer by using the left/right joystick movement.
If you press the upper button (or both together) then you can still trim balance point via the joystick but the steering is now done by tilting the whole nunchuck to the right or the left - i.e. you use the x-axis accelerometer to steer with.
If you fail to press any of the front 2 buttons, it assumes you have fallen off and a logic signal goes low to "tell" the main controller to cut the power to the motor etc.
 
You can get the idea from the 2 vids I have put on YouTube, where I am just dimming and brightening PWM'ed LEDs on the outputs to show you the general idea.
 
The Arduino programming environment is a simplified form of "C" and is designed as a low cost and easier way for people to get their projects up and running very quickly. The boards are open-source and come with a serial port, the capacity to assign additional pins as "software" serial outputs and inputs. They are programmed directly via a USB cable from the PC.
If I had started the skateboard project again now, I would probably have used Arduino - cheaper and easier. There are loads of add-on accessories such as Bluetooth, wireless data transfer, GPS units, even a complete model aircraft "autopilot" system if you want to build yourself a UAV.
The code below was for the WIRED Nunchuck, not the wireless version which was much more of a headache to get working.
 
 
 

// Arduino based interface between a Wii Nunchuck and a Segway style self balancing electric vehicle.

// In my case this is a twin wheeled electric skateboard. Outputs from Arduino go to the main microcontroller which is controlling

// and balancing the board.

// Uses Duemilanove ATMega328  Arduino 17 with wired Wii Nunchuck.

 

// 5V output on Arduino to Nunchuck (works better than using 3.3V line)

// GND

// SDA to analog pin 4

// SCK to analog pin 5

// the middle upper and middle lower contacts of nunchuck plug are not used

 

//Derived from code developed by Chad Phillips, see:  http://windmeadow.com/node/42

//   For info on wiring up the chuck to the Arduino, download Bionic Arduino Class 4 from TodBot blog:

//   http://todbot.com/blog/2007/11/24/bionic-arduino-class-notes-3-4/

 

// To avoid hacking off plug you can buy a nunchuck extender cable (plug one end socket the other) and

// cut that in half to get the socket on a cable, with free wires on the other end to attach to Arduino

 

// Some non-nintendo Nunchucks have shorter cables by the way than the real thing.

 

// Outputs for skateboard: Digital Pin 9 outputs 5V to balance controller board when buttons(s) pressed.

//                         if neither button pressed, Pin 9 goes to 0V to "tell" main board to cut the motors

 

//                         Smoothed PWM output on pin 10 gives 2.5V most of time. Climbs to +5V or drops to 0V when joystick

//                         moved forwards or backwards. Main microcontroller accesses this and uses it to control the "Trim" function

//                         which can be used to fine-tune balance point of the board

//                         Smoothed PWM output on pin 11 gives 2.5V most of time. If z button pressed, then joystick left/right will make it

//                         go down to 0V or up to 5V. Main micro takes this and uses it as signal to control steering of twin wheeled board.

//                         If z and c buttons pressed (or just c button alone) then this Pin 11 output becomes controlled by L/R TILT of the

//                         nunchuck (via embedded accelerometer) and the L/R joystick component is disabled.

 

 

 

 

 

 

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

int i; //declare i as an integer

int j; //declare j as an integer

 

#define LEDx 11   //pin 11 varies according to x accel  pin10 is a pwm

#define LEDy 10   //defines pin 10 as varying according to y accel pin 9 is a pwm

#define ledc 8   //defines pin 8 as output for c button

#define ledz 9   //defines pin 9 as output for z button

 

 

void

setup ()

{

  //Serial.begin (19200);

  //Serial.print ("Finished setup\n");

  pinMode(LEDx, OUTPUT); //tell arduino it is an output

  pinMode(LEDy, OUTPUT); //tell arduino it is an output

  pinMode(ledc, OUTPUT); //tell arduino it is an output

  pinMode(ledz, OUTPUT); //tell arduino it is an output

 

  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); //values seen: 37left 134mid  234right

  Serial.print ("\t");

 

  Serial.print (joy_y_axis, DEC); //values seen: 218forward  124mid  23back

  Serial.print ("\t");

 

  Serial.print (accel_x_axis, DEC); //values: 312left  512mid  745right

  Serial.print ("\t");

 

  Serial.print (accel_y_axis, DEC); //values: forward754  mid576   back361

  Serial.print ("\t");

 

  Serial.print (accel_z_axis, DEC);  // values: hovers around 530 - 743

  Serial.print ("\t");

 

  Serial.print (z_button, DEC); //NB: 0 when pressed 1 when not

  Serial.print ("\t");

 

  Serial.print (c_button, DEC); //NB: 0 when pressed 1 when not

  Serial.print ("\t");

 

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

  */

 

 

 

j = joy_y_axis;

analogWrite(LEDy, j);  //sets LED on pin 10 used to control the Trim function brightness according to forward/back of joystick

 

 

 

 

if (z_button < 1 && c_button == 1) {  //if pressing z button only, steer on left/right joystick

                  digitalWrite (ledz, HIGH);    // sets the LED high on pin 9

                  i = joy_x_axis - 6; //mid point is 134 this corrects it to 128

                  analogWrite(LEDx, i);  //sets LED on pin 11 steering PWM brightness

                                  }

                                 

if (z_button < 1 && c_button < 1) {  //if pressing both buttons, steer using accelerometer

                  digitalWrite (ledz, HIGH);    // sets the LED high motor kill switch control on pin 9

                  i= (int) (accel_x_axis - 512) / 2 + 128;   //convert from range 312 to 512mid to 745 to a 0-255 scale

                  if (i>255) i=255;

                  if (i<0) i=0;

                  analogWrite(LEDx, i);  //sets LED on pin 10 brightness

                                  }

 

 

                                    

if (z_button == 1 && c_button < 1) {  //if c button only pressed then steer on accelerometer

                  digitalWrite (ledz, HIGH);    // sets the LED high on pin 9

                  i= (int) (accel_x_axis - 512) / 2 + 128;   //convert from range 312 to 512mid to 745 to a 0-255 scale

                  if (i>255) i=255;

                  if (i<0) i=0;

                  analogWrite(LEDx, i);  //sets LED on pin 11 steering brightness

                                    }

                   

if (z_button == 1 && c_button == 1) {   //if no button pressed then dead ahead and control signal to AVR off i.e. stop motor

                  digitalWrite (ledz, LOW);     // sets the LED low on pin 9

                  analogWrite(LEDx, 128);  //sets LED on pin 11 steering half PWM i.e. dead ahead if skateboard 

                  analogWrite(LEDy, 128);  //sets LED on pin 10 half PWM i.e. mid point for the trim function if skateboard            

                                     }

         

}

 

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

}

 
PLAN B: Once that was done, try to use a wireles Nunchuck to do the same job
The Arduino code to read a wireless Nunchuck is different to that used for the wired Nunchuck.
 

The code "hack" for this was proving very tricky to make work. It is different to the one used for the "wired" Nunchuck.
 
OK managed to get this to work after much conversation on the Arduino forum November 09. For the wireless nunchuck code have a look at this forum page:
 
Take note of the comments about external pull-up resistors.
 
You can turn it by just tilting your hand left or right.
 
I have 2 different makes of wireless controllers, BlazePro and a newer rechargeable type with no name. Both of them work OK with this code
(Nintendo don't make a wireless one, these are all made in China, several types available).
 
 
 
 
Photo of the Arduino board which just spends its time reading the wireless Nunchuck and then sending control info to the main balance/steering microcontroller.
 
 
Also check out this link to someone who has usd an Arduin to read the 3 axis gyroscoe found within the Wii Motion Plus adapter.
This is a cheap way of getting a 3 axis gyro into an Arduino project.    Click Here
 
 
 
 
 
Comments