Wii MotionPlus protocol. . . Let us hack . . .

General project help for Adafruit customers

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
User avatar
sorceress sarah
 
Posts: 100
Joined: Sat Jun 20, 2009 2:07 pm

Wii MotionPlus protocol. . . Let us hack . . .

Post by sorceress sarah »

The recent release of the Wii Motionplus sensor gives us a < $20 3-axis (yes, it IS 3 axis!) gyro that speaks I2C. I got the thing to answer up. It's I2C address os 0x53. But I have yet to figure out how to extract the data. Several sites have some data, but there is still much missing. Any help would be much appreciated.

FWIW - I am using Cypress PSoCs

EDIT 1 -

I apologize for the duplicate thread. I intended to bump the other, but got distracted. The links mentioned in the links mentioned in the other thread point to gret stuff for the nunchuck accellerometer data, but say nothing about MotionPlus. Wiire.org appears to be a cobweb. It hasn't been updated in a while.

Now a little detail:

I have a motionplus which I have hacked slightly. Since I needed a connector for the proprietary Wii connector, I took the one that was already attached and used it. It has a "pass through" for the I2C bus. The connector that goes to the Wiimote is soldered to the middle of the board. That's a great one to leave for the input. The "pass through" is soldered to the edge of the board with FlexTrace. I removed it and replaced the flextrace with a rainbow-colored ribbon cable using the same color scheme as Nintendo.

Pin 1 (brown) is +3.3v
Pin 2 (red) is I2C clock
Pin 3 (orange) not used
Pin 4 (yellow) not used
Pin 5 (green) I2C Data
Pin 6 (blue) Ground

I connected the device to a Cypress USB-I2C Bridge. Using the usbiic.exe application I issued the command to LIST which immediately produced the following output:

Devices list: 8bit 7bit
address : 00 00
address : 02 01
address : 04 02
address : 06 03
address : 08 04
address : 0A 05
address : 0C 06
address : 0E 07
address : A6 53 <== Here's our boy!
address : F0 78
address : F2 79
address : F4 7A
address : F6 7B
address : F8 7C
address : FA 7D
address : FC 7E
address : FE 7F


Using the "Wii Nunchuck" hacks as a guide, we send:

0x00 to address 0x40
and then write a 0x00 to the device.

Both actions produce an ACK

However, data is not available at the usual place (offset 8). So. . . I have missed something entirely. I will attempt to rewire this a little so that I can talk to a Wiimote over Bluetooth while simultaneously sniffing the I2C bus. Will report findings.

adafruit
 
Posts: 12151
Joined: Thu Apr 06, 2006 4:21 pm

Re: Wii MotionPlus protocol. . . Let us hack . . .

Post by adafruit »

very exciting, i just heard of this device...will check it out soon. keep hacking!

User avatar
sorceress sarah
 
Posts: 100
Joined: Sat Jun 20, 2009 2:07 pm

Re: Wii MotionPlus protocol. . . Let us hack . . .

Post by sorceress sarah »

Update 1:

Sorceress Sarah did dumb and stupid thing. Remember the connector that I removed (the pass-through?) OK, I put a pin header on it, 'cuz I got some 6-conductor cables that had just the right spacing to use a pin header there. But I gotta make the other end of one fit the USB-IIC bridge. So I make the wire. Ohms out fine. I hook it up and "TIMEOUT".

That's when I remember the ferrite bead on the end I left intact. OK, remove the ferrite. Many fewer timeouts, but. . . Dmn, something's gettin' hot! I know 'cuz I burnt my finger! Not good. . .

Check the bug with the original adapter. . . Still OK. But apparently this thing does not appreciate being probed from the back side. . . (I wonder why?)

Additionally, it appears that the Wiimote does not play well with others. It's the I2C master, and apparently is not set up for multi-master. So much for that idea. . . The I2C bridge that I have isn't set up for multi-master either. . . Bummer. . .

EDIT 1

I usually do my best thinking in bed. Too bad I have insomnia. . . :/ As usual,I have an idea. When I get home from work, I'll make a PSoC that has a UART that is optically isolated from the data and clock lines that just re-clocks and retransmits the data out the USB. . .

adafruit
 
Posts: 12151
Joined: Thu Apr 06, 2006 4:21 pm

Re: Wii MotionPlus protocol. . . Let us hack . . .

Post by adafruit »

if its a passthru then how can it be a master on both ends? perhaps i have to see this thing in person...

