Panda II + BMP085 + HMC5883 + 6 Degrees of Freedom ITG3200/ADXL345 = slow I2C

Hi
I have an annoying problem with the combination of the following breakout boards, from Sparkfun, on my Panda II:
6 Degrees of Freedom ITG3200/ADXL345
BMP085 Barometric Pressure Sensor
Triple Axis Magnetometer Breakout - HMC5883L

I have 2 3k3 pullup resistors between SDA/SCL and +3v3

Connecting ITG3200/ADXL345 + HMC5883L gives me no problems or just connecting the BMP085 to the I2C bus speed is fine.
If i connect all sensors at once, communication speed drops to a crawl.

Drivers are from code.tinyclr.com.
i’ve modified the BMP085 driver to use AbstractI2CDevice from the ITG3200 + ADXL345 + HMC5883L driver

I’m a bit lost here, because the parts work by themself, but all three breakout boards slow I2C speed down to a crawl.

Can you define “a crawl”? It will be 3 times slower for starters because there are 3 sensors to read instead of 1. Can you measure the speed? Can you measure the speed of each sensor? Maybe one of them gets stuck.

Times are seconds:milliseconds

With BMP085 attached and powered up
BMP085
6:365: Altitude: 1583391.31
6:371: Pressure: 9936.00
6:376: Temperature: 2.54
Gyro
6:381: GyroXAxis: 0.81
11:693: GyroYAxis: -0.20
16:751: GyroZAxis: -0.53
Accelerometer
21:810: XAxis: 0.00
21:815: YAxis: 0.00
21:820: ZAxis: 0.00
Compass
21:826: HeadingDegrees: 0.00
32:11: HeadingTiltCompensated: -0.87

Without BMP085
Gyro
6:157: GyroXAxis: -0.01
6:164: GyroYAxis: 0.01
6:170: GyroZAxis: -0.01
Accelerometer
6:178: XAxis: 0.00
6:183: YAxis: 0.00
6:188: ZAxis: 0.00
Compass
6:194: HeadingDegrees: 3.21
6:207: HeadingTiltCompensated: -0.89

So with BMP085 it takes around 26s and without its 50ms so it’s significant
And i just noticed that the accelerometer and gyro is strangely stable, and that the calculations on the bmp085 values are way off…

Try to narrow it down ti a specific question/problem then post the code here please.

Well my 1st question must be:
Have anyone experienced this problem?
And the 2nd one is:
How did you solve it?

I think that the “strange” values i’m getting is my calculations, but i’ll have a look at that when i get the breakout boards to work, since i don’t know how much influence those problems have on the values i’m getting from the boards.

Forgot the source code. There are a bunch of files, so i’ve uploaded it to this url:
http://www.mediafire.com/file/0ve6fhb5p3dp037/IMUTest.zip

We never seen this before. Please post simplified code demonstrating the problem you are seeing.

As i said, im using code from:
ITG3200+ADXL345+HMC5883L MULTIPLE I2C ON SAME BUS:
http://code.tinyclr.com/project/441/itg3200adxl345hmc5883l-multiple-i2c-on-same-bus/

Modified the BMP085 code from Nicolas3 (also from tinyclr.com) like this

using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using NETMF.IO.I2C;
using TDK.NETMF.AUV_PII.Sensors;

namespace SensorsLibrary.IMU.Sensors
{
    public enum Oversampling : byte
    {
        None = 0,
        Level1,
        Level2,
        Level3,
    }

    public class BMP085 : AbstractI2CDevice
    {
        #region Fields
        private Int16 AC1, AC2, AC3, B1, B2, MB, MC, MD;
        private UInt16 AC4, AC5, AC6;

        private LowPassFilter filterTemperature = new LowPassFilter(0.1);
        private LowPassFilter filterPressure = new LowPassFilter(0.1);
        private LowPassFilter filterAltitude = new LowPassFilter(0.1);
        #endregion

        #region Properties
        /// <summary>
        /// 0 = low precision and power to 3 = higher both
        /// </summary>
        public byte OS { get; set; }
        /// <summary>
        /// Milliseconds between mesures
        /// </summary>
        public int Refresh { get; set; }
        /// <summary>
        /// True if sensor responding
        /// </summary>
        public bool Ok { get; private set; }
        /// <summary>
        /// In Celcius
        /// </summary>
        public double Temperature { get {return filterTemperature.Value;} }
        /// <summary>
        /// In Pa
        /// </summary>
        public int Pressure { get{return (int)filterPressure.Value;}}
        /// <summary>
        /// In mm
        /// </summary>
        public double Altitude { get{return filterAltitude.Value;}}
        #endregion


