Finally…
Hey folks, after a whole month of tears, blood and sweat – OK I apologize it wasn’t that bad, but it was close! – of trying(and trying and trying…) to interface a Nintendo Wii nunchuck with a dsPIC30f4011, I can proudly say that finally, I have succeeded. This little side project of mine is the reason why I haven’t posted anything substantial in this blog for quite a while. While everyone is flying away with their servos and steppers I’m somewhere trying to figure out what I was doing wrong. But the sleepless nights were all worth it just to see that UART console display something else aside from “0 0 255 255 255 255 255” (T.T). But nevermind my silly attachments to the piece of plastic, let’s talk I2C(or maybe not I don’t know how far I’ll get tonight). The demonstration I’ll have on this post will be using a Wii nunchuck to control a servo motor, when the ‘C’ button is pressed – the servo can be controlled using the analog stick of the nunchuck, and when the ‘Z’ button is pressed – the accelerometer is utilised and is used to control the movement of the servo, only one axis is used for this demo, the x axis in particular. I was going to use at least two but I don’t have another servo to do it, I spent last night trying to use the stepper as a second joint, but had problems as the driver chip used to control the servo needed more juice(the pic voltage had to be set low for the Wii nunchuck – more of that later). Now, time for things to get messy:
The Nintendo Wii Nunchuck
You’ve seen it at least once in your life if there’s a child in your household(that would be me in our household). The Nintendo Wii nunchuck is a very cheap and affordable sensor to use(€14 on ebay for the official one, and around €7 for the knockoff one), specially since it has an accelerometer inside that costs a bit more than your conventional sensor. The Wii sensor has two buttons C and Z on the top, which are basically just push buttons, an analog stick which are just two potentiometers, and as I’ve said above – an accelerometer. The only catch with it is the way it communicates – I2C. And luckily enough the dsPIC has it’s own I2C module that I can utilise.
The Accelerometer
‘So what is an accelerometer?’ one might ask. In a nutshell an accelerometer is a device that measures – yes you guessed it – acceleration. This can be static or dynamic acceleration, static being the acceleration due to the earths gravity, and dynamic when one actually moves the device and cause vibrations. By measuring the static acceleration one can find out how much an object is tilted, and the dynamic one measures how much an object is accelerating due to actual movement. The Wii nunchucks accelerometer supplies us with 3 axis data types – the X, Y, and Z planes.
As it can be seen the actual sensor is tiny, but in reality it’s much smaller than that as what can be seen is the casing around it used to protect the very sensitive insides. There’s a few ways on how an accelerometer operates: it could be by changing capacitance, heat transfer, piezoelectric – and the list goes on. Surprisingly, an accelerometer has moving parts(the wii nunchucks one anyway), the way it works is that by moving it or creating vibrations causes tiny “springs” inside the accelerometer to bounce around its walls and varies the overall capacitance of the sensor, which in turn gives us a signal when voltage is passed through it. I’ll go more in depth on this topic later on if I decide to go this route(which is very likely), but for now we can just accept the fact that an accelerometer gives us the orientation of the Wii nunchuck and tells us when it moves in space(no one can hear you scream, sorry I just can’t help it!).
The I2C Module
As mentioned above; the method that the nunchuck communicates is through ‘I2C’. I2C is one of the communications that’s available with the dsPIC30f4011. The most common communication we have used in class(the only one I think actually) is the UART module. There are other types available with the dsPIC such as CAN, but I2C is the only option when it comes to communicating with the nunchuck since this is how it was originally built and programmed, and this is the way the actual Wii console communicates with it. I will try to go through as much as I’ve learned by myself in explaining the I2C module but forgive me if I make any false statements as I am but a noob when it comes to this way of communication. Oh and if anyone has noticed, the SDA and SCL pins are actually using the same pins as the programming pins of the dsPIC30f4011 which is also the UART RX and TX pins and so it has to be removed everytime you want to program it. UART on the other hand; can still be used, this can be done by sending the data to the secondary uart pin set “U2TX” and “U2RX” which are pins 27 and 28 respectively. All one has to do is use this secondary UART and the communication can still be successful, this is useful for people who want to use the USB to Serial dongle that came in the ziploc bag. Instead of the normal UART configuration you put this instead:
__C30_UART=2; // Set the secondary uart as the primary U2BRG = 48; // 38400 baud @ 30 MIPS U2MODEbits.UARTEN = 1; // Enable UART 2
After doing so all you have to do is move the U1RX pin connection to the U2RX pin and move the U1TX pin to the U2TX pin and proceed as you would with using the UART normally.
I2C Baud Rate Generator
A way of setting the clock frequency of the I2C module is by changing the I2CBRG value in the register. The clock frequency is very important as this determines how fast you’re going to read from the nunchuck. And believe you me it is very temperamental. If you try to read from the nunchuck too often and too quickly – it will give bad readings which is a no no. This is the main culprit on why I spent so long trying to get it to work, I still cringe everytime I think about it. In the end I set the Frequency to 100kHz and everything worked as it should. The formula for calculating the I2CBRG is on page 110 of the dsPIC30f4011 data sheet or you can just look below. My value was calculated at 272 but I’ve been trying to push it ever since and so I’m using 75 now without any issues(so far anyway). My FCY is set to 30MIPS.
The Master and Margarita….and the slave
The I2C consists of only 2 pins: the SDA or data line, and SCL or clock line which are pins 26 and 25 respectively on the dsPIC30f4011. The data line is – surprise surprise – responsible for sending the data while the clock line controls the data flow. The amazing thing about I2C is that you can virtually hook up any number of devices, which will act as “slaves”, on the bus and call on them by just calling out their “adresses” which I’ll go into more detail in a bit. There’s two types of devices connected on the I2C bus – the Master, and the slave. The master is the one in charge at that certain point in time and can initiate a “Start” sequence and communicate with any slave that it wishes – so long that it knows its address. The master can transmit and receive data whilst the slave can only do whatever the master tells it to – befitting of its title. In my setup on this demo, the dsPIC acts as a master and the nunchuck will act as the slave. If the master calls the address of a slave it can ask for its contents in byte chunks. The wii nunchuck only has 6 usable byte chunks which contains the information that we need. Below is the table which shows what these bytes contain:
Everytime a byte is received by the Master an ACK or a positive acknowledge or a high pin is sent to the slave to tell it to move to the next byte, when all the bytes have been read – an NACK or a negative acknowledge is sent or a low pin to tell the slave that transmission is done and that he is no longer needed.
Nunchuck special handshake and I2C read sequence
Nintendo is a great company, I consider myself a big fanboy(what I’d give to shake Shigeru Miyamoto’s hand). They’re always the first to innovate when it comes with the video game industry, they have a hold of great intellectual property, and it’s expected that they protect this. Why am I rambling about this you ask? Well because of the fact that the Wii nunchuck are one of these guarded(not well enough) pieces of technology. The data is encrypted and the nunchuck wouldn’t respond if you tried to talk to it using the conventional I2C sequence which is:
- (As master talking to a slave) Open the I2C bus
- Call out the slave’s address and send 0
- Close I2C bus
- (If data is to be acquired)Open bus again and call out the address once more then send 0 and then close it again
- Request the data by talking to the “Read address” of the device which is “Write address + 1”
- Close bus once transmission is done
This will not work with the Wii nunchuck, even though we know its write address(which is 52 by the way, A4 in hex). For the communication to work – after sending the write adress for the first time – “0x40” must be sent before 0.
StartI2C(); IdleI2C(); MasterWriteI2C(0xA4); MasterWriteI2C(0x40); MasterWriteI2C(0x00); StopI2C();
And again for the knockoff versions of the nunchuck it’s different once more, there are not only one but TWO handshakes this time around, the cheeky bastards:
StartI2C(); IdleI2C(); MasterWriteI2C(0xA4); MasterWriteI2C(0xF0); MasterWriteI2C(0x55); StopI2C(); StartI2C(); IdleI2C(); MasterWriteI2C(0xA4); MasterWriteI2C(0xFB); MasterWriteI2C(0x00); StopI2C();
Just from looking up and reading around I’ve found out these information, here are the ones that I think are useful as these are the settings that I used experimenting around:
- Wii nunchuck write address = 52 or A4 in hex
- Wii nunchuck read address = 53 or A5 in hex
- Wii nunchuck operating frequency = 100khz normally but has been known to work up to 400khz as long as the slew rate control is turned on.
- Has the special handshake A4 – 40 – 00 for the original and A4 – F0 – 55 – A4 – FB – 00 for the knockoff ones.
- Takes in 3.3V as an input but was resetting when testing at this power level so I’m working mine with 3.8V.
Taking it out for a stroll with a Servo motor
With the hard part out of the way, I was finally able to test out my nunchuck. It took me a month just to accomplish my first goal which was to turn an LED on when I press the Z button! Without further ado: here’s my simple test.
Now I know it’s pretty basic but I worked with what I had, If I had another servo I would have been more “inspired” and do a pan and tilt system – which I plan to do in the near future when I get my hands on another servo. As I said earlier I tried to incorporate the stepper into it as well, using it to act like a servo, but alas I had to power down my pic to 3.8V to accomodate the Wii nunchuck. The digital signals out weren’t enough to control the motor drivers and so all I got off the stepper motor was a slight vibration. I had the code all figured out and everything! But this will have to do for now, very basic, x axis controls the servo. Pressing “C” takes you to analog stick mode and Z button takes you to Accelerometer mode. A thing that I found out while experimenting on it was that the values that the nunchuck is giving out isn’t linear. Not linear as in: for the x axis accelerometer the figures given were around 180, in midband it was around 500, and fully tilted right was 590. I still have to do a bit more reading so watch out for a better demo in the near future. For now I’m just happy that it works. Below is the wiring diagram and the code, I don’t think I need to explain it as it’s very simple and the complicated parts: I already explained on the top. In a nutshell I’m taking the 6 Byte values, storing them in an array and manipulating it. That is all I can think for now, I’ll add in more when I think I missed anything.
// // This program interfaces a Wii nunchuck with the dsPIC using the I2C module // // Program written by Gian Lorenzo // Last edited 03/11/2012 1:26AM // #include <libpic30.h> #include <p30f4011.h> #include <stdio.h> #include <xc.h> #include <i2c.h> #define Scale(a) ((a^0x17)+0x17) // Configuration settings _FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, Fcy=30MHz _FWDT(WDT_OFF); // Watchdog timer off _FBORPOR(MCLR_DIS); // Disable reset pin // Function prototypes void setup(); // Configure pins etc unsigned int read_analog_channel(int n); // Reading analog channel void nunchuck_init(); // Nunchuck initialisation/Handshake void nunchuck_read(unsigned char*); // Reading data from the Wii nunchuck unsigned char wii_data[20]; // Nunchuk data array void pause(unsigned int ms); // Pause function(in ms) void forward(); // Moves the Servo forward void back(); // Moves the Servo backward int main() { unsigned int Z_btn, C_btn, jx,jy,ax,ay,az; // Buttons on nunchuk pause(2000); setup(); // Set up which pins are which and set up i2c and configs pause(1000); // 1 second startup delay nunchuck_init(); // Initialise nunchuck while(1) { nunchuck_read(wii_data); // Read data from nunchuk C_btn = 0; Z_btn = (wii_data[5] >> 0 & 1) ^ 0b1; // Extract Z button bit if((((wii_data[5] >> 1 & 1) ^ 0b1) == 1 && ((wii_data[5] >> 0 & 1) ^ 0b1) == 0) || (((wii_data[5] >> 1 & 1) ^ 0b1) == 0 && ((wii_data[5] >> 0 & 1) ^ 0b1) == 1)) C_btn =1; // Extract C button bit jx = wii_data[0]; // Extract joystick x byte jy = wii_data[1]; // Extract joystick y byte ax = wii_data[2]; // Extract accelerometer x byte ay = wii_data[3]; // Extract accelerometer y byte az = wii_data[4]; // Extract accelerometer z byte //if ((wii_data[5] >> 2) & 1) ax += 2; //if ((wii_data[5] >> 3) & 1) ax += 1; //if ((wii_data[5] >> 4) & 1) ay += 2; //if ((wii_data[5] >> 5) & 1) ay += 1; //if ((wii_data[5] >> 6) & 1) az += 2; //if ((wii_data[5] >> 7) & 1) az += 1; pause(1); printf("%1d\r %1d\r %3d\r %3d\r %3d\r %3d\r %3d\r\n", //\r\n Z_btn, C_btn, jx, jy, ax, ay, az); _LATD0 = 0; _LATD2 = 0; if(C_btn) { _LATD2 = 1; pause(10); PDC1 = (550 + (8.1*jx) - 162); } if(Z_btn) { _LATD0 = 1; pause(1); if(ax<100) back(); else if(ax>130) forward(); } } return 0; } void forward() { if(PDC1 < 2010) { PDC1 = PDC1 + 20; pause(.5); } } void back() { if(PDC1 > 550) { PDC1 = PDC1 - 20; pause(.5); } } void setup() { // Setup UART __C30_UART=2; // Use secondary UART U2BRG = 48; // 38400 baud @ 30 MIPS U2MODEbits.UARTEN = 1; // Enable UART // Configure D ports as a digital outputs LATD = 0; TRISD = 0b11110000; // Configure analog inputs TRISB = 0x01FF; /* Port B all inputs */ ADPCFG = 0xFF00; /* PORTB 0-7 are analog inputs */ ADCON1 = 0; /* Manually clear SAMP to end sampling, start conversion*/ ADCON2 = 0; /* Voltage reference from AVDD and AVSS */ ADCON3 = 0x0005; /* Manual Sample, ADCS=5 -> Tad = 3*Tcy = 0.1us */ ADCON1bits.ADON = 1; /* Turn ADC ON */ // Configure PWM // PWM period = Tcy * prescale * PTPER = 0.33ns * 64 * 9470 = 20ms PWMCON1 = 0x00FF; // Enable all PWM pairs in complementary mode PTCON = 0; _PTCKPS = 3; // prescale=1:64 (0=1:1, 1=1:4, 2=1:16, 3=1:64) PTPER = 9470; // 20ms PWM period (15-bit period value) PDC1 = 0; // 0% duty cycle on channel 1 (max is 65536) PDC2 = 0; // 0% duty cycle on channel 2 (max is 65536) PDC3 = 0; // 0% duty cycle on channel 3 (max is 65536) PTMR = 0; // Clear 15-bit PWM timer counter _PTEN = 1; // Enable PWM time base // Configure I2c I2CCONbits.I2CSIDL = 1; // Stop module in idle mode I2CCONbits.A10M = 0; // 7-bit address I2CCONbits.DISSLW = 0; // slew rate control. Required to be on for 400kHZ I2CBRG = 272; // 272 for 100kHz at FCY = 30MHz formula next line // I2CBRG = (((1/rate)-900ns)*FCY)-1 I2CCONbits.I2CEN = 1; // Enable i2c } void nunchuck_init(void) { StartI2C(); // Open I2C bus IdleI2C(); // Wait for last command to finish MasterWriteI2C(0xA4);// Call device adress 52(Wii nunchuck) MasterWriteI2C(0x40);// Handshake protocol for wii nunchuck MasterWriteI2C(0x00); StopI2C(); printf("connected\r\n"); } void nunchuck_read(unsigned char* array) { int idx; // Counter // ---- Request measurement ---- // StartI2C(); // Open I2C bus IdleI2C(); // Wait for last command to finish MasterWriteI2C(0xA4); // Call the adress you want to read from MasterWriteI2C(0x00); StopI2C(); // Stop I2C pause(5); // 3 ms or more required or acquisition goes wrong // Request reading StartI2C(); // Open I2C bus IdleI2C(); // Wait for last command to finish MasterWriteI2C(0xA5); // 0XA5 is the read adress of the wii nunchuck(52+1) IdleI2C(); // Wait for last command to finish // Bytes reception for(idx = 0; idx < 5; idx++) { array[idx] = MasterReadI2C(); // Store the values into an array AckI2C(); // Send an ACK to notify nunchuck to send next byte IdleI2C(); // Wait for last command to finish } pause(1); array[5] = MasterReadI2C(); // Last byte to be read NotAckI2C(); IdleI2C(); // Send a NACK to tell nunchuck that reading is done StopI2C(); // Stop I2C } // Analogue input reading unsigned int read_analog_channel(int channel) { ADCHS = channel; // Select the requested channel ADCON1bits.SAMP = 1; // start sampling __delay32(30); // 1us delay @ 30 MIPS ADCON1bits.SAMP = 0; // start Converting while (!ADCON1bits.DONE); // Should take 12 * Tad = 1.2us return ADCBUF0; } void pause(unsigned int ms) { __delay32(30000L*ms); // 1ms Pause = (ms * FCY/1000) }
Hi Gian,
Excellent work… i2c interfacing is a marketable skill. This specific project is also genuinely useful piece of work which could have a number of applications.
Have you signed up to Linked In yet? If so, you should be developing an online portfolio for engineering work between this and your Robo Sumo / Industrial Computing wikis you have ample material to encourage prospective employers or internship host companies.
Well done indeed! d_b
Hey Damon,
Thanks for the feedback. I’ve been planning to setup a LinkedIn account for the past couple of months now, just always seem to forget. Thanks for the advice. Watch out for my next post(I think) as I’m currently working on interfacing the Wii motion plus sensor this time around, This sensor has a 3 axis gyroscope so if you can just imagine an accelerometer and a gyroscope working together. I’m at the final stages of interfacing it now I just have to clean up my code a bit and filter out some bad data that I’m getting. Anyhow thanks for the comment.
Gian