Multiple I2C inputs Assistance

Hello All,

I'm working on a project that has 11 points of measurement for humidity and temperature. With that in mind, I bought a Mega 2560 to have the correct amount of pins. Along with the board, I got 11 si7021 Humidity and Temperature sensors ( SparkFun Humidity and Temperature Sensor Breakout - Si7021 - SEN-13763 - SparkFun Electronics ). These sensors use an i2c communication protocol.

At first, I could get no code working at all, and I believe that was due to the code being used for just the Uno. I eventually found one that could compile and give me accurate readings. The original is down below:

// Distributed with a free-will license.
// Use it any way you want, profit or free, provided it fits in the licenses of its associated works.
// SI7021
// This code is designed to work with the SI7021_I2CS I2C Mini Module available from ControlEverything.com.
// https://www.controleverything.com/content/Humidity?sku=SI7021_I2CS#tabs-0-product_tabset-2

#include 

// SI7021 I2C address is 0x40(64)
#define Addr 0x40

void setup()
{
  // Initialise I2C communication as MASTER
  Wire.begin();
  // Initialise serial communication, set baud rate = 9600
  Serial.begin(9600);

  // Start I2C transmission
  Wire.beginTransmission(Addr);
  // Stop I2C transmission
  Wire.endTransmission();
  delay(300);
}

void loop()
{
  unsigned int data[2];
  
  // Start I2C transmission
  Wire.beginTransmission(Addr);
  // Send humidity measurement command, NO HOLD MASTER
  Wire.write(0xF5);
  // Stop I2C transmission
  Wire.endTransmission();
  delay(500);
    
  // Request 2 bytes of data
  Wire.requestFrom(Addr, 2);

  // Read 2 bytes of data
  // humidity msb, humidity lsb 
  if(Wire.available() == 2)
  {
    data[0] = Wire.read();
    data[1] = Wire.read();
  }
    
  // Convert the data
  float humidity  = ((data[0] * 256.0) + data[1]);
  humidity = ((125 * humidity) / 65536.0) - 6;

  // Start I2C transmission
  Wire.beginTransmission(Addr);
  // Send temperature measurement command, NO HOLD MASTER
  Wire.write(0xF3);
  // Stop I2C transmission
  Wire.endTransmission();
  delay(500);
    
  // Request 2 bytes of data
  Wire.requestFrom(Addr, 2);
  
  // Read 2 bytes of data
  // temp msb, temp lsb
  if(Wire.available() == 2)
  {
    data[0] = Wire.read();
    data[1] = Wire.read();
  }

  // Convert the data
  float temp  = ((data[0] * 256.0) + data[1]);
  float cTemp = ((175.72 * temp) / 65536.0) - 46.85;
  float fTemp = cTemp * 1.8 + 32;
   
  // Output data to serial monitor
  Serial.print("Relative humidity : ");
  Serial.print(humidity);
  Serial.println(" % RH");
  Serial.print("Temperature in Celsius : ");
  Serial.print(cTemp);
  Serial.println(" C");
  Serial.print("Temperature in Fahrenheit : ");
  Serial.print(fTemp);
  Serial.println(" F");
  delay(500);
}

Being that this just works with the SDA and SCL pins that are hardwired into the board and being called by the Wire.h file extension, I can't use this for multiple sensors. So I've been trying to find an extension that allows me to set multiple digital lines for more I2C comms. I've used SoftI2CMaster, but I'm not sure how to send the information for serial to read. My attempt is down below, along with the error messages that I am receiving:

// Distributed with a free-will license.
// Use it any way you want, profit or free, provided it fits in the licenses of its associated works.
// SI7021
// This code is designed to work with the SI7021_I2CS I2C Mini Module available from ControlEverything.com.
// https://www.controleverything.com/content/Humidity?sku=SI7021_I2CS#tabs-0-product_tabset-2

// Including the ports before the program
#define SDA_PORT PORTA
#define SDA_PIN 22
#define SCL_PORT PORTA
#define SCL_PIN 23

#define I2C_TIMEOUT 100

#include <SoftI2CMaster.h>    //including a test I2C program to implement multiple sensors