        //0 to 3 : 0 = low precision and low power and 20ms, to 3 = higher both and 40ms, refresh=seconds between capture, if 0 : only manual calls to ReadSensor()
        // Oversampling: Datasheet says:
        /// <summary>
        /// BMP085 breakout board. I2C address: 0x77
        /// </summary>
        /// <param name="clockRate">Clockrate on the I2C Bus</param>
        /// <param name="timeout">in ms</param>
        /// <param name="OSS">
        /// None:       Conversion time max: 4.5 ms  / RMS hPa: 0.06 / RMS m: 0.5
        /// Level1:     Conversion time max: 7.5 ms  / RMS hPa: 0.05 / RMS m: 0.4
        /// Level2:     Conversion time max: 13.5 ms / RMS hPa: 0.04 / RMS m: 0.3
        /// Level3:     Conversion time max: 25.5 ms / RMS hPa: 0.03 / RMS m: 0.25
        /// </param>
        /// <param name="refresh">if 0 : only manual calls to ReadSensor()</param>
        public BMP085(int clockRate, int timeout, Oversampling OSS = Oversampling.None, int refresh = 60)
            : base(0x77, clockRate, timeout)
        {
            int actualRefresh = refresh;
            if (refresh > 0)
            {
                switch (OSS)
                {
                    case Oversampling.None:
                        if (actualRefresh < 5)
                            actualRefresh = 5;
                        break;
                    case Oversampling.Level1:
                        if (actualRefresh < 8)
                            actualRefresh = 8;
                        break;
                    case Oversampling.Level2:
                        if (actualRefresh < 14)
                            actualRefresh = 14;
                        break;
                    case Oversampling.Level3:
                        if (actualRefresh < 26)
                            actualRefresh = 26;
                        break;
                }
            }
            Refresh = actualRefresh;
            Ok = false;

            // Calibration
            I2CDevice.I2CTransaction[] xActions = new I2CDevice.I2CTransaction[2];
            xActions[0] = I2CDevice.CreateWriteTransaction(new byte[] { 0xAA });
            byte[] RawData = new byte[22];
            xActions[1] = I2CDevice.CreateReadTransaction(RawData);
            if (Write(xActions, 100) != 23)
                return;
            AC1 = (short)((short)(RawData[0] << 8) + (short)(RawData[1]));
            AC2 = (short)((short)(RawData[2] << 8) + (short)(RawData[3]));
            AC3 = (short)((short)(RawData[4] << 8) + (short)(RawData[5]));
            AC4 = (ushort)((ushort)(RawData[6] << 8) + (ushort)(RawData[7]));
            AC5 = (ushort)((ushort)(RawData[8] << 8) + (ushort)(RawData[9]));
            AC6 = (ushort)((ushort)(RawData[10] << 8) + (ushort)(RawData[11]));
            B1 = (short)((short)(RawData[12] << 8) + (short)(RawData[13]));
            B2 = (short)((short)(RawData[14] << 8) + (short)(RawData[15]));
            MB = (short)((short)(RawData[16] << 8) + (short)(RawData[17]));
            MC = (short)((short)(RawData[18] << 8) + (short)(RawData[19]));
            MD = (short)((short)(RawData[20] << 8) + (short)(RawData[21]));
            if (AC1 * AC2 * AC3 * AC4 * AC5 * AC6 * B1 * B2 * MB * MC * MD == 0)
                return;
            Ok = true;
            OS = (byte)OSS;
            Refresh = refresh;
            if (refresh > 0)
                new Thread(Capture).Start();
        }

        private void Capture()
        {
            while (true)
            {
                ReadSensor();
                Thread.Sleep(Refresh);
            }
        }

        public bool ReadSensor()
        {
            Int32 X1, X2, B5, B6, X3, B3, P, UT, UP;
            UInt32 B4, B7;

            I2CDevice.I2CTransaction[] xActions = new I2CDevice.I2CTransaction[1];
            xActions[0] = I2CDevice.CreateWriteTransaction(new byte[] { 0xF4, 0x2E });
            if (Write(xActions, 100) != 2)
            {
                Ok = false;
                return false;
            }
            Thread.Sleep(5);
            I2CDevice.I2CTransaction[] xActions2 = new I2CDevice.I2CTransaction[2];
            xActions2[0] = I2CDevice.CreateWriteTransaction(new byte[] { 0xF6 });
            byte[] RawData = new byte[2];
            xActions2[1] = I2CDevice.CreateReadTransaction(RawData);
            if (Write(xActions2, 100) != 3)
            {
                Ok = false;
                return false;
            }
            UT = (Int32)((Int32)(RawData[0] << 8) + (Int32)(RawData[1]));
            // Pressure
            xActions[0] = I2CDevice.CreateWriteTransaction(new byte[] { 0xF4, (byte)((OS << 6) + 0x34) });
            if (Write(xActions, 100) != 2)
            {
                Ok = false;
                return false;
            }
            Thread.Sleep(OS > 0 ? OS * 10 : 5);
            xActions2[0] = I2CDevice.CreateWriteTransaction(new byte[] { 0xF6 });
            byte[] RawData2 = new byte[3];
            xActions2[1] = I2CDevice.CreateReadTransaction(RawData2);
            if (Write(xActions2, 100) != 4)
            {
                Ok = false;
                return false;
            }
            UP = (((Int32)(RawData2[0]) << 16) + ((Int32)(RawData2[1]) << 8) + (Int32)RawData2[2]) >> (8 - OS);
            // Calculus
            X1 = (UT - AC6) * AC5 >> 15;
            X2 = ((Int32)MC << 11) / (X1 + MD);
            B5 = X1 + X2;
            filterTemperature.Update(((double)((B5 + 8) >> 4)) / 10);
            B6 = B5 - 4000;
            X1 = (B2 * (B6 * B6 >> 12)) >> 11;
            X2 = AC2 * B6 >> 11;
            X3 = X1 + X2;
            B3 = ((((Int32)AC1 * 4 + X3) << OS) + 2) >> 2;
            X1 = AC3 * B6 >> 13;
            X2 = (B1 * (B6 * B6 >> 12)) >> 16;
            X3 = ((X1 + X2) + 2) >> 2;
            B4 = AC4 * (UInt32)(X3 + 32768) >> 15;
            B7 = ((UInt32)(UP - B3)) * (UInt32)(50000 >> OS);
            P = (B7 < 0x80000000) ? (Int32)((B7 * 2) / B4) : (Int32)((B7 / B4) * 2);
            X1 = (P >> 8) * (P >> 8);
            X1 = (X1 * 3038) >> 16;
            X2 = (-7357 * P) >> 16;
            filterPressure.Update(P + ((X1 + X2 + 3791) >> 4));
            filterAltitude.Update((44330.75 * (1 - System.Math.Pow((double)Pressure / 101325, 0.19029))) * 1000);
            Ok = true;
            return true;
        }

