Sensors with same slave address (I2C)

Hello Guys:) I am new here and also a little new to arduino and sensors.
For my Project I am using the Arduino Uno and 2 or maybe 3 of the LPS25H Pressure/Altitude Sensors (Pololu - LPS25H Pressure/Altitude Sensor Carrier with Voltage Regulator).

I want to measure pressure of both sensors at the same time with I2C. Thats where the problem starts. Because of the I2C Interface there will be data collision. Right? And I cant change the slave addresses of my sensors either.

I tried a couple of things with the CS and SDO pin (pulling them down and back up again), but that didnt work. Now I just found this multiplexer (Overview | Adafruit TCA9548A 1-to-8 I2C Multiplexer Breakout | Adafruit Learning System) which looks great. Maybe someone could help me to understand if this multiplexer will really make my problem disappear.

Thank you so much.

If you want to use two, that can be done. See the datasheet, second paragraph of section 5.2.1

Pete

According to the datasheet in your link, you need CS high for I2C. Then SDO/SA0 is either high or low to give you either of two addresses. The address is 1011101 with SA0 high, and 1011100 if low (5D or 5C hex). (But set them and leave them, don't toggle them which is what I gather you are trying.

So you should at least be able to use 2 without the need for a mux.

And you can use the i2c scanner to check the addresses.

Wow. This post looks an awful lot like the on posted by tanjam in the Project Guidance section. What do you all think?

tanjam:
Hello Guys,

I have the same problem as people above. I am using the Arduino Uno and 2 of the LPS25H Pressure/Altitude Sensors (Pololu - LPS25H Pressure/Altitude Sensor Carrier with Voltage Regulator).

I want to measure pressure of both sensors at the same time. Is that possible using the I2C Connection? The problem I have is, that I dont know how to change the addresses of one of the sensors to be able to read the measurements, since they appear to have the same address.

I was trying to use the CS pin, but as soon as I pull it down the measurement stops and doesnt continue when I pull it back up. I also did the same with the SDO pin.

Is it maybe necessary to us a multiplexer? I am a beginner with arduino and with these sensors. I would appreciate if u can explain it as simple as possible :slight_smile:

Thank you so much for all your help. Best wishes

As I replied to tanjam:

To answer your question, you read page 16 of the datasheet for the device on the webpage you provided.

To head off the next question. Are you able to read one of the sensors? At what address are you reading that sensor?

FYI. You can only use two of these devices. Once you read the datasheet, you will see why.

LesserMole:
According to the datasheet in your link, you need CS high for I2C. Then SDO/SA0 is either high or low to give you either of two addresses. The address is 1011101 with SA0 high, and 1011100 if low (5D or 5C hex). (But set them and leave them, don't toggle them which is what I gather you are trying.

So you should at least be able to use 2 without the need for a mux.

To do this, will the pullup resistor as noted in the schematic have to be removed?

adwsystems:
To do this, will the pullup resistor as noted in the schematic have to be removed?

I hadn't seen that one, but I reckon the pullups put it in I2C mode with and address 5D by default. Switching to the 5C address would surely be just a wire to ground which would win the voltage divider battle without removing the pullup. Ditto the CS to ground for SPI mode.

Okay Thank you so much:) for all the help already.
I did the scanning and I also put one of the SD0 to low and one to high yesterday already and I do get this when using the scanner:

I2C device found at address 0x5C !
I2C device found at address 0x5D !

So that works. But if I have them both connected…the data is a mess. Which I understand because I cant ask for data at the same time with I2C. After that I tried to pull one of the sensors low on SD0. Which does make just one sensor respond.

But as soon as I want to pull that sensor back to high and ask data from it, it doesnt send any data (looking at the Serial Monitor). Every time I start the Serial Monitor again, the sensor sends data again. Is there a command in the code which I can use to make the arduino continue reading? Because thats what the arduino stops doing, I assume. Please correct me if I am wrong.

The code that I am using is this:

#include <Wire.h>
#include <LPS.h>

LPS ps;

void setup()
{
Serial.begin(9600);
Wire.begin();

if (!ps.init())
{
Serial.println(“Failed to autodetect pressure sensor!”);
while (1);
}

ps.enableDefault();
}

void loop()
{
float pressure = ps.readPressureMillibars();
float altitude = ps.pressureToAltitudeMeters(pressure);
float temperature = ps.readTemperatureC();

Serial.print(“p: “);
Serial.print(pressure);
Serial.print(” mbar\ta: “);
Serial.print(altitude);
Serial.print(” m\tt: “);
Serial.print(temperature);
Serial.println(” deg C”);
{
Serial.begin(9600);
Wire.begin();

if (!ps.init())
{
Serial.println(“Failed to autodetect pressure sensor!”);
while (1);
}

ps.enableDefault();
}
delay(100);
}

ninni:
But if I have them both connected…the data is a mess.

You need a way to have like a ps1 and ps2 at the same time, instantiated with different addresses. But I had a look in LPS.cpp and it hard-codes the address in the library, although you can edit it of course, but it doesn’t look like you can instantiate two sensors with different addresses. As soon as the library fires up, it has one address or the other, which it uses in its Wire transmissions:

#define SA0_LOW_ADDRESS  0b1011100
#define SA0_HIGH_ADDRESS 0b1011101
.
.
.
LPS::LPS(void)
{
  _device = device_auto;
  
  // Pololu board pulls SA0 high, so default assumption is that it is
  // high
  address = SA0_HIGH_ADDRESS;
}
.
.
.
Wire.beginTransmission(address);

I may be missing something but looks as if the library doesn’t support two simultaneous sensors.

I wonder if it’s feasible to double up the actually library, naming one LPS_1 and another LPS_2 with edits inside the .h and .cpp to match those names, and hardcode different addresses, then have 2x similar but different libraries installed in the ide and do 2x includes and run each sensor off a different library?

That may be a crazy idea, and probably is, so perhaps wait to see what others more knowledgeable than I might think.

edit: In contrast, just to illustrate how other I2C libraries operate, the Liquid Crystal one gets you to put the address of the screen in when you instantiate:

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>

// Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2); //......................includes address

The LPS library does not let you do that:

#include <Wire.h>
#include <LPS.h>

LPS ps; //............................ no address

LesserMole:
You need a way to have like a ps1 and ps2 at the same time, instantiated with different addresses. But I had a look in LPS.cpp and it hard-codes the address in the library, although you can edit it of course, but it doesn't look like you can instantiate two sensors with different addresses. As soon as the library fires up, it has one address or the other, which it uses in its Wire transmissions:

#define SA0_LOW_ADDRESS  0b1011100

#define SA0_HIGH_ADDRESS 0b1011101
.
.
.
LPS::LPS(void)
{
  _device = device_auto;
 
  // Pololu board pulls SA0 high, so default assumption is that it is
  // high
  address = SA0_HIGH_ADDRESS;
}
.
.
.
Wire.beginTransmission(address);

So the programmer put both addresses in the library but only used the xx01 address and is hard coded with in the library?

(I didn't look at the library personally, just the code snippet posted)

adwsystems:
So the programmer put both addresses in the library but only used the xx01 address and is hard coded with in the library?

(I didn't look at the library personally, just the code snippet posted)

Looks that way- you would just edit this line:

address = SA0_HIGH_ADDRESS; 
//or edit to say SA0_LOW_ADDRESS

.... according to which address you set on the hardware. Seems he didn't think anyone would ever have two sensors on one system?

LesserMole:
Seems he didn’t think anyone would ever have two sensors on one system?

Given the address is “hardwired” on the module by having the resistor soldered in place to the SA0 pin to select the xx01 address, that correlates. The code makes sense given the hardware. The hardware design doesn’t make sense which makes the programming choice in the library questionable.

adwsystems:
Given the address is "hardwired" on the module by having the resistor soldered in place to the SA0 pin to select the xx01 address, that correlates.

It correlates as a default yes, and it can be changed, so if the module has the pad grounded then the library can be edited. But (unless I'm missing something) it's a shit design since it doesn't allow two modules to be used because you can't have two addresses active in the software, ala the way for example the Liquid Crystal library gets the address at instantiation time so you could easily run lcd1 and lcd2.

LesserMole:
It correlates as a default yes, and it can be changed, so if the module has the pad grounded then the library can be edited.

Based on the schematic and what I can see in the photos, the (easy) option to connect the SA0 pin to grounf does not exist. Ie., the only connect that I can see is to the resistor to power, there does not appear to be a jumper available to connect to ground. The other easy option is floating, that may be OK or not, that is based on the internals of the chip. The hard/ugly way would be to solder a piece of magnet or wire wrap wire from the pull-up/pin junction to a ground some where. That would work to change SA0 to 0, but as I said is ugly. Modules like this should have that option built in. Therefore ...

LesserMole:
(unless I'm missing something) it's a shit design since it doesn't allow two modules to be used because you can't have two addresses active in the software...

I think you are on target and have not missed anything. I'm disappointed in Pololu for having this revision (hint hint, if someone from there is reading this) and library on the market.

adwsystems:
Based on the schematic and what I can see in the photos, the (easy) option to connect the SA0 pin to grounf does not exist.

OP has already successfully got two addresses, see #8. Not sure if s/he did that elegantly or by some ugly kluge, which may or may not translate to a production version, but nevertheless, both addresses do work for OP according to the scanner.

Perhaps this is the first time anyone except OP and toejam has needed to run two at the same time?

Thank you so much for your help. So what do you normally do if you want to measure pressure of lets say two different Chambers at the same time? Is the Arduino Uno not useful for that? Would buying a multiplexer help?
Like this one: Overview | Adafruit TCA9548A 1-to-8 I2C Multiplexer Breakout | Adafruit Learning System

Thanks so much

Normally I check the limitations of the modules before I purchase them. Some have legitimate restrictions, some do not. This is the latter. The issue is not because of the UNO nor as generic as its ability to read two or more sensors. The problem lies wholly with the module design and library for the unit.

Thinking of it quickly, I see few options.

  1. Fix the hardware (which it looks like you may have done if you can see the two addresses) and then fix the library, or

  2. Roll your own mux with a 74xx AND chip, or

  3. The mux listed seems like it would also work

Not an Arduino limitation, nor is it a limitation of the module, although looks like they didn't make it easy to use the other address which is a feature of the chip itself and perhaps made physically awkward by the layout of the module. But you sussed that already since the scanner shows both.

It's a limitation of that library. Perhaps there's another library from another source.

Perhaps someone could re-jig that library to have the address as an instantiation parameter, ala the LCD one. That someone will not be me: waaaay over my head.

Perhaps you could try my idea of copying and editing the library to have two totally independent libraries and run one sensor off of each: I see nobody commented on that idea as being nonsense or feasible. I don't know enough about libraries to know what you would have to change to make them work side by side.

edit: it might be worthwhile contacting the library author via github to see if he has a suggestion?

Thanks so much for your comments.

So I bought the multiplexer TCA9548a
(Overview | Adafruit TCA9548A 1-to-8 I2C Multiplexer Breakout | Adafruit Learning System)
I ran a TCA script and the 2 LPS25H were detected.

So now I have this example code which they use for 2 acceleration sensors (same address).
Can someone help me understand what I need to change and how? Should I change the example code or can I maybe just add some pieces of the code to my LPS25H sensor code to make the multiplexer work?
I understand that in the Example Code I dont Need the adafruit_sensor and adafruit_HMC5883_U library. But how do I assign a unique address to each sensor for example…
Here is the LPS library (GitHub - pololu/lps-arduino: Arduino library for Pololu LPS25H and LPS331AP boards)
Here is the Adafruit_HMC5883_U.h library (https://github.com/adafruit/Adafruit_HMC5883_Unified/blob/master/Adafruit_HMC5883_U.h)

In the adafruit Forum I got this reply but dont know where I Need to put it. :

You do not need Adafruit_Sensor.h for the multiplexer. You just need to include the tcaselect function in your code. You can call that function in you loop() to select the channel for the sensor you want to read.

Code: Select all | TOGGLE FULL SIZE
void tcaselect(uint8_t i) {
if (i > 7) return;

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

I am so happy for any reply. Or help…thanks guys!

Example Code:

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_HMC5883_U.h>

#define TCAADDR 0x70

/* Assign a unique ID to this sensor at the same time */
Adafruit_HMC5883_Unified mag1 = Adafruit_HMC5883_Unified(1);
Adafruit_HMC5883_Unified mag2 = Adafruit_HMC5883_Unified(2);

void displaySensorDetails(Adafruit_HMC5883_Unified *mag)
{
sensor_t sensor;
mag->getSensor(&sensor);
Serial.println("------------------------------------");
Serial.print ("Sensor: "); Serial.println(sensor.name);
Serial.print (“Driver Ver: “); Serial.println(sensor.version);
Serial.print (“Unique ID: “); Serial.println(sensor.sensor_id);
Serial.print (“Max Value: “); Serial.print(sensor.max_value); Serial.println(” uT”);
Serial.print (“Min Value: “); Serial.print(sensor.min_value); Serial.println(” uT”);
Serial.print (“Resolution: “); Serial.print(sensor.resolution); Serial.println(” uT”);
Serial.println(”------------------------------------”);
Serial.println(””);
delay(500);
}

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

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

void setup(void)
{
Serial.begin(9600);
Serial.println(“HMC5883 Magnetometer Test”); Serial.println("");

/* Initialise the 1st sensor /
tcaselect(2);
if(!mag1.begin())
{
/
There was a problem detecting the HMC5883 … check your connections */
Serial.println(“Ooops, no HMC5883 detected … Check your wiring!”);
while(1);
}

/* Initialise the 2nd sensor /
tcaselect(6);
if(!mag2.begin())
{
/
There was a problem detecting the HMC5883 … check your connections */
Serial.println(“Ooops, no HMC5883 detected … Check your wiring!”);
while(1);
}

/* Display some basic information on this sensor */
tcaselect(2);
displaySensorDetails(&mag1);
tcaselect(6);
displaySensorDetails(&mag2);
}

void loop(void)
{
/* Get a new sensor event */
sensors_event_t event;

tcaselect(2);
mag1.getEvent(&event);

/* Display the results (magnetic vector values are in micro-Tesla (uT)) */
Serial.print("Sensor #1 - ");
Serial.print("X: “); Serial.print(event.magnetic.x); Serial.print(” ");
Serial.print("Y: “); Serial.print(event.magnetic.y); Serial.print(” ");
Serial.print("Z: “); Serial.print(event.magnetic.z); Serial.print(” ");Serial.println(“uT”);

tcaselect(6);
mag2.getEvent(&event);
/* Display the results (magnetic vector values are in micro-Tesla (uT)) */
Serial.print("Sensor #2 - ");
Serial.print("X: “); Serial.print(event.magnetic.x); Serial.print(” ");
Serial.print("Y: “); Serial.print(event.magnetic.y); Serial.print(” ");
Serial.print("Z: “); Serial.print(event.magnetic.z); Serial.print(” ");Serial.println(“uT”);

delay(500);
}

LPS25H Code:
#include <Wire.h>
#include <LPS.h>

LPS ps;

void setup()
{
Serial.begin(9600);
Wire.begin();

if (!ps.init())
{
Serial.println(“Failed to autodetect pressure sensor!”);
while (1);
}

ps.enableDefault();
}

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

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

void loop()
{
float pressure = ps.readPressureMillibars();
float altitude = ps.pressureToAltitudeMeters(pressure);
float temperature = ps.readTemperatureC();

Serial.print(“p: “);
Serial.print(pressure);
Serial.print(” mbar\ta: “);
Serial.print(altitude);
Serial.print(” m\tt: “);
Serial.print(temperature);
Serial.println(” deg C”);

delay(100);
}