void setup()
{
  // Initialise I2C communication as MASTER
  i2c_read(true);
  // Initialise serial communication, set baud rate = 9600
  Serial.begin(9600);
    
  // Start I2C transmission
  Wire.beginTransmission(var1);
  // Stop I2C transmission
  Wire.endTransmission();
  delay(500);
}

void loop()
{
  unsigned int data[2];
  
  // Start I2C transmission
  Wire.beginTransmission(var1);
  // Send humidity measurement command, NO HOLD MASTER
  Wire.write(var1);
  // Stop I2C transmission
  Wire.endTransmission();
  delay(500);
    
  // Request 2 bytes of data
  Wire.requestFrom(var1, 2);

  // Read 2 bytes of data
  // humidity msb, humidity lsb 
  if(Wire.available() == 2)
  {
    data[0] = Wire.read();
    data[1] = Wire.read();
  }
    
  // Convert the data
  float humidity  = ((data[0] * 256.0) + data[1]);
  humidity = ((125 * humidity) / 65536.0) - 6;

  // Start I2C transmission
  Wire.beginTransmission();
  // Send temperature measurement command, NO HOLD MASTER
  Wire.write(0xFFFF);
  // Stop I2C transmission
  Wire.endTransmission();
  delay(500);
    
  // Request 2 bytes of data
  Wire.requestFrom(var1, 2);
  
  // Read 2 bytes of data
  // temp msb, temp lsb
  if(Wire.available() == 2)
  {
    data[0] = Wire.read();
    data[1] = Wire.read();
  }

  // Convert the data
  float temp  = ((data[0] * 256.0) + data[1]);
  float cTemp = ((175.72 * temp) / 65536.0) - 46.85;
  float fTemp = cTemp * 1.8 + 32;
   
  // Output data to serial monitor
  Serial.print("Relative humidity : ");
  Serial.print(humidity);
  Serial.println(" % RH");
  Serial.print("Temperature in Celsius : ");
  Serial.print(cTemp);
  Serial.println(" C");
  Serial.print("Temperature in Fahrenheit : ");
  Serial.print(fTemp);
  Serial.println(" F");
  delay(1000);
}
Arduino: 1.8.1 (Linux), Board: "Arduino/Genuino Mega or Mega 2560, ATmega2560 (Mega 2560)"

/home/ken/Arduino_Projects/Temp_Humid_Sensor/DocB_i2c_SI7021/DocB_i2c_SI7021.ino/DocB_i2c_SI7021.ino.ino: In function 'void setup()':
DocB_i2c_SI7021.ino:26: error: 'Wire' was not declared in this scope
   Wire.beginTransmission(var1);
   ^
DocB_i2c_SI7021.ino:26: error: 'var1' was not declared in this scope
   Wire.beginTransmission(var1);
                          ^
/home/ken/Arduino_Projects/Temp_Humid_Sensor/DocB_i2c_SI7021/DocB_i2c_SI7021.ino/DocB_i2c_SI7021.ino.ino: In function 'void loop()':
DocB_i2c_SI7021.ino:37: error: 'Wire' was not declared in this scope
   Wire.beginTransmission(var1);
   ^
DocB_i2c_SI7021.ino:37: error: 'var1' was not declared in this scope
   Wire.beginTransmission(var1);
                          ^
exit status 1
'Wire' was not declared in this scope

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

Any guidance or help with this would be greatly appreciated. Thank you!

Thank you for your response!

I'm sorry about var1. I had added that as a float before, but since it didn't work, I removed it from the top of the code. This was supposed to be the value of whatever signal the SDA was receiving.

So, all I have to do is add more sensors and connect them to the SCL and SDA lines? I would think it would make it cleaner and more readable if I was able to address them individually and be able to display them with their correct labels.

Alright, will do

Your code lacks a complete #include line. It should read
#include <Wire.h>
then the first compiler error should go away.

Your sensors are not easy to use with a Mega, because they operate on 3.6V (max.), where the Mega works on 5V. They also have no programmable slave address, so that only one such sensor can be connected to an I2C bus. You need I2C multiplexers, so that every sensor can be connected to its own bus. These multiplexers should allow to shift the signal voltage level as well.

I did my research, and as it turns out, those sensors all have the same address, which means I can't run them on the same line. My initial idea, and what I am continuing to try, is to address pins on the board to be software I2C. This means that I could have all eleven sensors on the board, each with its own SCL/SDA line, and its own, differentiated output.

