Multiple I2C devices on single I2C channel?

The original was right. Your modifications broke it,but don’t worry. Try this:


using System;
using System.Threading;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

namespace Sensor
{
    public class TMP102
    {
        I2CDevice.Configuration _TMP102 = null;

        I2CDevice _i2c = null;

        public enum ADD0
        {
            Gnd,
            Vcc,
            SDA,
            SCL
        }

        public enum ConversionRate
        {
            quarter_Hz,
            one_Hz,
            four_Hz,
            eight_Hz
        }

        public enum ThermostatMode
        {
            ComparatorMode,
            InterruptMode
        }

        public enum AlertPolarity
        {
            activeLow,
            activeHigh
        }

        public enum ConsecutiveFaults
        {
            one,
            two,
            four,
            six
        }

        private enum Registers
        {
            Temperature = 0x00,
            Configuration = 0x01,
            T_low = 0x02,
            T_high = 0x03
        }

        private ushort _sensorAddress;

        private byte[] _registerNum = new byte[1] { (byte)Registers.Configuration };
        private byte[] _registerValue = new byte[2];

        private float _temperature = 0.0f;

        private bool _oneShotMode;
        private AlertPolarity _alertPolarity;
        private ThermostatMode _thermostatMode;
        private ConsecutiveFaults _consecutiveFaults;

        public bool Init(ADD0 addressSelect)
        {
            return Init(addressSelect, false, AlertPolarity.activeHigh, ConversionRate.four_Hz, ThermostatMode.ComparatorMode, ConsecutiveFaults.one, 0, 0);
        }

        public bool Init(
            ADD0 addressSelect = ADD0.Gnd,
            bool oneShotMode = false,
            AlertPolarity alertPolarity = AlertPolarity.activeHigh,
            ConversionRate conversionRate = ConversionRate.four_Hz,
            ThermostatMode thermostatMode = ThermostatMode.ComparatorMode)
        {
            return Init(addressSelect, oneShotMode, alertPolarity, conversionRate, thermostatMode, ConsecutiveFaults.one, 0, 0);
        }

        private bool Init(
            ADD0 addressSelect,
            bool oneShotMode,
            AlertPolarity alertPolarity,
            ConversionRate conversionRate,
            ThermostatMode thermostatMode,
            ConsecutiveFaults consecutiveFaults,
            ushort limitHigh,
            ushort limitLow)
        {
            // Sleep past first conversion
            Thread.Sleep(30);

            switch (addressSelect)
            {
                case ADD0.Gnd: _sensorAddress = 0x90 >> 1; break;
                case ADD0.Vcc: _sensorAddress = 0x92 >> 1; break;
                case ADD0.SDA: _sensorAddress = 0x94 >> 1; break;
                case ADD0.SCL: _sensorAddress = 0x96 >> 1; break;
            }

            // this is the I2C bus definitiion? (combined with object?)
            //_TMP102=new I2CDevice(new I2CDevice .Configuration(_sensorAddress, 100));
            // Splitting this up..

            // object
            _TMP102 = new I2CDevice.Configuration(_sensorAddress, 100);

            // bus
            _i2c = new I2CDevice(_TMP102);

            _alertPolarity = alertPolarity;
            _oneShotMode = oneShotMode;
            _thermostatMode = thermostatMode;
            _consecutiveFaults = consecutiveFaults;

            _registerNum[0] = (byte)Registers.Configuration;
            int bytesTransfered = ReadRegister();

            if (bytesTransfered == 3)
            {
                if (_oneShotMode)
                    _registerValue[0] = (byte)(_registerValue[0] | 0x01);
                else
                    _registerValue[0] = (byte)(_registerValue[0] & 0xfe);

                if (_thermostatMode == ThermostatMode.InterruptMode)
                    _registerValue[0] = (byte)(_registerValue[0] | 0x02);
                else
                    _registerValue[0] = (byte)(_registerValue[0] & 0xfd);

                if (_alertPolarity == AlertPolarity.activeLow)
                    _registerValue[0] = (byte)(_registerValue[0] | 0x04);
                else
                    _registerValue[0] = (byte)(_registerValue[0] & ~0x04);

                switch (conversionRate)
                {
                    case ConversionRate.quarter_Hz: _registerValue[1] = (byte)((_registerValue[1] & 0x3f) | (0x00 << 6)); break;
                    case ConversionRate.one_Hz: _registerValue[1] = (byte)((_registerValue[1] & 0x3f) | (0x01 << 6)); break;
                    case ConversionRate.four_Hz: _registerValue[1] = (byte)((_registerValue[1] & 0x3f) | (0x02 << 6)); break;
                    case ConversionRate.eight_Hz: _registerValue[1] = (byte)((_registerValue[1] & 0x3f) | (0x03 << 6)); break;
                }

                bytesTransfered = WriteRegister();
                Thread.Sleep(30);
            }

            return (bytesTransfered == 3);
        }

