Monday, June 22, 2009

Wii Motion Plus + Arduino = Love

Ok so I, after much research, have been able to read the gyro data of the new wii motion plus peripheral with the arduino microcontroller. With this code and the code previously developed for the wii nunchuck (here), we are able to create a 6 DOF IMU for under $40. Thanks Nintendo! Best of all, everything is I2C so only 2 analog inputs (A4 and A5 needed for the wire library) are needed to read 6 sensors and no ADC conversion happens on the arduino board.

Links and Thanks:
First off, I'm going to link(and thank) a few sites that have helped me immensely with this undertaking:
Arduino.cc
original nunchuck code
Wii brew WM+ info
Propeller forums

Hardware connections:
I used full 5V TTL signals and power and have had no problems thus far (a week going strong) but if you have a 3.3v arduino or a level converter and 3.3v regulator, i would suggest that over a 5v connection. Furthermore, twi.h under the wire library does not need to be changed for my setup but might under some setups. Start with the default 100khz TWI_FREQ and if that doesnt work, use 400khz. Websites disagree about which is the proper speed for direct I2C on wii peripherals. Connections to WM+ are same as nunchuck and look like this:
| 1 2 3 |
| |
| 6 5 4 |
|_-----_|

1 - green - data
2 - nothing
3 - red - 3.3+v
4 - yellow - clock
5 - nothing
6 - white - ground

So its pin 3 to 5v, 6 to ground, 1 to A4, and 4 to A5

Adapters such as the one sold here should (theoretically) work; I use jumper wires and hot glue

Software:

I have commented the demo code pretty well so it should be easy to follow, if not comment here or on Arduino.cc Forums->Exhibition

#include <Wire.h>
byte data[6]; //six data bytes
int yaw, pitch, roll; //three axes
int yaw0, pitch0, roll0; //calibration zeroes

void wmpOn(){
Wire.beginTransmission(0x53); //WM+ starts out deactivated at address 0x53
Wire.send(0xfe); //send 0x04 to address 0xFE to activate WM+
Wire.send(0x04);
Wire.endTransmission(); //WM+ jumps to address 0x52 and is now active
}

void wmpSendZero(){
Wire.beginTransmission(0x52); //now at address 0x52
Wire.send(0x00); //send zero to signal we want info
Wire.endTransmission();
}

void calibrateZeroes(){
for (int i=0;i<10;i++){
wmpSendZero();
Wire.requestFrom(0x52,6);
for (int i=0;i<6;i++){
data[i]=Wire.receive();
}
yaw0+=(((data[3]>>2)<<8)+data[0])/10; //average 10 readings
pitch0+=(((data[4]>>2)<<8)+data[1])/10;
roll0+=(((data[5]>>2)<<8)+data[2])/10;
}
Serial.print("Yaw0:");
Serial.print(yaw0);
Serial.print(" Pitch0:");
Serial.print(pitch0);
Serial.print(" Roll0:");
Serial.println(roll0);
}

void receiveData(){
wmpSendZero(); //send zero before each request (same as nunchuck)
Wire.requestFrom(0x52,6); //request the six bytes from the WM+
for (int i=0;i<6;i++){
data[i]=Wire.receive();
}
yaw=((data[3]>>2)<<8)+data[0]-yaw0;
pitch=((data[4]>>2)<<8)+data[1]-pitch0;
roll=((data[5]>>2)<<8)+data[2]-roll0;
}
//see http://wiibrew.org/wiki/Wiimote/Extension_Controllers#Wii_Motion_Plus
//for info on what each byte represents
void setup(){
Serial.begin(115200);
Serial.println("WM+ tester");
Wire.begin();
wmpOn(); //turn WM+ on
calibrateZeroes(); //calibrate zeroes
delay(1000);
}

void loop(){
receiveData(); //receive data and calculate yaw pitch and roll
Serial.print("yaw:");//see diagram on randomhacksofboredom.blogspot.com
Serial.print(yaw); //for info on which axis is which
Serial.print(" pitch:");
Serial.print(pitch);
Serial.print(" roll:");
Serial.println(roll);
delay(100);
}



Orientation:

Sorry for the arrows being all in the negative direction. Picture was done quickly.

Image and video hosting by TinyPic

ENJOY!

17 comments:

Anonymous said...

great! i will have a try later

Alpay Kasal said...

Inspirational stuff, thank you!

Anonymous said...

Any snaps of your set up? Very impressive and cheap as chips! Good work knuckles

isnoop said...