User avatar
sorceress sarah
 
Posts: 100
Joined: Sat Jun 20, 2009 2:07 pm

Re: Wii MotionPlus protocol. . . Let us hack . . .

Post by sorceress sarah »

Wiimote = Master 1
Cypress USB-I2C bridge = Master 2

I am guessing that the dueling clocks were making the bug in the middle unhappy.

So I am going to try to create a device that is just a receive-only I2C sniffer from a PSoC.

And, co-incidentally, I just found a box on my doorstep from DigiKey! Yay!!

User avatar
chelmi
 
Posts: 53
Joined: Wed Nov 19, 2008 12:09 pm

Re: Wii MotionPlus protocol. . . Let us hack . . .

Post by chelmi »

FYI, somebody on the arduino forum has code to interface with the Wii MotionPlus
http://www.arduino.cc/cgi-bin/yabb2/YaB ... 1245723011

User avatar
sorceress sarah
 
Posts: 100
Joined: Sat Jun 20, 2009 2:07 pm

Re: Wii MotionPlus protocol. . . Let us hack . . .

Post by sorceress sarah »

Cool stuff. I'll see if I can (w)hack it to work on a PSoC.

Be very careful, though. The guy's pinout is wrong. He's got the wires right, but the numbers of the pins are wrong.

His:

123
456

Correct

From the plug side:

531
642

Where:

1 = Power
2 = Clock
5 = Data
6 = Ground

User avatar
sorceress sarah
 
Posts: 100
Joined: Sat Jun 20, 2009 2:07 pm

Re: Wii MotionPlus protocol. . . Let us hack . . .

Post by sorceress sarah »

OK, so I hacked up the Arduino code. Here's the PSoC version. It has a bug. the same string is displayed for all the values and it contains non-digit characters. Maybe someone here can see what I can't 'cuz it's dangerous to code while sleeping.

Code: Select all


//----------------------------------------------------------------------------
// C main line
//----------------------------------------------------------------------------

#include <m8c.h>        // part specific constants and macros
#include "PSoCAPI.h"    // PSoC API definitions for all User Modules
#include "stdlib.h"

#define WiiMP_BeginI2C_ADDR 0x53
#define WiiMP_ADDR 0x52
#define BANNED 0xfe
#define ActivationData 0x04

const BYTE txActBuf[] = {0xfe,  0x04};
const BYTE txZeroBuf[] = {0x00};

BYTE rxBuf[6];
BYTE * txBuf; // RAM tx buffer

int yaw, pitch, roll;  //three axes
int yaw0, pitch0, roll0;  //calibration zeroes

char * yBuf;
char * pBuf;
char * rBuf;

void DisplayResults(void){
	itoa(yBuf,yaw,10);
	itoa(pBuf,pitch,10);
	itoa(rBuf,roll,10);
	LCD_Position(1,0);
	LCD_PrString(yBuf);
	LCD_Position(1,9);
	LCD_PrString(pBuf);
	LCD_Position(0,0);
	LCD_PrString(rBuf);

}

void main()
{
	BYTE status;
	int i;
	
	M8C_EnableGInt;  //  Enble the Global Interrupt
	LCD_Start();  //Start the LCD module
	
	I2Cm_Start();  //  Start the I2C in Master mode
	
	// Initialize Wii MotionPlus
	status = I2Cm_bWriteCBytes(WiiMP_BeginI2C_ADDR,txActBuf,2,I2Cm_CompleteXfer);
	
	/*  From this point on is a straight rip of the Arduino code.  */
	// Calibrate Zeros
	for (i = 0; i < 10; i++) {
	status = I2Cm_bWriteCBytes(WiiMP_ADDR,txZeroBuf,1,I2Cm_CompleteXfer); // Write a zero to request data
	status = I2Cm_fReadBytes(WiiMP_ADDR, rxBuf, 6, I2Cm_CompleteXfer);  // Read the data into the receive buffer
	yaw0+=(((rxBuf[3]>>2)<<8)+rxBuf[0])/10;        //average 10 readings for each zero
    pitch0+=(((rxBuf[4]>>2)<<8)+rxBuf[1])/10;
    roll0+=(((rxBuf[5]>>2)<<8)+rxBuf[2])/10;
	}
//	I2Cm_SendStop();

	for (;;){  //Loop forever
	status = I2Cm_bWriteCBytes(WiiMP_ADDR,txZeroBuf,1,I2Cm_CompleteXfer);  // Write a zero to request data
	status = I2Cm_fReadBytes(WiiMP_ADDR, rxBuf, 6, I2Cm_CompleteXfer);  // Read the data into the receive buffer
	yaw=((rxBuf[3]>>2)<<8)+rxBuf[0]-yaw0;        //see http://wiibrew.org/wiki/Wiimote/Extension_Controllers#Wii_Motion_Plus
	pitch=((rxBuf[4]>>2)<<8)+rxBuf[1]-pitch0;    //for info on what each byte represents
	roll=((rxBuf[5]>>2)<<8)+rxBuf[2]-roll0;
	DisplayResults();
	}
	
}




