Wii Nunchuck controlling ArduRover

ArduRover V1

This is to connect a Wii Nunchuck to the ArduRover and then, by moving the Wii Nunchuck move the rover instead of just using Serial and WASDX to control it.

The first thing to know is that I didn’t document everything for the first two Wii Nunchuck demos.

I am pretty sure I bought my wii adapter from Adafruit, but Sparkfun also has one.

The pins for the Wii Nunchuck adapter are:
gnd for the –
3.3v for the +
d = sda (pin 20 on the mega)
c = scl (pin 21 on the mega)

I didn’t document those and had to go back to the original train article to figure it out.

The next thing is the wii nunchuck pin locations.  I didn’t write those down either.  The adapter goes in so that the pin outs are on the top of the horseshoe (pics below).

I also really need to put the code into git so others can not have to copy and paste and I can have the projects organized instead of just in my arudino folder of my documents and pasted into the blog.

In any case, I wrote some quick and crappy code to take the joyx and joyy and determine the bounds I wanted for left, right, up, down, center which correspond to movement and center is stop.

nunchuck_funcs.h came from here (http://modelrail.otenko.com/arduino/wii-nunchuck-arduino-mega-model-railway-fun).  I may have modified it or not… I’ve posted it below for convenience just in case I did modify it (if I find out I did not then i’ll delete the code later when I’m less tired).

/*
* Nunchuck functions -- Talk to a Wii Nunchuck
*
* This library is from the Bionic Arduino course :
* http://todbot.com/blog/bionicarduino/
*
* 2007-11 Tod E. Kurt, http://todbot.com/blog/
*
* The Wii Nunchuck reading code originally from Windmeadow Labs
* http://www.windmeadow.com/node/42
*/

#if (ARDUINO >= 100)
#include
#else
#include
//#define Wire.write(x) Wire.send(x)
//#define Wire.read() Wire.receive()
#endif



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
static void nunchuck_init()
{
Wire.begin(); // join i2c bus as master
Wire.beginTransmission(0x52);// transmit to device 0x52
#if (ARDUINO >= 100)
Wire.write((uint8_t)0x40);// sends memory address
Wire.write((uint8_t)0x00);// sends sent a zero.
#else
Wire.send((uint8_t)0x40);// sends memory address
Wire.send((uint8_t)0x00);// sends sent a zero.
#endif
Wire.endTransmission();// stop transmitting
}

// Send a request for data to the nunchuck
// was "send_zero()"
static void nunchuck_send_request()
{
Wire.beginTransmission(0x52);// transmit to device 0x52
#if (ARDUINO >= 100)
Wire.write((uint8_t)0x00);// sends one byte
#else
Wire.send((uint8_t)0x00);// sends one byte
#endif
Wire.endTransmission();// stop transmitting
}

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

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

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

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

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

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

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

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

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

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

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

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

The ArduTank code is the same as before except I added the bounds and a couple of other elements like turning on and off serial commands for WASDX and Wii Nunchuck.  I also moved the speed out to a global variable.  Further iterations will remove the method calls with those global variables.

ArduTank.ino


#include
#include "nunchuck_funcs.h"

#define MOTOR_A 12
#define SPEED_A 3
#define BRAKE_A 9

#define MOTOR_B 13
#define SPEED_B 11
#define BRAKE_B 8

#define SERIAL_ON 0
#define WIICHUCK_ON 1

// tophat left x 24
// tophat left y 124
//
// tophat right x 216
// tophat right y 124
//
// tophat up x 124
// tophat up y 218
//
// tophat down x 120
// tophat down y 30
//
// tophat center x 122
// tophat center y 125


int loop_cnt=0;

byte accx, accy, zbut, cbut, joyx, joyy;
int ledPin = 13;

//255 is maximum speed
int leftspeed = 255;
int rightspeed = 255;


// from http://arduino.cc/en/Main/ArduinoMotorShieldR3
//Function Channel A Channel B
//Direction Digital 12 Digital 13
//Speed (PWM) Digital 3 Digital 11
//Brake Digital 9 Digital 8
//Current Sensing Analog 0 Analog 1

void setup() {
//Setup Channel A
pinMode(12, OUTPUT); //Initiates Motor Channel A pin
pinMode(9, OUTPUT); //Initiates Brake Channel A pin
pinMode(3, OUTPUT); //Initiates Speed Channel A pin

//setup Channel B
pinMode(13, OUTPUT); //Initiates Motor Channel B pin
pinMode(8, OUTPUT); //Initiates Speed Channel B pin
pinMode(11, OUTPUT); //Initiates Brake Channel B pin

Serial.begin(19200);
Serial.println("setup");

if (WIICHUCK_ON) {
nunchuck_setpowerpins();
nunchuck_init(); // send the initilization handshake
}
}

void loop() {
if (SERIAL_ON && !WIICHUCK_ON) {
serialOn();
}

if (!SERIAL_ON && WIICHUCK_ON) {
wiiChuck();
}
}

void wiiChuck() {
if( loop_cnt > 100 ) { // every 100 msecs get new data
loop_cnt = 0;

nunchuck_get_data();
nunchuck_print_data();

joyx = nunchuck_joyx();
joyy = nunchuck_joyy();
if (isLeft()) {
Serial.println("to the left");
left(leftspeed,rightspeed);
} else if (isCenter()) {
Serial.println("in the center");
stop();
} else if (isRight()) {
Serial.println("to the right");
right(leftspeed,rightspeed);
} else if (isForward()) {
Serial.println("forward ho");
forward(leftspeed,rightspeed);
} else if (isReverse()) {
Serial.println("retreat");
reverse(leftspeed,rightspeed);
} else {
stop();
}

// debugChuck();
}

loop_cnt++;
delay(1);
}

boolean isLeft() {
return joyx >= 20 && joyx = 120 && joyy <=126;
}

boolean isRight() {
return joyx >= 122 && joyx = 122 && joyy <= 135;
}

boolean isCenter() {
return joyx >= 120 && joyx = 122 && joyy <= 126;
}

boolean isForward() {
return joyx >=120 && joyx =122 && joyy <= 225;
}

boolean isReverse() {
return joyx >= 120 && joyx = 25 && joyy <=126;
}

void debugChuck() {
accx = nunchuck_accelx(); // ranges from approx 70 - 182
accy = nunchuck_accely(); // ranges from approx 65 - 173
zbut = nunchuck_zbutton();
cbut = nunchuck_cbutton();

Serial.print("accx: "); Serial.print((byte)accx,DEC);
Serial.print("\taccy: "); Serial.print((byte)accy,DEC);
Serial.print("\tzbut: "); Serial.print((byte)zbut,DEC);
Serial.print("\tcbut: "); Serial.println((byte)cbut,DEC);
}

void serialOn() {
// Wait until a character is received
while (Serial.available() < 1) {}

char val = Serial.read();


// Perform an action depending on the command
switch(val) {
case 'w'://Move Forward
case 'W':
forward (leftspeed,rightspeed);
break;
case 's'://Move Backwards
case 'S':
reverse (leftspeed,rightspeed);
break;
case 'a'://Turn Left
case 'A':
left (leftspeed,rightspeed);
break;
case 'd'://Turn Right
case 'D':
right (leftspeed,rightspeed);
break;
default:
stop();
break;
}
}

void stop(void) {
digitalWrite(MOTOR_A, LOW);
analogWrite(SPEED_A, 0);
digitalWrite(MOTOR_B, LOW);
analogWrite(SPEED_B, 0);
Serial.println("stop");
}

void forward(char a, char b) {
analogWrite (SPEED_A,a);
digitalWrite(MOTOR_A,LOW);
analogWrite (SPEED_B,b);
digitalWrite(MOTOR_B,LOW);
Serial.println("forward");
}

void reverse (char a, char b) {
analogWrite (SPEED_A,a);
digitalWrite(MOTOR_A,HIGH);
analogWrite (SPEED_B,b);
digitalWrite(MOTOR_B,HIGH);
Serial.println("reverse");
}

void left (char a, char b) {
analogWrite (SPEED_A, a);
digitalWrite(MOTOR_A, LOW);
analogWrite (SPEED_B, b);
digitalWrite(MOTOR_B, HIGH);
Serial.println("left");
}

void right (char a, char b) {
analogWrite (SPEED_A, a);
digitalWrite(MOTOR_A, HIGH);
analogWrite (SPEED_B, b);
digitalWrite(MOTOR_B, LOW);
Serial.println("right");
}

I think my next steps are to take the Nunchuck and be able to move the ArduRover based on the gyroscope output of the Nunchuck instead of the tophat (or press ‘c’ and the tophat can be used or something, I’ve not really gotten that far).

Check out the other entries on wii nunchuck.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.