I've been using two BME280 sensors to measure air and water character for several years. Worked great with electrons and enables me to monitor water temperature, water depth and temperature-correct water Dissolved Solids measurements. I power the 2 BMEs with digital pins so they don't use power while I have them sleep for an hour.
Setting one BME at 0x76 and one at 0x77, I use the CE_BME280 library and define two devices with CE_BME280 bme1; and CE_BME280 bme; I then start the sensors with bme1.begin(0x76) and bme2.begin(0x77) and read the sensors with, for example: t1 = bme1.readTemperature();. As I said, the code all worked fine for years on the Electron. But now on the Boron the 1st sensor to be read gives bad values. It doesn't matter if I read 0x76 or 0x77 first or swap the sensors around.
I've tried:
increasing the drive strength with pinSetDriveStrength(bme1PowerPin,DriveStrength::HIGH);
adding a bunch of delays of 500ms with a function based on Particle.process();
tried different BME280 hardware
swapping the order in which the sensors are read, i.e. 0x76 first or 0x77 first.
turning on and off each sensor to be read before reading the 2nd sensor
reading the 1st sensor a second time
What is particularly curious, and maybe telling, is that they both work fine if I am hooked to the Boron by usb and getting data to the terminal with Serial.println statements. So I'm thinking it might be some sort of timing error that the Serial.println's solves by slowing things down or maybe a buffer thing. A bit beyond my expertise. Any ideas appreciated.
john
It's usually either a hardware issue, typically caused by I2C pull-up conflicts, or a software issue. However, since it works with USB connected, it's probably not a hardware issue.
The CE_BME280 library doesn't have any logging in it, so it would be helpful if you can share your source code.
The boards already have 10K pullups and I tried adding 10K pull-ups to both BME280 and still no luck.
Before I post a bunch of ugly code, let me ask, is CE_BME280 the best library to use? Or is there a more dependable / predictable library for BMR280s?
thanks,
john
I use Adafruit_BME280_RK but just glancing at the other library it seems reasonable. However, switching libraries is probably a good debugging technique since you don't have a lot invested in using the other library yet.
You shouldn't need any additional pull-ups.
What is your power-on sequence? I'm pretty sure you're going to need to turn on both sensors at the same time, or ideally power them from the same pin assuming there's sufficient drive.
The reason I'm worried about this is if your breakout is unpowered (because the power pin is off), but I2C is being driven, it may put the BME280 into a bad state because it will be partially powered by leaking through the I2C interface. This may cause it to enter a bad state where it does not correctly respond to I2C commands.
Related to this, you may also need to Wire.end() before powering off the sensors, and repeat the begin sequence after power on, and a delay.
Thanks for the ideas. Just what I was looking for.
Re: timing, I increased my delay between powering-reading the two BME280s from 5 seconds to 10 seconds and that did not help.
I'll try the ideas you suggest and report back.
In the mean time here is a code snippet where I power&read the first bme and then delay before powering & reading the second.
john
before void setup() I do:
CE_BME280 bme1; // I2C for air temp. pressure, humidity, set address= 0x76
// (if HiLetGo bme defaults to 0x77, for 0x76 SD0 held high by wire to 3.3 V. see HiLetGo_BME280.txt)
CE_BME280 bme2; // I2C for WATER temp. & pressure, set address=0x77 //Adafruit BME defaults to 0x77
in void loop() I do
// ---- get WATER temperature and pressure // from BME280 using I2C connection
pinMode(H2oPwr, OUTPUT); // power for the bme2 digital sensor BME280
pinSetDriveStrength(H2oPwr, DriveStrength::HIGH); // ?needed to raise the current from 2mA to 9mA
digitalWrite(H2oPwr, HIGH);
waitMS(1000);
if (!bme2.begin(0x77)) // the WATER sensor BME280 for temp, humidity, pressure.
// on HiLeetGo BME280 if SD0 NOT held high by wire to 3.3 V. then 0x76, see HiLetGo_BME280.txt always check which bme has SD0 held high
// Adafruit BME280 defaults to 0x77
{
if(usbOn) {Serial.println("Could not find bme2 sensor, 1st check wiring!"); waitMS(200);}
}
waitMS(200);
// ---- get WATER temperature and pressure // from the BME280 using I2C connection.
// being used underwater (enclosed in mineral oil) for depth sensor
{
int i = 0;
while(i<1)
{
t2 = bme2.readTemperature();
p2 = bme2.readPressure()/100.0;
waitMS(50);
i++;
}
// Check if any reads failed but don't hold things up
if (isnan(p2) || p2<1.0 ) // isnan requires #include <SPI.h>
///if (p2<1.0 )
{ t2 = -99.1; p2 = -99.1; } // -99.1 is flag for bme read error
digitalWrite(H2oPwr, LOW); ///turn off digital sensor
}
waitSec(10) ;
// ---- get AIR temperature and humidity and pressure // from BME280 using I2C connection
same code as above but using bme1
.
.
.
the 2 BME280s work fine under a variety of configurations if I wake the Boron with the reset button.
But if, when it wakes from System.sleep(config); and is reset with System.reset(RESET_NO_WAIT); in code, the following 1st BME280 read gets bad values.
This suggests that System.reset(RESET_NO_WAIT); is not just like pushing the reset button, contrary to the documentation.
Any thought on how they might be different.
Solution Found for running 2 (or possibly more) BME280s on the Boron:
On the Boron I had to insert a Wire.end(); either in void setup() or after the two BME280s are read. This is because I use System.reset(RESET_NO_WAIT); after a sleep with ULTRA_LOW_POWER. That reset does not seem to release the i2c line correctly.
By having a Wire.end(); in setup the SCL and SDA lines are cleared of any residual settings or data from previous runs. In the Particle reference documentation it is described as: .end() "Releases the I2C bus so that the pins used by the I2C bus are available for general purpose I/O."
My code now looks like:
//before void setup() I do:
#include <CE_BME280.h>
CE_BME280 bme1; // I2C for air temp. pressure, humidity, set address= 0x76
CE_BME280 bme2; // I2C for WATER temp. & pressure, set address=0x77
void setup() // along with a bunch or code unrelated to the BMEs
Wire.end();
void loop() // along with a bunch of code unrelated to the BMEs
// ---- get WATER temperature and pressure // from BME280 using I2C connection
//create power on digital pin for bme2
pinMode(H2oPwr, OUTPUT); // power for the bme2 digital sensor BME280
digitalWrite(H2oPwr, HIGH);
waitMS(1000);
if (!bme2.begin(0x77)) // the WATER sensor BME280 for temp, humidity, pressure.
{
if(usbOn) {Serial.println("Could not find bme2 sensor, check wiring!"); waitMS(200);}
}
waitMS(200);
// ---- get the data from bme2
{
int i = 0;
while(i<1)
{
t2 = bme2.readTemperature();
p2 = bme2.readPressure()/100.0;
waitMS(50);
i++;
}
// Check if any reads failed but don't hold things up
if (isnan(p2) || p2<1.0 ) // isnan requires #include <SPI.h>
{ t2 = -99.1; p2 = -99.1; } // -99.1 is flag for bme read error
digitalWrite(H2oPwr, LOW); ///turn off digital power to sensor
}
waitSec(1) ;
// ---- get AIR temperature and humidity and pressure // from BME280 using I2C connection
//same code to set up power & bme and read data as above but using bme1
.
//when completed reading all sensors and uploading to Particle go to sleep for a time specified in seconds.
SystemSleepConfiguration config;
config.mode(SystemSleepMode::ULTRA_LOW_POWER) .duration((seconds) );
System.sleep(config);
//when the Boron wakes up after "seconds" I do a reset to re-run my code from the beginning
System.reset(RESET_NO_WAIT);
This code differs from what I wrote for the Electron in that with the Electron I did not need to do a Wire.end(); Apparently this is because on the Electron, sleeping-waking with the command: System.sleep(SLEEP_MODE_DEEP, sleepInterval); appears to clean up the i2c lines.
I did learn that the i2c circuit on the Boron is pretty robust to other things I tested:
it does not need pinSetDriveStrength(bme-power-pin, DriveStrength::HIGH);
power to the BME280s and bme.begin(0x76) can be in either void setup() or void loop();
the BME280s can be run off of the 3v3 pin or from digital pins held high
need about 1000 ms delay after turning on power to the BME by the digital pin, if bme.begin() can't find the bme, first try increasing the delay after powering on the bme
the two BME280 can be powered at the same time or in sequence
the delays between reading the BME280s need not be large. I currently have at 1 sec but likely could be reduced to 100 or 200 ms.
a Wire.end(); after each BME280 read is OK but doesn't seem necessary if there is one in void setup()
most BME280 boards have adequate pullups. I used HiLetGo and Adafruit which both have 10K resistors on their i2c lines
sequential powering and reading of multiple BME280 may open up the possibility of having a nearly unlimited number of BME280 or probably other i2c sensors in a project.