Connect 4 i2c sensor on an Arduino?

Hello Koepel,

My last code :

#include <Wire.h>
#include <SHTSensor.h>

#include <Ethernet.h>
#include <HttpClient.h>


// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };


// if you don't want to use DNS (and reduce your sketch size)
// use the numeric IP instead of the name for the server:
//IPAddress server(74,125,232,128);  // numeric IP for Google (no DNS)
char server[] = "www.google.com";    // name address for Google (using DNS)


// Set the static IP address to use if the DHCP fails to assign
IPAddress ip(192, 168, xxxx, xxx);
IPAddress myDns(192, 168, xxx, xxx);

const char API_KEY[] = "XXxxxXXXxxxxxXXxxxXXX";
const char BASE_URL[] =  "GET /core/api/jeeApi.php?plugin=virtual&apikey=";
byte HOST[] = { 192, 168, xxx, xxx };


const char URL_SUFFIX[] = "&type=virtual&id=";
const char URL_VALUE[] = "&value=";
const char HTTP[] = " HTTP/1.1";




char stringBuffer[200]; 

EthernetClient client;



#define TCAADDR 0x70
const byte NBSENSORS = 4;
const int bus[NBSENSORS] = { 0, 1, 2, 3 };


int id0t = 4907; // Virtual ID
int id0h = 4908; // Virtual ID
int id1t = 4909; // Virtual ID
int id1h = 4910; // Virtual ID
int id2t = 4911; // Virtual ID
int id2h = 4912; // Virtual ID
int id3t = 4913; // Virtual ID
int id3h = 4914; // Virtual ID

float value0t;
float value1t;
float value2t;
float value3t;
float value0h;
float value1h;
float value2h;
float value3h;


SHTSensor sht;

void setup() {

  Wire.begin();
  Serial.begin(9600);

  
// start the Ethernet connection:
  Serial.println("Initialize Ethernet with DHCP:");
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // Check for Ethernet hardware present
    if (Ethernet.hardwareStatus() == EthernetNoHardware) {
      Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");
      while (true) {
        delay(1); // do nothing, no point running without Ethernet hardware
      }
    }
    if (Ethernet.linkStatus() == LinkOFF) {
      Serial.println("Ethernet cable is not connected.");
    }
    // try to congifure using IP address instead of DHCP:
    Ethernet.begin(mac, ip, myDns);
  } else {
    Serial.print("  DHCP assigned IP ");
    Serial.println(Ethernet.localIP());
  }
  
  
  delay(1000); // let serial console settle

  for (byte i = 0; i < NBSENSORS; i++)
  {

    tcaselect( bus[i] );

    if (sht.init()) {
      Serial.print("init(): success\n");
    } else {
      Serial.print("init(): failed\n");
    }
    sht.setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM); // only supported by SHT3x
  }
}

void loop() {

  for (byte i = 0; i < NBSENSORS; i++)
  {
    //tcaselect(i);
    tcaselect( bus[i] );

    Serial.print("SHT: ");
    Serial.println(i);
    if (sht.readSample()) {
      Serial.print("  RH: ");
      Serial.print(sht.getHumidity(), 2);
      Serial.print("\n");
      Serial.print("  T:  ");
      Serial.print(sht.getTemperature(), 2);
      Serial.print("\n");
      
      if ( i == 0 ){
        value0t=sht.getTemperature();
        value0h=sht.getHumidity();
      }
      if ( i == 1 ){
        value1t=sht.getTemperature();
        value1h=sht.getHumidity();
      }
      if ( i == 2 ){
        value2t=sht.getTemperature();
        value2h=sht.getHumidity();
      }
      if ( i == 3 ){
        value3t=sht.getTemperature();
        value3h=sht.getHumidity();
      }

    } else {
      Serial.print("Error in readSample()\n");
    }
  }

  delay(1000);


  sendToJeedom(value0t, id0t);
  sendToJeedom(value1t, id1t);
  sendToJeedom(value2t, id2t);
  sendToJeedom(value3t, id3t);

  sendToJeedom(value0h, id0h);
  sendToJeedom(value1h, id1h);
  sendToJeedom(value2h, id2h);
  sendToJeedom(value3h, id3h);


}


