|
The plan here is to modify code 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..
The code is below. There are some other websites referenced within it 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.
// 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; } I am trying very hard to improve this so I can use a wireless Nunchuck to do the same task. The code "hack" for this is proving very tricky to make work however. Watch this space.....
I have 2 wireless controllers so far but cannot get it to work with either of them:
I have been using the code posted by Michael Dreher half way down this blog page:
He seems to be only person who has managed to get a wireless Nunchuck to talk to an Arduino.
I get one set of data then it stops. I have to press the manual reset button of the Arduino each time to get another line of (good) data (see below). This is a shame as the system is obviously very nearly working. If anyone can help me out please do contact me via Youtube. I have also put this question up on the Arduino Forums in the "Interfacing" section.
|