In the code above, I used SoftwareI2C, but at some point, it said it was incompatible with the Mega. So, I did another Google search to see if I could find some code that would work. I found Slow SoftI2C Master ( GitHub - felias-fogg/SlowSoftI2CMaster ), and it compiles just fine, but the readings I am getting give indication that the sensor is not connected to the pins, even though they are. I'm assuming that Uno and Mega's pinouts are different, at least when you call them in code.

This is what I have so far:

#include <SlowSoftI2CMaster.h>
#include <avr/io.h>

SlowSoftI2CMaster si = SlowSoftI2CMaster (20, 21);    //(SDA, SCL)

#define Addr 0x40

void setup()
{
  
  // Initialise I2C communication as MASTER
  si.i2c_start(Addr|I2C_READ);
  // Initialise serial communication, set baud rate = 9600
  Serial.begin(9600);
    
  // Start I2C transmission
  si.i2c_read(Addr);
  // Stop I2C transmission
  si.i2c_stop();
  delay(500);
}

void loop()
{
  unsigned int data[2];
  
  // Start I2C transmission
  si.i2c_start(Addr | I2C_WRITE);
  // Send humidity measurement command, NO HOLD MASTER
  si.i2c_write(Addr);
  // Stop I2C transmission
  si.i2c_stop();
  delay(500);
  
  data[0] = si.i2c_read(Addr);
  data[1] = si.i2c_read(Addr);
  
    
  // Convert the data
  float humidity  = ((data[0] * 256.0) + data[1]);
  humidity = ((125 * humidity) / 65536.0) - 6;

  // Start I2C transmission
  si.i2c_start(Addr | I2C_WRITE);
  // Send temperature measurement command, NO HOLD MASTER
  si.i2c_write(Addr);
  // Stop I2C transmission
  si.i2c_stop();
  delay(500);
 
  data[0] = si.i2c_read(Addr);
  data[1] = si.i2c_read(Addr);
  
  // Convert the data
  float temp  = ((data[0] * 256.0) + data[1]);
  float cTemp = ((175.72 * temp) / 65536.0) - 46.85;
  float fTemp = cTemp * 1.8 + 32;
   
  // Output data to serial monitor
  Serial.print("Relative humidity : ");
  Serial.print(humidity);
  Serial.println(" % RH");
  Serial.print("Temperature in Celsius : ");
  Serial.print(cTemp);
  Serial.println(" C");
  Serial.print("Temperature in Fahrenheit : ");
  Serial.print(fTemp);
  Serial.println(" F");
  delay(1000);
}

If you compare the code in my initial post and this one, they are very similar, but I had to change the object functions and get rid of other things that weren't in the header files being used.

DrDiettrich, I'm sorry I forgot to add the Wire.h, but I did use it in the original code. I noticed that error right away, haha. As far as the voltage is concerned, there's a 3.3 V line on the Mega. I've seen the multiplexer idea, but that doesn't solve the 11 sensors issue. The most I've seen is a multiplexer with 8 inputs, so I'd still need another I2C line, which I'm trying to resolve.

Thank you so much for your help guys! It helps steer me in a direction, instead of just throwing ideas around in the dark.

A way to avoid software i2c might be to use a couple of these i2c multiplexers.

You can use multiple multiplexers.

On the same SDA line?

Yes, on the same bus. Up to 8 multiplexers from the link in #7 can be used at the same time.

Thank you again. Before I bite the bullet and get two multiplexers, do you have a link for a code example to attach the extra multiplexer? I looked up some code already and it looks like the code runs through a for loop and stops when it doesn't detect any more pins, but I'm not sure how it would run through the pin that has the extra mux on it. I'm assuming that the for loop detects those pins on the extra mux, but I'm not sure.

Thank you again for your help. I may just see this done yet!!

You give every multiplexer a distinct I2C address, by tying their address inputs low or high. Then tell a multiplexer which channel to use, and finally talk to the sensor connected to that channel.

If you already have code to talk to one sensor, that code does not require any further changes. Simply add the code for the channel selection, then call your sensor reading function. The results should be stored in an array, for subsequent processing.