        public float Read()
        {
            int bytesTransfered;

            if (_oneShotMode)
            {
                _registerNum[0] = (byte)Registers.Configuration;
                ReadRegister();

                if ((_registerValue[0] & 0x01) == 0x01)
                {
                    // Toggle OS bit
                    _registerValue[0] |= 0x80;
                    WriteRegister();

                    // Sleep so conversion can start
                    Thread.Sleep(1);

                    // Wait for OS bit to toggle back to 1
                    do
                    {
                        ReadRegister();
                    }
                    while ((_registerValue[0] & 0x80) == 0x00);
                }
            }

            _registerNum[0] = (byte)Registers.Temperature;
            bytesTransfered = ReadRegister();

            if (bytesTransfered == 3)
            {
                int temp = ((_registerValue[0] << 4) | (_registerValue[1] >> 4));

                if ((temp & 0x0800) == 0x0800)
                {
                    _temperature -= 1;
                    _temperature = ~temp;
                    _temperature = temp & 0x0FFF;

                    _temperature = (float)temp * -0.0625f;
                }
                else
                {
                    _temperature = (float)temp * 0.0625f;
                }
            }

            return _temperature;
        }

        public float asCelcius()
        {
            return _temperature;
        }

        public float asFahrenheit()
        {
            return (asCelcius() * 9.0f / 5.0f) + 32.0f;
        }

        public float asKelvin()
        {
            return (asCelcius() + 273.15f);
        }

        public float asRankine()
        {
            return (asKelvin() * 9.0f / 5.0f);
        }

        private int ReadRegister()
        {
            I2CDevice.I2CTransaction[] xActions = new I2CDevice.I2CTransaction[2];

            xActions[0] = I2CDevice.CreateWriteTransaction(_registerNum);
            xActions[1] = I2CDevice.CreateReadTransaction(_registerValue);

            return _i2c.Execute(xActions, 30); // << This line gives the error type 'System.NullReferenceException' 

        }

        private int WriteRegister()
        {
            I2CDevice.I2CTransaction[] xActions = new I2CDevice.I2CTransaction[1];

            xActions[0] = I2CDevice.CreateWriteTransaction(new byte[] { _registerNum[0], _registerValue[0], _registerValue[1] });

            return _i2c.Execute(xActions, 30);
        }
    }
}

Many thanks again Architect!
This does indeed now work! I have worked my way through it and can see (I think) my mistakes!
I now need to see if I can ‘graft’ on the BMP085 code by splitting it in a similar way. But I think I will be back for some more expert guidance.

Many Thanks again Simon M.

If you lived close I’d be giving you a Beer! ;D

Just to clear things up in my head before I start on this, and now I’m a little more awake,

I2CDevice.Configuration _TMP102 = null;

refers to the ‘thing’ _TMP102 which is plugged into the I2C bus called _i2c as defined by

I2CDevice _i2c = null;

.

If I have got this correct then I can’t see how the xActions picks up which ‘thing’ it is dealing with?

Any clues…

From the Wiki:

Accessing Multiple Devices

The I2C object on NETMF is a representation of the “bus” and not the “device”, and so you can’t construct multiple I2C object. To access multiple I2C device you need to have multiple configurations and then when accessing device “A” we need to use configuration “A” and when accessing device “B” we need to use configuration “B”.

and

       //create I2C object
        I2CDevice.Configuration conDeviceA =
           new I2CDevice.Configuration(0x38, 400);
        I2CDevice.Configuration conDeviceB =
           new I2CDevice.Configuration(0x48, 400);
 
        I2CDevice MyI2C = new I2CDevice(conDeviceA);

and

        // We are now accessing device A
        MyI2C.Config = conDeviceA;
        if (MyI2C.Execute(xActions, 1000) == 0)
        {
            Debug.Print("Failed to perform I2C transaction");
        }
        else
        {
            Debug.Print("Register value: " + RegisterValue[0].ToString());
        }
        // We are now accessing device B
        MyI2C.Config = conDeviceB;