// sendtoJeedom est donc censé prendre un float en entrée
void sendToJeedom(float value, int id)
{
char sValue[8];
dtostrf(value, 4, 2, sValue);

sprintf(stringBuffer, "%s%s%s%d%s%s%s", BASE_URL, API_KEY, URL_SUFFIX, id, URL_VALUE, sValue, HTTP);

  // give the Ethernet shield a second to initialize:
  delay(1000);
  Serial.print("connecting to host");
  Serial.println("...");

    
  // if you get a connection, report back via serial:
  if (client.connect(HOST, 9080)) {
  //if (client.connect(URL,80)) {
    Serial.print("connected to ");
    Serial.print(client.remoteIP());
    Serial.print(":");
    Serial.println(client.remotePort());
     // Make a HTTP request:
    client.println(stringBuffer);
    //For Docker host
    client.println(F("Host: 0.0.0.0"));

  
    //client.println(HOST);
    client.println("Connection: close");
    client.println();
  } else {
    // if you didn't get a connection to the server:
    Serial.println("connection failed");
  }

  Serial.println(stringBuffer);
}



void tcaselect(uint8_t i) {
  if (i > 7) return;

  Wire.beginTransmission(TCAADDR);
  Wire.write(1 << i);
  Wire.endTransmission();
}

Being bad in development, I think the code can be improved.

It will allow me to control my double flow Heat Recovery Ventilator.
I send the data to my home automation server on which I calculate the air efficiency.
Depending on the result, changes the speed of the fans until achieving the best air efficiency.
The goal is to have intelligent and automatic management.

This:

SHTSensor sht;

is one object for one sensor. With four sensors, you also need four objects. You can make an array of 4 objects. I think that the data is stored in the object. Perhaps with one object all the temperatures and humidity of all sensors are mixed together.

The size of 8 is very small:

char sValue[8];
dtostrf(value, 4, 2, sValue);

Can you make the size 20 or so ? The width of 4 is the minimal width. You don’t want a wrong number to crash the sketch.

The text layout with indents, spaces, new lines, and so on is very good.
But there is some inconsistancy, you use these two styles for the brackets:

for ( ...
{


if ( ... ) {

Can you choose just one style for the text layout ?

The value0t, value0h, value1t, and so on could be in a array. That would make a loop shorter and easier.

float temperature[4];
float humidity[4];

...

  for (byte i = 0; i < NBSENSORS; i++)
  {
    tcaselect( bus[i] );

    Serial.print("SHT: ");
    Serial.println(i);
    if (sht[i].readSample())
    {
      temperature[i] = sht[i].getTemperature();
      humidity[i] = sht[i].getHumidity();

      Serial.print("  RH: ");
      Serial.print( humidity[i], 2);
      Serial.print("\n");
      Serial.print("  T:  ");
      Serial.print( temperature[i], 2);
      Serial.print("\n");
    }

Have you thought about a ESP32 ?

Hello,

Ok, The modified code:

#include <Wire.h>
#include <SHTSensor.h>

#include <Ethernet.h>
#include <HttpClient.h>


// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };


// if you don't want to use DNS (and reduce your sketch size)
// use the numeric IP instead of the name for the server:
//IPAddress server(74,125,232,128);  // numeric IP for Google (no DNS)
char server[] = "www.google.com";    // name address for Google (using DNS)


// Set the static IP address to use if the DHCP fails to assign
IPAddress ip(192, 168, xxxx, xxx);
IPAddress myDns(192, 168, xxx, xxx);

const char API_KEY[] = "XXxxxXXXxxxxxXXxxxXXX";
const char BASE_URL[] =  "GET /core/api/jeeApi.php?plugin=virtual&apikey=";
byte HOST[] = { 192, 168, xxx, xxx };


const char URL_SUFFIX[] = "&type=virtual&id=";
const char URL_VALUE[] = "&value=";
const char HTTP[] = " HTTP/1.1";




char stringBuffer[200];

EthernetClient client;



#define TCAADDR 0x70
const byte NBSENSORS = 4;
const int bus[NBSENSORS] = { 0, 1, 2, 3 };


int id0t = 4907; // Virtual ID
int id0h = 4908; // Virtual ID
int id1t = 4909; // Virtual ID
int id1h = 4910; // Virtual ID
int id2t = 4911; // Virtual ID
int id2h = 4912; // Virtual ID
int id3t = 4913; // Virtual ID
int id3h = 4914; // Virtual ID

float tempValue[NBSENSORS] = { 0, 1, 2, 3 };
float humValue[NBSENSORS] = { 0, 1, 2, 3 };

SHTSensor sht[NBSENSORS] = { 0, 1, 2, 3 };

void setup() {

  Wire.begin();
  Serial.begin(9600);

  
// start the Ethernet connection:
  Serial.println("Initialize Ethernet with DHCP:");
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // Check for Ethernet hardware present
    if (Ethernet.hardwareStatus() == EthernetNoHardware) {
      Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");
      while (true) {
        delay(1); // do nothing, no point running without Ethernet hardware
      }
    }
    if (Ethernet.linkStatus() == LinkOFF) {
      Serial.println("Ethernet cable is not connected.");
    }
    // try to congifure using IP address instead of DHCP:
    Ethernet.begin(mac, ip, myDns);
  } else {
    Serial.print("  DHCP assigned IP ");
    Serial.println(Ethernet.localIP());
  }
  
  
  delay(1000); // let serial console settle

  for (byte i = 0; i < NBSENSORS; i++) {

    tcaselect( bus[i] );

    if (sht[i].init()) {
      Serial.print("init(): success\n");
    } else {
      Serial.print("init(): failed\n");
    }
    sht[i].setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM); // only supported by SHT3x
  }
}

void loop() {

  for (byte i = 0; i < NBSENSORS; i++) {
    //tcaselect(i);
    tcaselect( bus[i] );

    Serial.print("SHT: ");
    Serial.println(i);
    if (sht[i].readSample()) {
      Serial.print("  RH: ");
      Serial.print(sht[i].getHumidity(), 2);
      Serial.print("\n");
      Serial.print("  T:  ");
      Serial.print(sht[i].getTemperature(), 2);
      Serial.print("\n");

      tempValue[i]=sht[i].getTemperature();
      humValue[i]=sht[i].getHumidity();   

    } else {
      Serial.print("Error in readSample()\n");
    }
  }

  delay(1000);


  sendToJeedom(tempValue[0], id0t);
  sendToJeedom(tempValue[1], id1t);
  sendToJeedom(tempValue[2], id2t);
  sendToJeedom(tempValue[3], id3t);

  sendToJeedom(humValue[0], id0h);
  sendToJeedom(humValue[1], id1h);
  sendToJeedom(humValue[2], id2h);
  sendToJeedom(humValue[3], id3h);


}


// sendtoJeedom est donc censé prendre un float en entrée
  void sendToJeedom(float value, int id) {
  char sValue[20];
  dtostrf(value, 4, 2, sValue);
  
  sprintf(stringBuffer, "%s%s%s%d%s%s%s", BASE_URL, API_KEY, URL_SUFFIX, id, URL_VALUE, sValue, HTTP);

  // give the Ethernet shield a second to initialize:
  delay(1000);
  Serial.print("connecting to host");
  Serial.println("...");

    
  // if you get a connection, report back via serial:
  if (client.connect(HOST, 9080)) {
  //if (client.connect(URL,80)) {
    Serial.print("connected to ");
    Serial.print(client.remoteIP());
    Serial.print(":");
    Serial.println(client.remotePort());
     // Make a HTTP request:
    client.println(stringBuffer);
    //For Docker host
    client.println(F("Host: 0.0.0.0"));

  
    //client.println(HOST);
    client.println("Connection: close");
    client.println();
  } else {
    // if you didn't get a connection to the server:
    Serial.println("connection failed");
  }

  Serial.println(stringBuffer);
}



void tcaselect(uint8_t i) {
  if (i > 7) return;

  Wire.beginTransmission(TCAADDR);
  Wire.write(1 << i);
  Wire.endTransmission();
}

But there is something I did not do well.

I have many warning :

sketch_jul03b.ino:68:41: warning: invalid conversion from 'int' to 'SHTSensor::SHTSensorType' [-fpermissive]
 SHTSensor sht[NBSENSORS] = { 0, 1, 2, 3 };
                                         ^
In file included from sketch_jul03b.ino:2:0:
SHTSensor.h:91:3: note:   initializing argument 1 of 'SHTSensor::SHTSensor(SHTSensor::SHTSensorType)'
   SHTSensor(SHTSensorType sensorType = AUTO_DETECT)
   ^~~~~~~~~
sketch_jul03b.ino:68:41: warning: invalid conversion from 'int' to 'SHTSensor::SHTSensorType' [-fpermissive]
 SHTSensor sht[NBSENSORS] = { 0, 1, 2, 3 };
                                         ^
In file included from SHTSensor.h:91:3: note:   initializing argument 1 of 'SHTSensor::SHTSensor(SHTSensor::SHTSensorType)'
   SHTSensor(SHTSensorType sensorType = AUTO_DETECT)
   ^~~~~~~~~
sketch_jul03b.ino:68:41: warning: invalid conversion from 'int' to 'SHTSensor::SHTSensorType' [-fpermissive]
 SHTSensor sht[NBSENSORS] = { 0, 1, 2, 3 };
                                         ^
In file included from sketch_jul03b.ino:2:0:
SHTSensor.h:91:3: note:   initializing argument 1 of 'SHTSensor::SHTSensor(SHTSensor::SHTSensorType)'
   SHTSensor(SHTSensorType sensorType = AUTO_DETECT)
   ^~~~~~~~~
sketch_jul03b.ino:68:41: warning: invalid conversion from 'int' to 'SHTSensor::SHTSensorType' [-fpermissive]
 SHTSensor sht[NBSENSORS] = { 0, 1, 2, 3 };
                                         ^
In file included from sketch_jul03b.ino:2:0:
SHTSensor.h:91:3: note:   initializing argument 1 of 'SHTSensor::SHTSensor(SHTSensor::SHTSensorType)'
   SHTSensor(SHTSensorType sensorType = AUTO_DETECT)

And warning about memory :

As the available memory is low, stability problems could arise.

Before this change, I already had a warning unless I commented Serial.print(sht).
If I comment, I still have them.

I use an Arduino Uno.

When you give a value, the variable will be initialized to it.

These are the same:

int x0 = 500;
int x1 = 4;
int x2 = 100;

int x[3] = { 500, 4, 100 };

You did not initialize the value0t, value1t, value2t variable, so you don't have to initialize the new variables 'tempValue' and 'humValue'.

float tempValue[NBSENSORS];
float humValue[NBSENSORS];

Initializing a object is something special. Because a class defines functions and variables. You can not just assign a integer to it.

SHTSensor sht[NBSENSORS];

You did not assign a number to it before, and there is no need to assigne a number to it now.
The "SHTSensor" is the class. That is a package of functions and variables and what not. It is only a definition of how it should be.
The "sht" is the object that uses memory and you may choose the name. That is what is used runtime.

The index connects everything together. bus[0] is the first bus and sht[0] is the first sensor and tempValue[0] is the first temperature. It makes sense if you think about it :wink:

The Arduino Uno with a Ethernet shield is not a good choice. As soon as you add a SD library or four objects for four sensors, then you could run out of memory. This happens to all of us. What we all do is to change to a Mega 2560 board and after a while we start looking at the ESP32 for internet things and SAMD MKR boards for normal things with sensors.

A Mega 2560 R3 clone is about 10 dollars.
Another solution is perhaps to find a minimalistic library for the sensors.

Hello,

I have corrected and I have no more warning except for the memory problem.
I have clones of the Mega 2560 R3. I will test.

Thank you, thank you, thank you!

EDIT: if I delete all Serial.print(), no more memory warning. To see because the Serial.print () is only used in the console to verify that everything is ok.

Mmmhhhh..

With Arduino Mega 2560 R3, no reading of the sensors ...

init(): failed
init(): failed
init(): failed
init(): failed
SHT: 0
Error in readSample()
SHT: 1
Error in readSample()
SHT: 2
Error in readSample()
SHT: 3
Error in readSample()

That means your I2C is not working. The SDA and SCL pins near the AREF pin are still the same. But the other pins are no longer at A4 and A5 but at pin 20 and 21: Arduino - Wire.

The Ethernet shield with the Arduino Mega 2560 board is a good combination. Keep in mind that the ATmega2560 microcontroller really needs a voltage that is higher than 4.5V and the Ethernet shield draws current. You can measure the voltage at the 5V pin. The ethernet-chip can get really hot, but that seems to be normal (I have added a heatsink to the ethernet chip).

Ok, thank you.
I had stupidly plugged into A4 and A5 (Arduino UNO) because I had seen it on a diagram, without trying to understand.

I tested with the Arduino Mega and it works well.

Regarding the memory problem with an Arduino UNO, I discussed it with someone who made a mini library for SHT and replace the Serial.print () with DEBUG_PRINT (). Debug which I can activate or not by uncommenting / commenting #define SERIAL_DEBUG.
With this (comment mode), only 57% of the memory is use.

In this case, is there any point in using an Arduino Mega?

Note that the Arduino will be with an Ethernet Shield as you know and also a PoE module.
The only power supply will be an Ethernet cable.

This is what I have been using for months on my Arduino UNO with 4 DHT22 sensors.

The code in flash memory is no problem as long as it fits in the memory. If the code has 102% for a Arduino Uno, then turning off messages can get it below 100%.

There are other things to do before turning of messages.
Do you know the F() macro ?

Serial.println("Hello");   // The text "Hello" is in flash memory and in sram memory.
Serial.println(F("Hello"));  // The text "Hello" is only in flash memory

The sram, dynamic memory, is the problem.
The Arduino Uno has 2kbyte sram.
The Arduino Mega 2560 has 8kbyte sram.

This is an example for a project on a Arduino Uno, the compiler says:

Sketch uses 31330 bytes (97%) of program storage space. Maximum is 32256 bytes.
Global variables use 1165 bytes (56%) of dynamic memory, leaving 883 bytes for local variables. Maximum is 2048 bytes.

The 97% is no problem, but I can probably not add new things.
The 56% is very good. I am sure because I check it runtime, and 656 bytes are still not used.

Ok, thank you for all.