User avatar
fat16lib
 
Posts: 595
Joined: Wed Dec 24, 2008 1:54 pm

Re: Wii MotionPlus protocol. . . Let us hack . . .

Post by fat16lib »

Looks like it might be the itoa() calls. I see two things. yBuf, pBuf, and rBuf are pointers that are not initialized to point to separate buffers. Second, it looks like the first two parameters in the itoa() calls may be swapped.

The itoa I knew was:

Code: Select all

char *itoa(int value, char *string, int radix);

value - the integer to be converted to a string representation.

string - points to the buffer that is to hold resulting string.

radix - the base of the number.

User avatar
sorceress sarah
 
Posts: 100
Joined: Sat Jun 20, 2009 2:07 pm

Re: Wii MotionPlus protocol. . . Let us hack . . .

Post by sorceress sarah »

The parameters are correct. Here's the function prototype from stdlib.h:

Code: Select all


char *itoa(char *buf, int i, int base);

I'll have a look at the output assembler to verify that the three buffers are indeed separate buffers when I get home from work. Thanks for the help.

EDIT 1 -

Oh, and here's the prototype for the call to LCD_PrString();

Code: Select all

extern void  LCD_PrString(char * sRamString);
And the Assembler output:

Code: Select all


(0027) void DisplayResults(void){
(0028) 	itoa(yBuf,yaw,10);
_DisplayResults:
__text_start:
083D: 50 00    MOV   A,0
083F: 08       PUSH  A
0840: 50 0A    MOV   A,10
0842: 08       PUSH  A
0843: 62 D0 00 MOV   REG[208],0
0846: 51 25    MOV   A,[yaw]
0848: 08       PUSH  A
0849: 51 26    MOV   A,[yaw+1]
084B: 08       PUSH  A
084C: 62 D0 00 MOV   REG[208],0
084F: 51 19    MOV   A,[yBuf]
0851: 08       PUSH  A
0852: 51 1A    MOV   A,[yBuf+1]
0854: 08       PUSH  A
0855: 7C 0B 65 LCALL _itoa
0858: 38 FA    ADD   SP,250
(0029) 	itoa(pBuf,pitch,10);
085A: 50 00    MOV   A,0
085C: 08       PUSH  A
085D: 50 0A    MOV   A,10
085F: 08       PUSH  A
0860: 62 D0 00 MOV   REG[208],0
0863: 51 29    MOV   A,[pitch]
0865: 08       PUSH  A
0866: 51 2A    MOV   A,[pitch+1]
0868: 08       PUSH  A
0869: 62 D0 00 MOV   REG[208],0
086C: 51 1D    MOV   A,[pBuf]
086E: 08       PUSH  A
086F: 51 1E    MOV   A,[pBuf+1]
0871: 08       PUSH  A
0872: 7C 0B 65 LCALL _itoa
(0030) 	itoa(rBuf,roll,10);
0875: 50 00    MOV   A,0
0877: 08       PUSH  A
0878: 50 0A    MOV   A,10
087A: 08       PUSH  A
087B: 62 D0 00 MOV   REG[208],0
087E: 51 21    MOV   A,[roll]
0880: 08       PUSH  A
0881: 51 22    MOV   A,[roll+1]
0883: 08       PUSH  A
0884: 62 D0 00 MOV   REG[208],0
0887: 51 17    MOV   A,[rBuf]
0889: 08       PUSH  A
088A: 51 18    MOV   A,[rBuf+1]
088C: 08       PUSH  A
088D: 7C 0B 65 LCALL _itoa
0890: 38 F4    ADD   SP,244
(0031) 	LCD_Position(1,0);
0892: 10       PUSH  X
0893: 57 00    MOV   X,0
0895: 50 01    MOV   A,1
0897: 7C 04 F4 LCALL 0x04F4
(0032) 	LCD_PrString(yBuf);
089A: 62 D0 00 MOV   REG[208],0
089D: 51 19    MOV   A,[yBuf]
089F: 08       PUSH  A
08A0: 51 1A    MOV   A,[yBuf+1]
08A2: 5C       MOV   X,A
08A3: 18       POP   A
08A4: 7C 03 B2 LCALL 0x03B2
(0033) 	LCD_Position(1,9);
08A7: 57 09    MOV   X,9
08A9: 50 01    MOV   A,1
08AB: 7C 04 F4 LCALL 0x04F4
(0034) 	LCD_PrString(pBuf);
08AE: 62 D0 00 MOV   REG[208],0
08B1: 51 1D    MOV   A,[pBuf]
08B3: 08       PUSH  A
08B4: 51 1E    MOV   A,[pBuf+1]
08B6: 5C       MOV   X,A
08B7: 18       POP   A
08B8: 7C 03 B2 LCALL 0x03B2
(0035) 	LCD_Position(0,0);
08BB: 50 00    MOV   A,0
08BD: 5C       MOV   X,A
08BE: 7C 04 F4 LCALL 0x04F4
(0036) 	LCD_PrString(rBuf);
08C1: 62 D0 00 MOV   REG[208],0
08C4: 51 17    MOV   A,[rBuf]
08C6: 08       PUSH  A
08C7: 51 18    MOV   A,[rBuf+1]
08C9: 5C       MOV   X,A
08CA: 18       POP   A
08CB: 7C 03 B2 LCALL 0x03B2
08CE: 20       POP   X
08CF: 7F       RET   

Last edited by sorceress sarah on Tue Jun 23, 2009 9:21 am, edited 1 time in total.

User avatar
fat16lib
 
Posts: 595
Joined: Wed Dec 24, 2008 1:54 pm

Re: Wii MotionPlus protocol. . . Let us hack . . .

Post by fat16lib »

Wow itoa() is non-standard! Here it is from the Arduino/Avr stdlib.h:

Code: Select all

extern char *itoa(int __val, char *__s, int __radix);
Edit - I looked at the Cypress compiler and it is has the parameters reversed from other itoa implementations.

The example I found was:

Code: Select all

void main(void)
{
   int myInt = 10;
   char OutputString[7];  // String to hold the ascii result

   itoa(OutputString, ADCResult, myInt);
   LCD_Position(0,0);
   LCD_PrString(OutputString);

}
So maybe it is just the buffer to hold the string.

User avatar
sorceress sarah
 
Posts: 100
Joined: Sat Jun 20, 2009 2:07 pm

Re: Wii MotionPlus protocol. . . Let us hack . . .

Post by sorceress sarah »

Changing the buffer declaration fixed it. This works:

Code: Select all

char  yBuf[];
char  pBuf[];
char  rBuf[];
Thanks for the pointer to the pointer (pun intended :D )

So for PSoC, the code is:

Code: Select all



//----------------------------------------------------------------------------
// C main line
//----------------------------------------------------------------------------

#include <m8c.h>        // part specific constants and macros
#include "PSoCAPI.h"    // PSoC API definitions for all User Modules
#include "stdlib.h"

#define WiiMP_BeginI2C_ADDR 0x53
#define WiiMP_ADDR 0x52
#define BANNED 0xfe
#define ActivationData 0x04

const BYTE txActBuf[] = {0xfe,  0x04};
const BYTE txZeroBuf[] = {0x00};

BYTE rxBuf[6];
BYTE * txBuf; // RAM tx buffer

int yaw, pitch, roll;  //three axes
int yaw0, pitch0, roll0;  //calibration zeroes

char  yBuf[];
char  pBuf[];
char  rBuf[];

void DisplayResults(void){
	itoa(yBuf,yaw,10);
	itoa(pBuf,pitch,10);
	itoa(rBuf,roll,10);
	LCD_Position(1,0);
	LCD_PrString(yBuf);
	LCD_Position(1,9);
	LCD_PrString(pBuf);
	LCD_Position(0,0);
	LCD_PrString(rBuf);

}

void main()
{
	BYTE status;
	int i;
	
	M8C_EnableGInt;  //  Enble the Global Interrupt
	LCD_Start();  //Start the LCD module
	
	I2Cm_Start();  //  Start the I2C in Master mode
	
	// Initialize Wii MotionPlus
	status = I2Cm_bWriteCBytes(WiiMP_BeginI2C_ADDR,txActBuf,2,I2Cm_CompleteXfer);
	
	/*  From this point on is a straight rip of the Arduino code.  */
	// Calibrate Zeros
	for (i = 0; i < 10; i++) {
	status = I2Cm_bWriteCBytes(WiiMP_ADDR,txZeroBuf,1,I2Cm_CompleteXfer); // Write a zero to request data
	status = I2Cm_fReadBytes(WiiMP_ADDR, rxBuf, 6, I2Cm_CompleteXfer);  // Read the data into the receive buffer
	yaw0+=(((rxBuf[3]>>2)<<8)+rxBuf[0])/10;        //average 10 readings for each zero
    pitch0+=(((rxBuf[4]>>2)<<8)+rxBuf[1])/10;
    roll0+=(((rxBuf[5]>>2)<<8)+rxBuf[2])/10;
	}

	for (;;){  //Loop forever
	status = I2Cm_bWriteCBytes(WiiMP_ADDR,txZeroBuf,1,I2Cm_CompleteXfer);  // Write a zero to request data
	status = I2Cm_fReadBytes(WiiMP_ADDR, rxBuf, 6, I2Cm_CompleteXfer);  // Read the data into the receive buffer
	yaw=((rxBuf[3]>>2)<<8)+rxBuf[0]-yaw0;        
	pitch=((rxBuf[4]>>2)<<8)+rxBuf[1]-pitch0;    
	roll=((rxBuf[5]>>2)<<8)+rxBuf[2]-roll0;
	DisplayResults();
	}
	
}



User avatar
sorceress sarah
 
Posts: 100
Joined: Sat Jun 20, 2009 2:07 pm

Re: Wii MotionPlus protocol. . . Let us hack . . .

Post by sorceress sarah »

One last change:

Code: Select all

	for (;;){  //Loop forever
	status = I2Cm_bWriteCBytes(WiiMP_ADDR,txZeroBuf,1,I2Cm_CompleteXfer);  // Write a zero to request data
	status = I2Cm_fReadBytes(WiiMP_ADDR, rxBuf, 6, I2Cm_CompleteXfer);  // Read the data into the receive buffer

	// Smooth the data with IIR
	yaw = yaw + alpha * ((((rxBuf[3]>>2)<<8)+rxBuf[0]-yaw0) - yaw);        
	pitch = pitch + alpha * ((((rxBuf[4]>>2)<<8)+rxBuf[1]-pitch0) - pitch);    
	roll = roll + alpha * ((((rxBuf[5]>>2)<<8)+rxBuf[2]-roll0) - roll);

	DisplayResults();
	}

Applies an infinite impulse response filter to the data. Alpha is declared as:

Code: Select all

float alpha = 0.1;
A value of 0.1 provides about 10% smoothing. Smaller values for alpha provide more smoothing but at the expense of lag. There's your trade-off: Smooth data or fast response. Alpha can be adjusted to best suit your application. This has a significant advantage over a Finite Impulse Response filter in that it is computationally cheap. It requires only 4 extra bytes (float uses single-precision in PSoC) of memory and executes quickly.

If another interface (USB, I2C, SPI, 1-Wire, UART) is added, Alpha can be set at run-time and adjusted on the fly.
Last edited by sorceress sarah on Wed Jun 24, 2009 12:03 am, edited 1 time in total.

User avatar
sorceress sarah
 
Posts: 100
Joined: Sat Jun 20, 2009 2:07 pm

Re: Wii MotionPlus protocol. . . Let us hack . . .

Post by sorceress sarah »

There's still something wrong and I think it's in the original Arduino code. I get results on the LCD:

at rest:

Yaw = 4462x
Pitch = 462x
Roll = 54462x

where x is uncertain. Has anyone tried out the Arduino code on an Arduino?

User avatar
fat16lib
 
Posts: 595
Joined: Wed Dec 24, 2008 1:54 pm

Re: Wii MotionPlus protocol. . . Let us hack . . .

Post by fat16lib »

The buffers are still part of the problem.

Code: Select all

  char yBuf[];
allocates a single byte, the string's zero byte, with most compilers. Try a dimension of 7 like this:

Code: Select all

  char yBuf[7];


Looks like the three strings overlay each other. Note the common sub string 462.

Locked
Please be positive and constructive with your questions and comments.

Return to “General Project help”