        public override bool Connected()
        {
            //TODO: Needs to be fixed..
            return DeviceIdentifier()[0] == 0;
        }

        public override byte[] DeviceIdentifier()
        {
            //TODO: Needs to be fixed..
            return new byte[1] { 0 };
            //throw new NotImplementedException();
        }
    }
}

And i’m constructing the objects like this:

        private const int I2C_CLOCK_RATE = (int)I2CBus.I2CBusSpeed.ClockRate400;
        private const int I2C_TIMEOUT = 500;

            gyro = new ITG3200(I2C_CLOCK_RATE, 0, false);
            //at 1kHz/(20+1) = 47.6 Hz sampling rate
            gyro.setSampleRateDivider(40);
            gyro.setFilter(ITG3200.Filter.LP20Hz_Int1kHz);
            // We have no power concerns, turn all axes on
            gyro.Sleep(false, false, false, false);
            //Set the clock source to PLL attached to the Z gyro
            //it's much more accurate than the default internal oscillator
            gyro.setClockSource(ITG3200.ClockSource.INT_OSC);//  .PLL_ZGRYO_REF);
            //calibrate after changing settings
            Thread.Sleep(100);
            gyro.calibrate();

            compass = new Compass(I2C_CLOCK_RATE, I2C_TIMEOUT);
            compass.SetScale(1.3);
            compass.SetContinuous();

            accel = new ADXL345(0x53, I2C_CLOCK_RATE, I2C_TIMEOUT);
            accel.Range = 2;
            accel.FullResolution = true;
            accel.EnableMeasurements();
            accel.SetDataRate(ADXL345.DataRates.DataRate50Hz);

            pressure = new BMP085(I2C_CLOCK_RATE,  I2C_TIMEOUT, Oversampling.Level2, 50);

and then i a loop in main, i read the different values off the sensors, and then wait for 50ms

(lots of code anyway…

Your read sensor method creates multiple objects whenever it is called. This is not ideal. Still, I can’t see how using multiple drivers causes slow downs!

I don’t think i’m creating multiple objects in my code. I do it like this:

main()
{
   Create the drivers
  
   while(true)
  {
      read the values
      sleep for 50ms
  }
}

Just tried the same code on a NetDuino. The problem is the same, so unless it’s a problem with the SDK, it’s either my code, or the breakout boards thats malfunctioning somehow when combined.
I should be able to borrow an arduino i can try the breakout boards on in a few days. I should be able to locate some code for it to use the 3 boards at the same time. If they also fail on that it’s the boards

Time for some isolation testing.

Create a simple codebase, that sets up and talks to one device only. Make that work, and make sure that the results look correct.
Then do the same for the next device
Then do the same for the last device
Then, once you know all of them work individually, combine the code.

Your read is creating objects internally.

Ok. I’m gonna start over on the drivers, and take them one at the time, and make sure that the memory usage stays stable, and the values is what i expect.

And i’m gonna see if i have the same issue with an arduino. If i have, it might have to order some new breakout boards, and try with them

So i recoded the drivers, and now everything works.
When i’ve figured out how to calculate the tilt compensated heading, i’ll post my drivers at code.tinyclr.com

I’m still not sure why the readymade code from code.tinyclr.com broke down when combined, but guess thats a mystery to be solved another day ::).

Thanks for the help :slight_smile:

Glad it worked and look forward to seeing your updated drivers.