Multiple I2C Sensors issue

Working on a project that uses 1, 2 or 3 light sensors (OPT3001) on the I2C bus,
I need my code to work correctly with multiple sensors and also with a single when others fail/disconnect/etc.

I found a nice code example for a single sensor, but I failed to make it work correctly as a function call for multiple sensors, when the amount of the sensors (I2C devices on the bus) may vary.

I would really appreciate if an expert can help me expand the code example and modify for multiple I2C sensors.

I tried a few attempts but even the best of them had a problem that when there is a call for I2C address that no longer holds a sensor, then all of the I2C bus freezes, and I failed to fix it myself.

Code example for SINGLE OPT3001 that works:

#include <Wire.h>
//#define OPT3001_A_ADDR 0x44       // Light sensor 1 HEX address
#define OPT3001_B_ADDR 0x45       // Light sensor 2 HEX address 

void setup() {
  
  Serial.begin(19200);                            // Initialize serial communication at 9600
  Wire.begin();                                  // Initialize Arduino in I2C master.
  Wire.beginTransmission(OPT3001_B_ADDR);                  // I2C address of OPT3001 = 0x44
  Wire.write(0x01);                              // Config register address 0x01
  Wire.write(0xCE);
  Wire.write(0x10);                              // Write 0xCE10 to turn on sensor
  Wire.endTransmission();
  Serial.println("Data received \t\t Lux");
}

void loop() {
  Wire.beginTransmission(OPT3001_B_ADDR);
  Wire.write(0x00);                              // Send result register address
  Wire.endTransmission();
  delay(100);
  
  Wire.requestFrom(OPT3001_B_ADDR, 2);                     // Request 2 bytes data from OPT3001
  uint16_t iData;
  uint8_t  iBuff[2];
  while (Wire.available()) { 

    iBuff[0]=Wire.read();
    iBuff[1]=Wire.read();

    
    iData = (iBuff[0] << 8) | iBuff[1];
    float fLux = SensorOpt3001_convert(iData);   // Calculate LUX from sensor data
 
 Serial.print(iData,BIN);                     // Print the received data
    Serial.print("\t\t");
    Serial.println(fLux);                        // Print it on serial terminal
  }
  delay(1000);
}

float SensorOpt3001_convert(uint16_t iRawData){
 
  uint16_t iExponent, iMantissa;
  iMantissa = iRawData & 0x0FFF;                 // Extract Mantissa
  iExponent = (iRawData & 0xF000) >> 12;         // Extract Exponent 
  return iMantissa * (0.01 * pow(2, iExponent)); // Calculate final LUX
}

OPT3001 datasheet:
https://www.ti.com/product/OPT3001

Stay safe friends.

I would like to know what is the correct way of collecting data from multiple I2C sensors, even in cases that some sensors can be removed or fail.
In my case those are light sensors.
When I am collecting data from several I2C sensors and all of them are connected, everything is alright, but if I am reaching address of a sensor that was removed, then all the other sensors are not able to communicate through the I2C anymore, seems that the whole bus is stuck.
I suspect that I failed to understand how to release the I2C bus correctly.
So my question is how to build the code correctly for it to be robust for cases that some I2C devices can be missing.

I would really appreciate a simple example of how to handle this. :confused:

My code that freezers the I2C bus when reaching for address of removed sensor:

#include <Wire.h>

#define OPT3001_A_ADDR 0x44     
#define OPT3001_B_ADDR 0x45      


void setup() {
  
 Serial.begin(19200); 
  
 Wire.begin();
 
 OPT3001_init_transmission(OPT3001_A_ADDR);     // init light sensor A
 OPT3001_init_transmission(OPT3001_B_ADDR);     // init light sensor B
}

void loop() {
 
 float lux_A = read_lux_OPT3001(OPT3001_A_ADDR);
 float lux_B = read_lux_OPT3001(OPT3001_B_ADDR);

  
 Serial.print("OPT3001_A = ");
 Serial.println(lux_A);
 Serial.print("OPT3001_B = ");
 Serial.println(lux_B);
  
 delay(800);
}


void OPT3001_init_transmission(int address){
 Wire.beginTransmission(address);    
 Wire.write(0x01);                    // Config register address 0x01
 Wire.write(0xCE);
 Wire.write(0x10);                    // Write 0xCE10 to turn on sensor
 Wire.endTransmission();
}


void OPT3001_transmission_to_0(int address){
 Wire.beginTransmission(address);
 Wire.write(0x00);                              // Send result register address
 Wire.endTransmission();
 delay(100);
}


float read_lux_OPT3001(int I2C_address){
 
 OPT3001_transmission_to_0(I2C_address);
 
 Wire.requestFrom(I2C_address, 2);               // Request 2 bytes data from OPT3001
 uint16_t iData;
 uint8_t  iBuff[2];
 while (Wire.available()) { 
    
 iBuff[0] = Wire.read();
 iBuff[1] = Wire.read();

 iData = (iBuff[0] << 8) | iBuff[1];
 //Serial.print(iData,BIN);                   // Print the received data
 float fLux = SensorOpt3001_convert(iData);   // Calculate LUX from sensor data

 return fLux;
 }
 return 0; // false reading
}


float SensorOpt3001_convert(uint16_t iRawData) {
 uint16_t iExponent, iMantissa;
 iMantissa = iRawData & 0x0FFF;                 // Extract Mantissa
 iExponent = (iRawData & 0xF000) >> 12;         // Extract Exponent 
 return iMantissa * (0.01 * pow(2, iExponent)); // Calculate final LUX
}

vicdidwhat:
...I need my code to work correctly with multiple sensors and also with a single when others fail/disconnect/etc.

try including something like this in your code:

 // ------------------------------------------
 // Test if the sensor can be seen at its address.
 // ------------------------------------------
 Wire.beginTransmission(Addr);
 int error = Wire.endTransmission();
 if( error == 0)
 {
   Serial.println( "The sensor is detected.");
   //now do read/write to sensor
 }
 else
 {
   Serial.println( "Error, no sensor found.");
 }
}

hope that helps...

sherzaad:
try including something like this in your code:

...

hope that helps...

I've tried, the problem I encounter is that after an attempt for reaching address that has no sensor on it, the whole I2C bus gets unavailable, forever. :cry:
Feels like I must somehow to release the I2C bus before I can use it again, and resetting it is not that simple from what I've seen. So I'm searching for more elegant solution if such exist.

Can you give an example how I can correctly: " // Test if the sensor can be seen at its address." ?

I'm so desperate at this point that I'm ready to pay for the solutions of this, it's the last piece of a bigger project that remains problematic.

the snippet I suggested is essentially a form of the i2c scanner code

did you try it? do you get the same issue with that code?

Threads merged.

Hi, I don't know if you managed to solve your problem, but just in case.
I got the same issue with the example you're using. Instead, I swapped to ClosedCube library link and it worked fine.