......

Thats a very simple example, and you have the added complexity of managing each object in a class. I expect that either means you still need to have an in-scope I2C bus object and manage that outside the class as the above does, or have the methods you call ensure it has the right configuration in place. I think that means you need to show more code in your project.

So you define the objects / things that connect to the I2C bus first, then define the actual bus?
Yours Simon M.

May be I’m missing the obvious here, ???
In the example given from the wiki,
I can see two objects have been defined, conDeviceA at address 0x38, and conDeviceB at address 0x48, both running at 400kHz.
But then it goes to pieces for me, What is

I2CDevice MyI2C = new I2CDevice(conDeviceA)

doing? It looks like it is setting up an I2C bus for conDeviceA, If so how is conDeviceB referenced? or could I just as well used the line

I2CDevice MyI2C = new I2CDevice(conDeviceB)

?
This is probably so easy to some that you can’t see what my problem is.

All I want to do is capture two I2C devices, TMP102 & BMP085. Both I did with out an issue using Ardunio.

Yours Simon M.

You can’t access 2 I2C devices at the same time.

So what you do is query them one after the other:


// pseudo code

//create I2C configurations

// Config for the TMP102
I2CDevice.Configuration configTMP102 = new I2CDevice.Configuration(0x38, 400);

// Config for the BMP085
I2CDevice.Configuration configBMP085 = new I2CDevice.Configuration(0x48, 400);

//create the I2C Device (actualy, here is the confusion, we create the bus with the device config)
I2CDevice MyI2C = new I2CDevice(configTMP102) // you can't create I2CDevice without config)

//now say you want to start reading the BMP085:
MyI2C.Config = configBMP085
//read the BMP085 here

//and now we want to read the TMP102:
MyI2C.Config = configTMP102
//read device here


Does this make it bit clearer ?

I think it does Eric,
So putting it simply define your physical things, in this case a TMP102 & BMP085.
Then create the Device (bus) based upon the TMP102 thing.
The system is then told ether it is is using a TMP102 or BMP085 and then takes care of the rest.

Would

I2CDevice MyI2C = new I2CDevice(configBMP085)

be just as valid a thing to use to configure the I2C with?

Yours Simon M.

Very simple I2CDevice is an object that represent the “bus” and not your “device”.

You could have 10 I2C “devices” connected but you will only have “one bus”. Each device will have its own “configuration” so you are allowed to have multiple configurations but all these are using ONE BUS.

If the wiki page is not clear enough then feel free to adjust it so it makes more sense.

Now you have jut gone and confused me again! ???
I think that in this case the bus has been defined by

I2CDevice MyI2C = new I2CDevice(configTMP102)

But, I was asking if

I2CDevice MyI2C = new I2CDevice(configBMP085)

could be a valid replacement?

Yours Simon M.

To answer your question,
you call


I2CDevice MyI2C = new I2CDevice(configdevice1);

only once !! Make it easy on yourself, and call that with the config of the device you want to read first.

once you get the desired values, move on to the next I2C device by doing:


MyI2C.Config = configdevice2;

again, when you have the values, you can move on to the next I2C device by:


MyI2C.Config = configdevice3;

and so on, and so on…

Yes Eric has helped (again, well done).

The key is the NEW statement and using it once.

You can create the bus based on any single device (and it doesn’t matter which one you pick), but you only need to do that ONCE. NEW signifies that you want a brand new instance of the I2C bus, so you only ever call it once in that way. Then you just set the Config to the appropriate device’s config and act on that device, change the config to the next device, then move on…

I’ve just updated the example on the Wiki with some additional commentary in the code, and corrected a typo. Let me know if that helps?

Eric helped me a lot in the chat room where he paitently went through my problem and under stood that I was confused as to why

I2CDevice MyI2C = new I2CDevice(configBMP085);

[italic]could[/italic] be used. The answer is it depends upon which physical device you access first.

So in my case where I have a BMP085, and a TMP102, and I want to access the TMP102 first I would use

I2CDevice MyI2C = new I2CDevice(configTMP102);

to define the I2C bus.

But if I accessed the BMP085 first then the I2C bus definition would look like

I2CDevice MyI2C = new I2CDevice(configBMP085)

and this should only be done after all the physical devices have been defined, and once only!

Again my thanks to Eric and all those of you who have taken the trouble to reply…