Great stuff! Soon the sky will be swarming with homebrew UAVs and the streets teeming with balancing robots thanks to Nintendo!

Anonymous said...

Thanks, I know where to turn for 6dof!

Anonymous said...

I'm confused, wouldn't this just give you 3 dof? Just rotation on 3 axes?

Anonymous said...

Since that it is using information from both the Nunchuck (using it's accelerometer) and the Wii Motion Plus (using it's gyroscope), it can detect acceleration as well as tilt (the tilt data is more accurate coming from the gyro than the accelerometer, especially during acceleration). You get 3 readings from each device (which is 6 readings).

Mad_Robot said...

Has anyone fried there arduino chip with this?
I bought the Wii-Motion-Plus and cabled it up as the example states.. ran it just fine.
Started to modify the code.. ie. was going to average the results for less bouncy-ness.. and I couldn't upload the code.. come to find out, the chip is dead.
I beleive it is the microcode (ie arduino-firmware) that is messed up.
Tried it on a second chip.. same thing happened.
Tried it on a second hardware-board and third chip.. same thing happened.

Really puzzled here.. and I hope someone else might clue me in..

Thanks

Tom said...

I'm mainly after this to get a straight rotational sensor, not even the 3 full axes of data, but I've hit a stumbling block after just opening the case...

My wiring to the Wii-extension port on the unit from the PCB, has 7 Wires. Not normally a big deal, but the colours do not even remotely match up to what's listed here, in terms of pin numbering.

I don't have the connector here to reference, but has anyone else seen a different wiring layout used in their WiiM+?

uphi said...

I tried your code with the 2 transistors. Works great ! Now for the Kalman filter. I want to use this to replace the expensive IR sensors in the Ardupilot. I also have been making an autopilot with the Parallax Propeller micro.
Hope to see the Kalman filter soon.
Earl

uphi said...

@mad robot : Never ever fried a chip yet. Have experimented with several ardunio boards no problem. This code works great. No problem here.

@k can we post pics of our projects that use the MP+ and Nun here ?

Mad_Robot said...

Mad_Robot here...
I did re-flash the chips. they were hosed.
The values I get from the sensor sometimes are low.. but most of the time are very large numbers.
So, if I were to make a guess, the homemade PCB-wii-adapter that I made might not be thick enough and maybe they are getting errorneous values. Values that might be bigger than an 'Arduino int' (>32k).. and possibly overwriting memory? (Not sure how Arduino would handle it).

So, I would ask (as I build a new PCB-wii-adapter) what is the average response range that people get from this code ie.(0-255)?

Thanks for the feedback.

MadR.

jpiat said...

Does anyone know if the motion plus can cause trouble on the bus. I have hooked an i2C two axis accelerometer on the I2C bus and a wii motion plus, but it seems that i can't access the accelerometer (always reading zeros on accelerometer registers). I don't know if the trouble comes from the accelerometer or if the wii motion plus control the bus for every address ...

DogP said...

Hey,

I couldn't find any info on getting the MotionPlus to work with the passthrough (for the Nunchuck and Classic Controller), so I hooked it up to my logic analyzer and RE'd it.

Just wanted to let you know I updated my MotionPlus object for the Propeller (from the thread linked above) to work w/ the passthrough port: http://obex.parallax.com/objects/471/ . The code/comments should explain everything pretty well, so it should be easy to modify for the Arduino.

It alternates MotionPlus and Extension data on every other read, and you lose the LSBit of each accelerometer axis in Nunchuck mode (and the LSBit of each axis on the left joystick in Classic Controller mode), and it moves a couple bits around. I assume this is so you can tell the difference between MP data and Ext data (MP ends in 10, Ext ends in 00).

DogP

Anonymous said...

Muy bueno, genial.....muchas felicidades..
Solo le falta kalman....un 10

......
Very good, great ..... many congratulations ..
Only lacks kalman

Anonymous said...

Hi, great work here.

I've been able to interface with the nunchuck before but I can't get it to work with the M+. Can't even get the I2C start sequence to work.

I don't have a proper oscilloscope but it seems my clock and data lines are always low. Usually when I connect the nunchuck the lines would go high, not the case of the M+ though. And there's nothing wrong with my M+, works great in Sports Resort.

Any ideas?

Dan

Anonymous said...

Oh nevermind. I figured it out. It seems the M+ needs external pull-up resistors on the clock and data lines whereas the nunchuck doesn't. I tried 1K and 4K7 resistors, both worked fine (100KHz, 3.3V).

Dan

Post a Comment