Sparkfun 6dof IMU - ITG-3200 + ADXL345

Hi there.

I have bought 4 of these 6DoF IMU Digital Combo Boards from Sparkfun and attached them to (separate) Arduino Pro Mini 3.3V boards to control them. I'm trying to get an IMU running with this setup.
As far as I can see it should be a case of wiring them up and then reading the gyro and accel values and feeding them into some IMU code. But that doesn't work. All four boards have the same problem, so I think it's unlikely to be hardware related (unless it's a systemic design issue - again unlikely).

I have rolled my own code to read the gyro and accel, and then fed it into an IMU algorithm. The orientation keeps drifting even when the device is just sitting on my desk. Further investigation shows that the X axis gyro values aren't stable - there is a consistent bias in there.
I initialise the gyro and take the average of 256 samples to determine the bias for removal from subsequent readings, but this doesn't do the trick. The values keep drifting.
I've tried everything I can think of - high gyro sample rates, low sample rates, all of the low pass filter selections, over-sampling. Nothing makes a difference.
I have used the driver code being developed on Google Code to get the values from the gyro - no difference there either.

I'm at my wit's end. Has anyone had any success with this board and an arduino to make an IMU? Or perhaps any suggestions?

Links that I couldn't put in the first post:

IMU Digital Combo Board.

IMU algorithm.

ITG-3200 driver code.

Post a picture of your wiring, well lighted.

Post your code.

Thanks for the reply. The wiring is fine - the accelerometer on the board is working perfectly and it's all done via the I2C interface.

I have to post the files one by one - they're too big to all fit in the 9500 character limit...

imu_test.pde:

/*
* Set up the accelerometer and gyroscope in the IMU.
 * Read data from them and spit it out on the serial port.
 *
 * JRW 12/11/10
 */

#include <Wire.h> /

#include "i2c.h"
#include "adxl345.h"
#include "itg3200.h"
#include "IMUfilter.h"

#define GYRO 0x68
#define ACCEL 0x53
#define FILTER_RATE 0.01

ADXL adxl(ACCEL);
ITG itg(GYRO);
IMUfilter imuFilter(FILTER_RATE, 0.6);
int count=0;
long lastmillis=0;

float ax=0,ay=0,az=0,gx=0,gy=0,gz=0;
float loops=0;
float sumx,sumy,sumz=0;

void setup() {
  i2c.begin();
  Serial.begin(57600);
  Serial.println("I2C initialised.");
  adxl.init();
  itg.init();
  itg.wake();
  
  while (!itg.dataReady()) {
  }
  
  unsigned char whoami=i2c.readRegister(ACCEL,ADXL_WHO_AM_I);
  Serial.print("Accel: ");
  Serial.println(whoami,HEX);

  whoami=i2c.readRegister(GYRO,ITG_WHO_AM_I);
  Serial.print("Gyro: ");
  Serial.println(whoami,HEX);
  Serial.print("Temp: ");
  Serial.println(itg.getTemp());

  /*Serial.println(i2c::readRegister(ACCEL, ADXL_TIME_INACT),DEC);
   Serial.println(i2c::readRegister(ACCEL, ADXL_THRESH_INACT),DEC);
   Serial.println(i2c::readRegister(ACCEL, ADXL_ACT_INACT_CTL),BIN);
   Serial.println(i2c::readRegister(ACCEL, ADXL_POWER_CTL),BIN);
   delay(5000);*/
  //Serial.println(i2c::readRegister(GYRO, ITG_PWR_MGM),BIN);
  //delay(2000);
}

void loop() {

    while (!adxl.dataReady()) {
    }
    if (adxl.sleeping()) {
      Serial.println("Sleeping");
    } 
    else 
    {
      ax+=adxl.getX();
      ay+=adxl.getY();
      az+=adxl.getZ();
      /*Serial.print(ax);
       Serial.print('\t');
       Serial.print(ay);
       Serial.print('\t');
       Serial.println(az);*/
    }
    while (!itg.dataReady()) {
    }
    gx+=(float)itg.getX();
    gy+=(float)itg.getY()*PI/180.0;
    gz+=(float)itg.getZ()*PI/180.0;
    loops++;
    //}

  long current=millis();
  if (current-lastmillis>FILTER_RATE*1000) {
    lastmillis=current;
    imuFilter.updateFilter(gx/loops*PI/180.0, gy/loops*PI/180.0, gz/loops*PI/180.0, ax, ay, az);
    imuFilter.computeEuler();
    sumx+=gx/loops*FILTER_RATE;
    sumy+=gy/loops*FILTER_RATE;
    sumz+=gz/loops*FILTER_RATE;
    /*Serial.print(ax);
     Serial.print('\t');
     Serial.print(ay);
     Serial.print('\t');
     Serial.println(az);*/

    if (count++>10) {
      Serial.print(imuFilter.getPitch()/PI*180.0);
      Serial.print('\t');
      Serial.print(imuFilter.getRoll()/PI*180.0);
      Serial.print('\t');
      Serial.print(imuFilter.getYaw()/PI*180.0);
      Serial.print('\t');
      Serial.print(sumx);
      Serial.print('\t');
      Serial.print(sumy);
      Serial.print('\t');
      Serial.println(sumz);
      count=0;
      /*Serial.print(itg.getX());
       Serial.print('\t');
       Serial.print(itg.getY());
       Serial.print('\t');
       Serial.print(itg.getZ());
       Serial.print('\t');
       Serial.print(adxl.getX());
       Serial.print('\t');
       Serial.print(adxl.getY());
       Serial.print('\t');
       Serial.println(adxl.getZ());*/
      /*Serial.print(imuFilter.q0);
       Serial.print('\t');
       Serial.print(imuFilter.q1);
       Serial.print('\t');
       Serial.print(imuFilter.q2);
       Serial.print('\t');
       Serial.println(imuFilter.q3);*/
    }
  ax=0,ay=0,az=0,gx=0,gy=0,gz=0;
  loops=0;
  } 
  else {
    //Serial.println("Waiting"); 
  }
}

imufilter.cpp:

/**
 * @section LICENSE
 *
 * Copyright (c) 2010 ARM Limited
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @section DESCRIPTION
 *
 * IMU orientation filter developed by Sebastian Madgwick.
 *
 * Find more details about his paper here:
 *
 * http://code.google.com/p/imumargalgorithm30042010sohm/ 
 */

/**
 * Includes
 */
#include "IMUfilter.h"

IMUfilter::IMUfilter(double rate, double gyroscopeMeasurementError){

    firstUpdate = 0;
    
    //Quaternion orientation of earth frame relative to auxiliary frame.
    AEq_1 = 1;
    AEq_2 = 0;
    AEq_3 = 0;
    AEq_4 = 0;
    
    //Estimated orientation quaternion elements with initial conditions.
    SEq_1 = 1;
    SEq_2 = 0;
    SEq_3 = 0;
    SEq_4 = 0;

    //Sampling period (typical value is ~0.1s).
    deltat = rate;
    
    //Gyroscope measurement error (in degrees per second).
    gyroMeasError = gyroscopeMeasurementError;
    
    //Compute beta.
    beta = sqrt(3.0 / 4.0) * (PI * (gyroMeasError / 180.0));

}

void IMUfilter::updateFilter(double w_x, double w_y, double w_z, double a_x, double a_y, double a_z) {

    //Local system variables.

    //Vector norm.
    double norm;
    //Quaternion rate from gyroscope elements.
    double SEqDot_omega_1;
    double SEqDot_omega_2;
    double SEqDot_omega_3;
    double SEqDot_omega_4;
    //Objective function elements.
    double f_1;
    double f_2;
    double f_3;
    //Objective function Jacobian elements.
    double J_11or24;
    double J_12or23;
    double J_13or22;
    double J_14or21;
    double J_32;
    double J_33;
    //Objective function gradient elements.
    double nablaf_1;
    double nablaf_2;
    double nablaf_3;
    double nablaf_4;

    //Auxiliary variables to avoid reapeated calcualtions.
    double halfSEq_1 = 0.5 * SEq_1;
    double halfSEq_2 = 0.5 * SEq_2;
    double halfSEq_3 = 0.5 * SEq_3;
    double halfSEq_4 = 0.5 * SEq_4;
    double twoSEq_1 = 2.0 * SEq_1;
    double twoSEq_2 = 2.0 * SEq_2;
    double twoSEq_3 = 2.0 * SEq_3;

    //Compute the quaternion rate measured by gyroscopes.
    SEqDot_omega_1 = -halfSEq_2 * w_x - halfSEq_3 * w_y - halfSEq_4 * w_z;
    SEqDot_omega_2 = halfSEq_1 * w_x + halfSEq_3 * w_z - halfSEq_4 * w_y;
    SEqDot_omega_3 = halfSEq_1 * w_y - halfSEq_2 * w_z + halfSEq_4 * w_x;
    SEqDot_omega_4 = halfSEq_1 * w_z + halfSEq_2 * w_y - halfSEq_3 * w_x;

    //Normalise the accelerometer measurement.
    norm = sqrt(a_x * a_x + a_y * a_y + a_z * a_z);
    a_x /= norm;
    a_y /= norm;
    a_z /= norm;

    //Compute the objective function and Jacobian.
    f_1 = twoSEq_2 * SEq_4 - twoSEq_1 * SEq_3 - a_x;
    f_2 = twoSEq_1 * SEq_2 + twoSEq_3 * SEq_4 - a_y;
    f_3 = 1.0 - twoSEq_2 * SEq_2 - twoSEq_3 * SEq_3 - a_z;
    //J_11 negated in matrix multiplication.
    J_11or24 = twoSEq_3;
    J_12or23 = 2 * SEq_4;
    //J_12 negated in matrix multiplication
    J_13or22 = twoSEq_1;
    J_14or21 = twoSEq_2;
    //Negated in matrix multiplication.
    J_32 = 2 * J_14or21;
    //Negated in matrix multiplication.
    J_33 = 2 * J_11or24;

    //Compute the gradient (matrix multiplication).
    nablaf_1 = J_14or21 * f_2 - J_11or24 * f_1;
    nablaf_2 = J_12or23 * f_1 + J_13or22 * f_2 - J_32 * f_3;
    nablaf_3 = J_12or23 * f_2 - J_33 * f_3 - J_13or22 * f_1;
    nablaf_4 = J_14or21 * f_1 + J_11or24 * f_2;

    //Normalise the gradient.
    norm = sqrt(nablaf_1 * nablaf_1 + nablaf_2 * nablaf_2 + nablaf_3 * nablaf_3 + nablaf_4 * nablaf_4);
    nablaf_1 /= norm;
    nablaf_2 /= norm;
    nablaf_3 /= norm;
    nablaf_4 /= norm;

    //Compute then integrate the estimated quaternion rate.
    SEq_1 += (SEqDot_omega_1 - (beta * nablaf_1)) * deltat;
    SEq_2 += (SEqDot_omega_2 - (beta * nablaf_2)) * deltat;
    SEq_3 += (SEqDot_omega_3 - (beta * nablaf_3)) * deltat;
    SEq_4 += (SEqDot_omega_4 - (beta * nablaf_4)) * deltat;

    //Normalise quaternion
    norm = sqrt(SEq_1 * SEq_1 + SEq_2 * SEq_2 + SEq_3 * SEq_3 + SEq_4 * SEq_4);
    SEq_1 /= norm;
    SEq_2 /= norm;
    SEq_3 /= norm;
    SEq_4 /= norm;

    if (firstUpdate == 0) {
        //Store orientation of auxiliary frame.
        AEq_1 = SEq_1;
        AEq_2 = SEq_2;
        AEq_3 = SEq_3;
        AEq_4 = SEq_4;
        firstUpdate = 1;
    }

}

void IMUfilter::computeEuler(void){

    //Quaternion describing orientation of sensor relative to earth.
    double ESq_1, ESq_2, ESq_3, ESq_4;
    //Quaternion describing orientation of sensor relative to auxiliary frame.
    double ASq_1, ASq_2, ASq_3, ASq_4;    
                              
    //Compute the quaternion conjugate.
    ESq_1 = SEq_1;
    ESq_2 = -SEq_2;
    ESq_3 = -SEq_3;
    ESq_4 = -SEq_4;

    //Compute the quaternion product.
    ASq_1 = ESq_1 * AEq_1 - ESq_2 * AEq_2 - ESq_3 * AEq_3 - ESq_4 * AEq_4;
    ASq_2 = ESq_1 * AEq_2 + ESq_2 * AEq_1 + ESq_3 * AEq_4 - ESq_4 * AEq_3;
    ASq_3 = ESq_1 * AEq_3 - ESq_2 * AEq_4 + ESq_3 * AEq_1 + ESq_4 * AEq_2;
    ASq_4 = ESq_1 * AEq_4 + ESq_2 * AEq_3 - ESq_3 * AEq_2 + ESq_4 * AEq_1;

    //Compute the Euler angles from the quaternion.
    phi = atan2(2 * ASq_3 * ASq_4 - 2 * ASq_1 * ASq_2, 2 * ASq_1 * ASq_1 + 2 * ASq_4 * ASq_4 - 1);
    theta = asin(2 * ASq_2 * ASq_3 - 2 * ASq_1 * ASq_3);
    psi = atan2(2 * ASq_2 * ASq_3 - 2 * ASq_1 * ASq_4, 2 * ASq_1 * ASq_1 + 2 * ASq_2 * ASq_2 - 1);

}

double IMUfilter::getRoll(void){

    return phi;

}

double IMUfilter::getPitch(void){

    return theta;

}

double IMUfilter::getYaw(void){

    return psi;

}

imufilter.h:

/**
 * @section LICENSE
 *
 * Copyright (c) 2010 ARM Limited
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @section DESCRIPTION
 *
 * IMU orientation filter developed by Sebastian Madgwick.
 *
 * Find more details about his paper here:
 *
 * http://code.google.com/p/imumargalgorithm30042010sohm/
 */

#ifndef IMU_FILTER_H
#define IMU_FILTER_H

/**
 * Includes
 */
#include "math.h"

/**
 * Defines
 */
#define PI 3.1415926536

/**
 * IMU orientation filter.
 */
class IMUfilter {

public:

    /**
     * Constructor.
     *
     * Initializes filter variables.
     *
     * @param rate The rate at which the filter should be updated.
     * @param gyroscopeMeasurementError The error of the gyroscope in degrees
     *  per second. This used to calculate a tuning constant for the filter.
     *  Try changing this value if there are jittery readings, or they change
     *  too much or too fast when rotating the IMU.
     */
    IMUfilter(double rate, double gyroscopeMeasurementError);

    /**
     * Update the filter variables.
     *
     * @param w_x X-axis gyroscope reading in rad/s.
     * @param w_y Y-axis gyroscope reading in rad/s.
     * @param w_z Z-axis gyroscope reading in rad/s.
     * @param a_x X-axis accelerometer reading in m/s/s.
     * @param a_y Y-axis accelerometer reading in m/s/s.
     * @param a_z Z-axis accelerometer reading in m/s/s.
     */
    void updateFilter(double w_x, double w_y, double w_z,
                      double a_x, double a_y, double a_z);

    /**
     * Compute the Euler angles based on the current filter data.
     */
    void computeEuler(void);

    /**
     * Get the current roll.
     *
     * @return The current roll angle in radians.
     */
    double getRoll(void);

    /**
     * Get the current pitch.
     *
     * @return The current pitch angle in radians.
     */
    double getPitch(void);

    /**
     * Get the current yaw.
     *
     * @return The current yaw angle in radians.
     */
    double getYaw(void);

private:

    int firstUpdate;

    //Quaternion orientation of earth frame relative to auxiliary frame.
    double AEq_1;
    double AEq_2;
    double AEq_3;
    double AEq_4;

    //Estimated orientation quaternion elements with initial conditions.
    double SEq_1;
    double SEq_2;
    double SEq_3;
    double SEq_4;

    //Sampling period
    double deltat;

    //Gyroscope measurement error (in degrees per second).
    double gyroMeasError;

    //Compute beta (filter tuning constant..
    double beta;

    double phi;
    double theta;
    double psi;

};

#endif /* IMU_FILTER_H */

adxl345.cpp:

#include "adxl345.h"
#include "i2c.h"
#include <Wire.h> 

ADXL::ADXL(char device) {
  _device = device;
}
void ADXL::init() {
  //the data format and range register is fine by default

    i2c.writeRegister(_device, ADXL_POWER_CTL, 0); //standby mode
  //we want the accelerometer to go to sleep when not in use
  i2c.writeRegister(_device, ADXL_DATA_FORMAT, 0b1011); //4 mg/LSB full scale
  i2c.writeRegister(_device, ADXL_THRESH_INACT, 16); //62.5 mg/LSB
  i2c.writeRegister(_device, ADXL_THRESH_ACT, 8); //62.5 mg/LSB
  i2c.writeRegister(_device, ADXL_TIME_INACT, 5); // 1s/LSB
  i2c.writeRegister(_device, ADXL_BW_RATE, 0b1010); // 100Hz

  //i2c.writeRegister(_device, ADXL_ACT_INACT_CTL, 0b11111111); // enable ac operation on all axes

  //i2c.writeRegister(_device, ADXL_INT_ENABLE, 0b10011000); // enable interrupts for activity/inactivity

  //now turn it on and start reading
  //i2c.writeRegister(_device, ADXL_POWER_CTL, (1<<5)|(1<<4)|(1<<3)); //link on, auto sleep on, measure on
  i2c.writeRegister(_device, ADXL_POWER_CTL, (1<<5)|(1<<3)); //link on, auto sleep OFF, measure on
  //calibrate();
}
int ADXL::readInt(char address) {
  Wire.beginTransmission(_device);
  Wire.send(address);
  Wire.endTransmission();
  Wire.requestFrom(_device,2);
  byte lsb=Wire.receive();
  byte msb=Wire.receive();
  return ((int)msb)<<8 | lsb;
}
int ADXL::getX() {
  return readInt(ADXL_DATAX0);
}
int ADXL::getY() {
  return readInt(ADXL_DATAY0);
}
int ADXL::getZ() {
  return readInt(ADXL_DATAZ0);
}

void ADXL::calibrate() {
  _offsetX=0, _offsetY=0, _offsetZ=0;
  long ox=0,oy=0,oz=0;

  char power=i2c.readRegister(_device, ADXL_POWER_CTL); //get the power setting
  i2c.writeRegister(_device, ADXL_POWER_CTL, 1<<3 ); //return to previous power mode

  //get some readings to generate offset values
  for (int i=0; i<ADXL_CALIBRATION_SAMPLES; i++) {
    while (i2c.readRegister(_device,ADXL_INT_SOURCE) & 0b10000000 == 0) {
    }
    ox+=readInt(ADXL_DATAX0);
    oy+=readInt(ADXL_DATAY0);
    oz+=readInt(ADXL_DATAZ0);
  }
  _offsetX=ox/ADXL_CALIBRATION_SAMPLES;
  _offsetY=oy/ADXL_CALIBRATION_SAMPLES;
  _offsetZ=oz/ADXL_CALIBRATION_SAMPLES;

  /*Serial.println("Offset");
  Serial.print(_offsetX);
  Serial.print('\t');
  Serial.print(_offsetY);
  Serial.print('\t');
  Serial.println(_offsetZ);*/

  i2c.writeRegister(_device, ADXL_POWER_CTL, power); //return to previous power mode
}

bool ADXL::dataReady() {
  char interrupt=i2c.readRegister(_device,ADXL_INT_SOURCE);
  return (interrupt & 0b10000000);
}
bool ADXL::sleeping() {
  char val=i2c.readRegister(_device,ADXL_ACT_TAP_STATUS);
  return (val & 0b1000);
}

adxl345.h:

#ifndef adxl345_h
#define adxl345_h

#define ADXL_WHO_AM_I 0
#define ADXL_THRESH_ACT 0x24
#define ADXL_THRESH_INACT 0x25
#define ADXL_TIME_INACT 0x26
#define ADXL_ACT_INACT_CTL 0x27
#define ADXL_ACT_TAP_STATUS 0x2B
#define ADXL_BW_RATE 0x2C
#define ADXL_POWER_CTL 0x2D
#define ADXL_INT_ENABLE 0x2E
#define ADXL_INT_MAP 0x2F
#define ADXL_INT_SOURCE 0x30
#define ADXL_DATA_FORMAT 0x31
#define ADXL_DATAX0 0x32
#define ADXL_DATAX1 0x33
#define ADXL_DATAY0 0x34
#define ADXL_DATAY1 0x35
#define ADXL_DATAZ0 0x36
#define ADXL_DATAZ1 0x37

#define ADXL_CALIBRATION_SAMPLES 512 //number of samples to average to work out the offset

class ADXL {
public:
  ADXL(char device);
  void init();
  int getX();
  int getY();
  int getZ();
  bool dataReady();
  bool sleeping();
private:
  char _device;
  int _offsetX, _offsetY, _offsetZ;
  int readInt(char address);
  void calibrate();
};

#endif

i2c.cpp:

#include "i2c.h"
#include <Wire.h> 

I2C::I2C() {
}

void I2C::begin() {
  Wire.begin();
  /*PORTC |= ((1<<PORTC4) | (1<<PORTC5)); //set the i2c pins to internal pull up resistors
  TWBR = I2C_BAUD;                                  // We want 400kHz.
  //TWSR = TWI_TWPS;                                // Not used. Driver presumes prescaler to be 00.
  TWDR = 0xFF;                                      // Default content = SDA released.
  TWCR = (1<<TWEN)|                                 // Enable TWI-interface and release TWI pins.
  (0<<TWIE)|(0<<TWINT)|                      // Disable Interupt.
  (0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|           // No Signal requests.
  (0<<TWWC);                                 //
  */
}    

void I2C::writeRegister(int device, unsigned char address, unsigned char value) {
  Wire.beginTransmission(device);
  Wire.send(address);
  Wire.send(value);
  Wire.endTransmission();
}
byte I2C::readRegister(int device, unsigned char address) {
  Wire.beginTransmission(device);
  Wire.send(address);
  Wire.endTransmission();
  Wire.requestFrom(device,1);
  return Wire.receive();
}


I2C i2c=I2C();

ic2.h:

//i2c.h
//defines the constants for i2c operation
#ifndef i2c_h
#define i2c_h

#include "WProgram.h"

#define CLOCK_RATE 8000000
#define I2C_BAUD ((CLOCK_RATE/400000)-16)/2 //400kHz fast mode I2C

#define START 0x08
#define RESTART 0x10
#define MR_SLA_ACK 0x40
#define MR_SLA_NACK 0x48
#define MR_DATA_ACK 0x50
#define MR_DATA_NACK 0x58
#define MT_SLA_ACK 0x18
#define MT_SLA_NACK 0x20
#define MT_DATA_ACK 0x28
#define MT_DATA_NACK 0x30

class I2C {
  public:
  I2C();
  void begin();
  void writeRegister(int device, unsigned char address, unsigned char value);
  byte readRegister(int device, unsigned char address);
};

extern I2C i2c;

#endif

itg3200.cpp:

#include "itg3200.h"
#include "i2c.h"
#include <Wire.h> 

//#define ITG_PWR_MODE 0b00001011 //use z axis oscillator as clock source, z in standby
//#define ITG_PWR_MODE 0b00000011 //use z axis oscillator as clock source
#define ITG_PWR_MODE 0b00000000 //use internal oscillator as clock source
#define ITG_CALIBRATION_SAMPLES 512 //number of samples to average to work out the offset

ITG::ITG(char device) {
  _device=device;
}
void ITG::init() {
  i2c.writeRegister(_device, ITG_PWR_MGM, 1<<7); //reset the device
  i2c.writeRegister(_device, ITG_SMPLRT_DIV, 9); //sample rate divider to give 100Hz
  i2c.writeRegister(_device, ITG_DLPF_FS, 0b00011011); //42Hz low pass, 1kHz internal sample rate
  i2c.writeRegister(_device, ITG_INT_CFG, 0b100101); //interrupt for data available and new clock
  i2c.writeRegister(_device, ITG_PWR_MGM, ITG_PWR_MODE); 
  delay(70);
  /*while ((i2c.readRegister(_device, ITG_INT_STATUS) & 0b100) ==0 )
  {
    Serial.println("Waiting for clock sync.");
  }*/
  calibrate();
  sleep(); // start the device in sleep mode
}
int ITG::readInt(char address) {
  Wire.beginTransmission(_device);
  Wire.send(address);
  Wire.endTransmission();
  Wire.requestFrom(_device,2);
  byte msb=Wire.receive();
  byte lsb=Wire.receive();
  return ((int)msb)<<8 | lsb;
}
float ITG::getX() {
  return ((float)readInt(ITG_GYRO_XOUT_H)-_offsetX)/14.375;
}
float ITG::getY() {
  return ((float)readInt(ITG_GYRO_YOUT_H)-_offsetY)/14.375;
}
float ITG::getZ() {
  return ((float)readInt(ITG_GYRO_ZOUT_H)-_offsetZ)/14.375;
}
float ITG::getTemp() {
  //return (float)readInt(ITG_GYRO_TEMP_OUT_H);
  return ((float)readInt(ITG_GYRO_TEMP_OUT_H)+13200)/280.0+35.0;
}
void ITG::sleep() {
  char power = i2c.readRegister(_device, ITG_PWR_MGM);
  power = power | 0b01000000; //turn on the sleep bit
  i2c.writeRegister(_device, ITG_PWR_MGM, power);
}
void ITG::wake() {
  char power = i2c.readRegister(_device, ITG_PWR_MGM);
  if (power & 0b01000000 ==0)
    return;
  power = power & ~0b01000000; //turn off the sleep bit
  i2c.writeRegister(_device, ITG_PWR_MGM, power);
  delay(70);
}
void ITG::calibrate() {
  //delay(2000);
  _offsetX=0, _offsetY=0, _offsetZ=0;
  long ox=0,oy=0,oz=0;

  char power = i2c.readRegister(_device, ITG_PWR_MGM);
  char sample = i2c.readRegister(_device, ITG_SMPLRT_DIV);
  //wake(); //in case we were asleep
  //i2c::writeRegister(_device, ITG_SMPLRT_DIV, 0); //max sample rate
  
    //get some readings to generate offset values
  for (int i=0; i<ITG_CALIBRATION_SAMPLES; i++) {
    while (!dataReady()) {
    }
    ox+=readInt(ITG_GYRO_XOUT_H);
    oy+=readInt(ITG_GYRO_YOUT_H);
    oz+=readInt(ITG_GYRO_ZOUT_H);
  }
  _offsetX=ox/ITG_CALIBRATION_SAMPLES;
  _offsetY=oy/ITG_CALIBRATION_SAMPLES;
  _offsetZ=oz/ITG_CALIBRATION_SAMPLES;
  
  /*Serial.println("Offset");
  Serial.print(_offsetX);
  Serial.print('\t');
  Serial.print(_offsetY);
  Serial.print('\t');
  Serial.println(_offsetZ);*/

  //i2c::writeRegister(_device, ITG_SMPLRT_DIV, sample); //bring the sample rate back to what it was
  i2c.writeRegister(_device, ITG_PWR_MGM, power); //return to previous power mode
  //delay(2000);
}
bool ITG::dataReady() {
  Wire.beginTransmission(_device);
  Wire.send(ITG_INT_STATUS);
  Wire.endTransmission();
  Wire.requestFrom(_device,1);
  char interrupt=Wire.receive();
  //Serial.println(interrupt, BIN);
  return (interrupt & 0b00000001);
}

itg3200.h:

#ifndef itg3200_h
#define itg3200_h

#define ITG_WHO_AM_I 0
#define ITG_SMPLRT_DIV 0x15
#define ITG_DLPF_FS 0x16
#define ITG_INT_CFG 0x17
#define ITG_INT_STATUS 0x1A
#define ITG_GYRO_TEMP_OUT_H 0x1B
#define ITG_GYRO_TEMP_OUT_L 0X1C
#define ITG_GYRO_XOUT_H 0x1D
#define ITG_GYRO_XOUT_L 0x1E
#define ITG_GYRO_YOUT_H 0x1F
#define ITG_GYRO_YOUT_L 0x20
#define ITG_GYRO_ZOUT_H 0x21
#define ITG_GYRO_ZOUT_L 0x22
#define ITG_PWR_MGM 0x3E


class ITG {
public:
  ITG(char address);
  void init();
  float getX();
  float getY();
  float getZ();
  float getTemp();
  void sleep();
  void wake();
  bool dataReady();
private:
  char _device;
  int _offsetX, _offsetY, _offsetZ;
  int readInt(char address);
  void calibrate();
};

#endif

That's the lot! I hope someone can spot the problem!

You might wanna check my implementation of a 9DOF/DOM sensor fusion orientation filter .. also you might be interested in my FreeIMU: a libre 9DOM/DOF inertial measurement unit.