Razor 9DOF IMU – I2C to Arduino
January 3, 2011 7 Comments
This is obviously related to my endless post about the home made quadcopter…
But it’s a much more generic problem, so here I am, talking about it in a post of its own…
The default way of interacting with Sparkfun’s 9DOF Razor IMU , as per its documentation, is through its serial interface.
This was ok for me initially, but now I’m about to re-design the quadcopter to use an Arduino as the main board instead of the initial FEZ Domino, and the problem with the Atmega 328 is that it has only 1 hardware serial port, which I use for debugging. I could obviously use a software serial library to talk to the IMU, but this poses several problems, among which the fact that it uses a lot of CPU cycles…
And anyhow, WHY not use the hardware I2C port that not only supports plenty of different peripherals on the same bus, but it’s already used on the IMU ?
So here’s the theory:
- I’ve installed this code base on the IMU, which is basically Arduino code that aggregates the data from all the sensors and does some filtering, in order to provide you with a pretty acceptable AHRS
- this already uses the I2C bus to get the data from 2 of the sensors (the HMC5843 compass and ADXL345 accelerometer)
- the IMU’s Atmega is already acting as I2C master, and given that we’ll connect the main Arduino board on the same bus, this will have to be a slave. Slightly counter intuitive, but that’s ok, this main board does its balancing job and just gets told by the IMU master whenever new sensors data is available
Hardware
This is the hard part, as the Razor IMU board does NOT have the I2C bus connections readily available.
It was and still is a pain, as I had to connect to some vias on the board, which only worked for 1 of the signals, and then I’ve soldered a wire directly to one of the MCU’s pins, which is really NOT pretty ! (and also broke 1 hour later…!)
Sparkfun guys, if you do read this, then pleeeease, do add a simple connector on this board for the I2C bus !
Thist first version, lasted only about 1 hour, afer which one of the wires broke loose…
In the final version (that you can see at the beginning of the post), the soldering is so bad mechanically speaking that I had to drown everything in epoxy glue ! Let’s hope this will be sturdy enough, as I don’t think I’ll ever be able to remove that glue to solder them back again…
Another problem specific to my configuration is that the Arduino is 5V whereas the IMU is 3.3V. So here comes this little Logic Level Converter from Sparkfun:
Important point here, as you can see in the previous picture, you’ll have to solder both SDA and SCL to TX lines (rather than a TX and a RX), as the I2C protocol is bi-directional on both lines ! (see the comments on the Sparkfun’s page for more details)
Software
This is quite easy:
On the IMU
It’s Arduino code and it already uses the Wire library, so the changes are quite easy. Rather than doing Serial.print() we do Wire.send(), AFTER having started a connection with the main board, which is a I2C slave:
#if PRINT_EULER_BINARY == 1 Wire.beginTransmission(PILOT_ADDR); // make everything positive (+ 200 ) and include first 2 decimals ( * 100) tempint=(ToDeg(roll) + 200) * 100; //Roll (degrees) * 100 in 2 bytes _buffer[0]=tempint & 0xff; _buffer[1]=(tempint >> 8) & 0xff; tempint=(ToDeg(pitch) + 200) * 100; //Pitch (degrees) * 100 in 2 bytes _buffer[2]=tempint & 0xff; _buffer[3]=(tempint >> 8) & 0xff; tempint=(ToDeg(yaw) + 200) * 100; //Yaw (degrees) * 100 in 2 bytes _buffer[4]=tempint & 0xff; _buffer[5]=(tempint >> 8) & 0xff; ck = 0; for (int i=0; i<6; i++) { Wire.send(_buffer[i]); ck += _buffer[i]; //Calculates checksum } Wire.send(ck); Wire.endTransmission(); #endif
On the main Arduino board
Just connect to I2C as a slave (with the same address as the IMU expects you to have 🙂 ) and then register a handler that will be called whenever the master (the IMU) sends anything:
#include <Wire.h> #define I2C_SLAVE_ADDR 0x2A #define MSG_SIZE 7 byte _buff[MSG_SIZE], _ck; void setup_RazorIMU(){ Wire.begin(I2C_SLAVE_ADDR); // start as slave Wire.onReceive(IMUDataHandler); // what to call when new IMU data has arrived } void IMUDataHandler(int numBytes){ if(_imuDataStatus >= 0){ // nobody is using this data right now _imuDataStatus == -1; if(MSG_SIZE == numBytes) { for(i = 0; i < MSG_SIZE; i++) _buff[i] = Wire.receive(); // the checksum is simply the sum module 256 of all the data (excluding itself 🙂 ) _ck = 0; for(i = 0; i < MSG_SIZE - 1; i ++) _ck += _buff[i]; // last byte is the check sum, it should match !!! _imuDataStatus == -2; if (_ck == _buff[MSG_SIZE - 1]){ _roll = getIntFrom2Bytes(_buff, 0); _pitch = getIntFrom2Bytes(_buff, 2); _yaw = getIntFrom2Bytes(_buff, 4); // data updated correctly _imuDataStatus == 1; } } } } int getIntFrom2Bytes(byte buff[], byte pos){ return (buff[pos] | buff[pos + 1] << 8) - 20000; }
Thanks! for your documentation: Arduino as I2C slave, sample code, etc. all got me headed down the path of successfully transmitting data from SparkFun’s latest Razor IMU to my Arduino. This should be on SparkFun’s site.
I2C pins nicely available on this Razor:
https://www.sparkfun.com/products/14001
Hi Kevin,
Super glad to hear it’s been so useful for you !
Feel free to ask Sparkfun to put a link on their page to my post, I’d be honoured… 🙂
Thanks,
Dan
So did you update the AHRS firmware to work with the ITG-3200 Gyros? It looks like the AHRS firmware does not yet support this part.
Not really… following my burning of the Atmega chip on the board, I’m simply using the I2C accelerometers and compass combined with the gyros froma Wii motion plus, and I do the DCM matrix calculation on another Arduino board.
See https://trandi.wordpress.com/2011/01/16/wii-motion-plus-and-arduino/ for the Wii motion plus gyroscopes…
Did you get any feedback from Sparkfun, related to exposing the I2C pins in their design?
Kind regards
Jesper
Nope, I don’t think they read my blog 🙂 and I haven’t sent them an e-mail…
However, they now have introduced these which communicate over I2C. They don’t have an on board uC to do the DCM calculations for you, but still, you can access ALL the sensors nicely over the same bus and without blocking your hardware serial port on the main board !
It seems that finally, SparkFun has listened to my request 🙂
They’ve actually created a new IMU, that among other things (like a much more powerful CPU) also have the I2C bus exposed:
http://www.sparkfun.com/products/10082
Great work, and thanks SparkFun !
dan