The I2C communication protocol is great because it requires only two wires to connect your microcontroller to one, two or multiple sensors. But this only works if each sensor has its own I2C address. Suppose you want to connect four (or more) sensors with the same fixed address? One way would be to use the SPI protocol instead, as I have described on another tutorial.
But not every sensor or breakout board allows you to use the SPI protocol. So what can you do? Well, just use an I2C multiplexer,
like the chip TCA9548A from Texas Instruments (Need a datasheet?). This device is available as a breakout board from Adafruit and you can check their great tutorial using this link: Adafruit TCA9548A 1-to-8 I2C Multiplexer Breakout.
Okay, so this thing has 8 channels, that means I can connect up to 8 sensors to the I2C bus, right? Well, yes, but way more than that! Let's take a closer look at this device and how to use it!
This is the TCA9548A Multiplexer. You can connect it to your Arduino just like any other I2C device:
Just as the name implies, the reset pin (RST Pin) is used to reset the multiplexer by connecting it to GND. It is pulled HIGH by default, so you can just leave it as it is. The pins A0, A1 and A2 are used in case we need to change the I2C address from the multiplexer. In this example, just leave them unconnected.
The other 16 pins available are connected to the 8 channels from the multiplexer. So pins SD0 and SC0 correspond to the data line (SDA) and clock line (SCL) from the first I2C bus; pins SD1 and SC1 correspond to the lines from the second I2C bus, and so forth.
Each I2C bus from the multiplexer is independent from each other, and just like the I2C bus from your Arduino, you can connect multiple devices with different I2C addresses to each bus from the multiplexer!
In this simple tutorial you will see how to connect and program the multiplexer to four environmental sensors BME280 using the same I2C address (0x77). Take a look at the picture below, this is what we are talking about. We want to connect these four boards to an Arduino Uno using the I2C protocol. In this particular case, all four sensors share the same I2C address (0x77).
Let's connect them first to each other, then we will bring the Arduino Uno to the picture. First connect all GND pins from all boards to each other (blue wires). Since all boards run on either 5V or 3.3V, we can use either to power all boards. So let's connect 5V to all boards using the red wires.
Now we will connect the first sensor to the first I2C bus from the multiplexer. For that, connect the SDI pin from the sensor to the SD0 from the multiplexer. Now connect the SCK from the sensor to the SC0 from the multiplexer.
Just repeat the same to all sensors, connecting each one to a different I2C bus from the multiplexer. As you can see, after all that we have four different I2C buses, where the white wires correspond to the clock lines and the grey wires represent the data lines.
Finally, connect the data and clock lines from the Arduino Uno to the multiplexer. In this case, pin A4 from the Arduino goes to the SDA pin from the multiplexer, while pin A5 from the Arduino connects to pin SCL from the multiplexer. As mentioned before, we are using 5V to power all boards. So the 5V pin from the Arduino goes to the VIN pin from the multiplexer, as well as the VCC pins from each boards (red wires). And of course, connect all GND pins to each other (blue wires).
The sensors and the multiplexer are now all happily connected to each other and to an Arduino Uno. So let's start programming!
The multiplexer TCA9548A does not require any library, but the BME280 sensor does. If you didn't do it already, go ahead and download it now from the link below. If you want to connect the multiplexer to another sensor, just ignore this step.
You can now download the Arduino sketch I have written for this example from the next link. But don't worry, we will go through the code step by step.
At the very beginning of our sketch, we include the Wire library to use the I2C protocol and the BlueDot BME280 library. The next step is very important. For every sensor we want to use, we need to create a different instance. In this case we are connecting four BME280 sensors, so we create four different instances (bme280_0, bme280_1, bme280_2 and bme280_3). The importance of these instances will be clear down below.
#include <Wire.h>
#include "BlueDot_BME280.h" BlueDot_BME280 bme280_0;
BlueDot_BME280 bme280_1;
BlueDot_BME280 bme280_2;
BlueDot_BME280 bme280_3;
Now we define the I2C address for the multiplexer using the variable TCAADDR. If the pins A0, A1 and A2 from the multiplexer board are left unconnected, then we are using the 0x70 address. Your Arduino will be seeing only this address on the I2C bus. Following that, we create the function tcaselect for the multiplexer. Through this function, we can tell the multiplexer to toggle between its eight channels and use the I2C protocol to communicate to any device connected to it. So it first communicates through pins SD0 and SC0, then through pins SD1 and SC1, all the way up to pins SD7 and SC7.
#define TCAADDR 0x70
void tcaselect (uint8_t i) {
if (i > 7) return;
Wire.beginTransmission(TCAADDR);
Wire.write(1 << i);
Wire.endTransmission();
}
After starting the serial communication (at a baud rate of 9600, but you can go faster if you want), and enabling the I2C protocol through the Wire.begin() function, we initialize the first BME280 sensor. Then we tell the multiplexer to connect to the sensor on pins SD0 and SC0, using the function tcaselect(0). After that, for every variable and every function we use the instance related to the first sensor (bme280_0). We are basically initializing the first sensor with this configuration.
//*************INITIALIZING FIRST SENSOR*******************************
tcaselect(0);
bme280_0.parameter.communication = 0;
bme280_0.parameter.I2CAddress = 0x77; //Choose I2C Address
bme280_0.parameter.sensorMode = 0b11; //Choose sensor mode
bme280_0.parameter.IIRfilter = 0b100; //Setup for IIR Filter
bme280_0.parameter.humidOversampling = 0b101; //Setup Humidity Oversampling
bme280_0.parameter.tempOversampling = 0b101; //Setup Temperature Oversampling
bme280_0.parameter.pressOversampling = 0b101; //Setup Pressure Oversampling
if (bme280_0.init() != 0x60)
{ Serial.print(F("BME280 Nr.1 detected?\t")); Serial.println(F("No")); }
else
{ Serial.print(F("BME280 Nr.1 detected?\t")); Serial.println(F("Yes")); }
//**********************************************************************
We now repeat the procedure for the second, third and fourth sensors. Note that we begin the initialization with the function tcaselect and that each sensor has its own instance (bme280_1, bme280_2 and bme280_3) and its own set of parameters. That means, we can use a different configuration for each sensor.
//*************INITIALIZING SECOND SENSOR*******************************
tcaselect(1);
bme280_1.parameter.communication = 0;
bme280_1.parameter.I2CAddress = 0x77; //Choose I2C Address
bme280_1.parameter.sensorMode = 0b11; //Choose sensor mode
bme280_1.parameter.IIRfilter = 0b100; //Setup for IIR Filter
bme280_1.parameter.humidOversampling = 0b101; //Setup Humidity Oversampling
bme280_1.parameter.tempOversampling = 0b101; //Setup Temperature Oversampling
bme280_1.parameter.pressOversampling = 0b101; //Setup Pressure Oversampling
if (bme280_1.init() != 0x60)
{ Serial.print(F("BME280 Nr.2 detected?\t")); Serial.println(F("No")); }
else
{ Serial.print(F("BME280 Nr.2 detected?\t")); Serial.println(F("Yes")); }
//**********************************************************************
/*************INITIALIZING THIRD SENSOR*******************************
tcaselect(2);
bme280_2.parameter.communication = 0;
bme280_2.parameter.I2CAddress = 0x77; //Choose I2C Address
bme280_2.parameter.sensorMode = 0b11; //Choose sensor mode
bme280_2.parameter.IIRfilter = 0b100; //Setup for IIR Filter
bme280_2.parameter.humidOversampling = 0b101; //Setup Humidity Oversampling
bme280_2.parameter.tempOversampling = 0b101; //Setup Temperature Oversampling
bme280_2.parameter.pressOversampling = 0b101; //Setup Pressure Oversampling
if (bme280_2.init() != 0x60)
{ Serial.print(F("BME280 Nr.3 detected?\t")); Serial.println(F("No")); }
else
{ Serial.print(F("BME280 Nr.3 detected?\t")); Serial.println(F("Yes")); }
//**********************************************************************
/*************INITIALIZING FOURTH SENSOR*******************************
tcaselect(3);
bme280_3.parameter.communication = 0;
bme280_3.parameter.I2CAddress = 0x77; //Choose I2C Address
bme280_3.parameter.sensorMode = 0b11; //Choose sensor mode
bme280_3.parameter.IIRfilter = 0b100; //Setup for IIR Filter
bme280_3.parameter.humidOversampling = 0b101; //Setup Humidity Oversampling
bme280_3.parameter.tempOversampling = 0b101; //Setup Temperature Oversampling
bme280_3.parameter.pressOversampling = 0b101; //Setup Pressure Oversampling
if (bme280_3.init() != 0x60)
{ Serial.print(F("BME280 Nr.4 detected?\t")); Serial.println(F("No")); }
else
{ Serial.print(F("BME280 Nr.4 detected?\t")); Serial.println(F("Yes")); }
//**********************************************************************
After initializing the sensors, we go to the loop function and start the measurements. First of all, we start with the tcaselect(0) function. We are basically using three functions to read values for temperature (function readTempC), humidity (readHumidity) and pressure (readPressure). Just as before, using the instance bme280_0 before each function to address the first sensor.
tcaselect(0);
Serial.print(F("Duration1[s]=\t"));
Serial.print(float(millis())/1000);
Serial.print("\t");
Serial.print(F("Temperature1[C]=\t"));
Serial.print(bme280_0.readTempC());
Serial.print(F("Humidity1[%]=\t"));
Serial.print(bme280_0.readHumidity());
Serial.print("\t");
Serial.print(F("Pressure1[hPa]=\t"));
Serial.print(bme280_0.readPressure());
Serial.print("\t");
Serial.println();
Repeating the same for the remaining three sensors, we get the rest of the code. Now upload the code and open the serial monitor at 9600 baud speed to get the temperature, humidity and pressure of all four sensors.
xkcd.com