Inconsistent output from multiple bmp280

Hello
I've been working on a project but no success. Any help would be really appreciated.
I am trying to build a device to convert a completely manual laboratory process to an automated one.
8 channels monitoring fermentation in 8 bottles. Each bottle is connected to a glass tube which is slotted. In that slot I placed BMP 280 for each channel connected to a TCA9548A multiplexer to record the pressure. The pressure in first steps are recorded and their average is registered as reference for future. When the pressure reaches a threshold, my Arduino mega2560 sends a signal to the relay for each channel and opens an electric valve and the pressure goes back to normal. The loop repeats.
Everything is alright except almost nothing. Bmp280s report varied numbers which regardless of my altitude from sea level, are irrelevant to each other. For example they report 881.0 and 1071 in the very same loop in a distance less than 30cm on my table!!! I don't really care about their numbers because I only want the difference from the reference pressure which I explained before. But the problem is they are not reliable too. In every 4 or 5 times that I start the device, at least 3 of them won't even connect, if I try to reset them to others also reset. Sometimes they don't even read. I should reset them to work again. Correcting all these flaws took me hours to write the code but still no success.
Do you think I chose the wrong kind of sensors? Do you have any suggestions for me to replace them with something else? Has anyone heard such problems from BMP 280 before? Any solution?



20211119_125856---1

My code comes below. most of its complicated look was due to trying to correct all the malfunctions I found during millions of times tests!!! but still they exist :frowning:

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_BMP280.h>
#include <Adafruit_Sensor.h>
#include <SD.h>
#include <Keypad.h>
#include <LiquidCrystal.h>
#include <Math.h>
// Initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const int rs = 33, en = 35, d4 = 37, d5 = 39, d6 = 41, d7 = 43;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// Set pressure sensors
Adafruit_BMP280 bmp; // use I2C interface
Adafruit_Sensor *bmp_temp = bmp.getTemperatureSensor();
Adafruit_Sensor *bmp_pressure = bmp.getPressureSensor();
// Keypad
const byte ROWS = 4, COLS = 3;
char keys[ROWS][COLS] =
{
  {'1', '2', '3'},
  {'4', '5', '6'},
  {'7', '8', '9'},
  {'*', '0', '#'}
};
const byte colPins[COLS] = {38 , 40, 42}; //column pins
const byte rowPins[ROWS] = {30, 32, 34, 36}; //row pins
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
// We are assuming that relay pins start from 22 and increment by 1
int relaypin = 22;
// Delay in loop
int loopDelay = 500; 
// Sensor data
int CountOfSensors = 1;
float *basePressure;
float *baseTemperature;
float *averageAfterResetPressure;
float *averageAfterResetTemperature;
int *basePressureMeasureCount;
int *countOfDrain;
int *isRelayOpen;
int NormalizationSteps = 10;
#define maxPressureToOpenValve 2.0
#define minPressureToCloseValve 0.5
uint32_t totalDelay = 0;

void setup() {
  Serial.begin(9600);
    while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  // Initialize LCD
  analogWrite(4,100);
  // Set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  // Initialize SD
  Serial.println("Initializing SD card...");
  if (!SD.begin(53)) {
    lcdPrint("SD failed!", "Restart device");
    Serial.println("initialization failed!");
    while (1);
  }
  Serial.println("initialization done.");
  Wire.begin();
  // Get number of sensors from keypad
  CountOfSensors = readKeypad(1, "Sensors:", CountOfSensors);
  basePressure = (float*)malloc(CountOfSensors * sizeof(float));
  baseTemperature = (float*)malloc(CountOfSensors * sizeof(float));
  averageAfterResetPressure = (float*)malloc(CountOfSensors * sizeof(float));
  averageAfterResetTemperature = (float*)malloc(CountOfSensors * sizeof(float));
  basePressureMeasureCount = (int*)malloc(CountOfSensors * sizeof(int));
  isRelayOpen = (int*)malloc(CountOfSensors * sizeof(int));
  countOfDrain = (int*)malloc(CountOfSensors * sizeof(int));
  // Initialize sensors
  for (int i = 0; i < CountOfSensors; i++) {
    TCA9548A(i);
    if (!bmp.begin(0x76)) {
      Serial.print("Could not find a valid BMP280 sensor, check wiring or try a different address! ");
      Serial.println(i);
      lcdPrint("Check Sensor on", String(i));
      while (1) delay(10);
    }
  }
  for (int i = 0; i < CountOfSensors; i++) {
    // Initialize relay module
    pinMode(relaypin + i, OUTPUT);
    // Set valve open for initial state
    isRelayOpen[i] = 1;
    digitalWrite(relaypin + i, LOW);
    // Set drain count to 0
    countOfDrain[i] = 0;
  }
  //reset all sensors
  for (int i = 0; i < CountOfSensors; i++) {
    TCA9548A(i);
    bmp.setSampling(0xB6);
    bmp.reset();
    delay(50);
    bmp.setSampling(0x03);
    bmp.begin();
    delay(50);
  }
  // Initialize base pressure and measure count to 0
   for (int i = 0; i < CountOfSensors; i++) {
    basePressureMeasureCount[i] = 0;
    basePressure[i] = 0;
    baseTemperature[i] = 0;
    averageAfterResetPressure[i] = 0;
    averageAfterResetTemperature[i] = 0;
  }
  // Get normalization steps from keypad
  NormalizationSteps = readKeypad(2, "Steps:", NormalizationSteps);
  // Insert file format
  File myFile = SD.open("Report.txt", FILE_WRITE); 
  myFile.print("TotalDelay,");
  for(int i = 0; i < CountOfSensors; i++)
    myFile.print("IsDataForAverage,ChanelPressure[" + String(i) + "](hPa),ChanelTemperature[" + String(i) + "](K),ValveStatus[" + String(i) + "],");
  myFile.println("");
  myFile.close();
}

void loop() { 
  File myFile = SD.open("Report.txt", FILE_WRITE); //creates a file named Report or opens a previously named file as Report and writes to it
  String fileLine = String(totalDelay) + ",";
  sensors_event_t temp_event, pressure_event;
  bool normalDelay = true;
  for (int i = 0; i < CountOfSensors; i++) {
    TCA9548A(i);
    bmp_temp->getEvent(&temp_event);
    bmp_pressure->getEvent(&pressure_event);
    float temperature = temp_event.temperature + 273.15;
    // Initial callibration
    if (basePressureMeasureCount[i] < NormalizationSteps) {
      isRelayOpen[i]++;
      digitalWrite(relaypin + i, LOW);  
      fileLine += "1," + String(pressure_event.pressure) + "," + String(temperature) + ",1,";
      averageAfterResetPressure[i] = basePressure[i] = (basePressure[i] * basePressureMeasureCount[i] + pressure_event.pressure) / (basePressureMeasureCount[i] + 1);
      averageAfterResetTemperature[i] = baseTemperature[i] = (baseTemperature[i] * basePressureMeasureCount[i] + temperature) / (basePressureMeasureCount[i] + 1);
      basePressureMeasureCount[i]++;
      normalDelay = false;
    }
    // Check if sensor needs reset
    else if (getCalibratedPressure(i) > basePressure[i] + 100 || getCalibratedPressure(i) < basePressure[i] - 100 || 
      ((pressure_event.pressure / temperature) > (1.05 * (basePressure[i] / baseTemperature[i]))) || 
      ((pressure_event.pressure / temperature) < (0.95 * (basePressure[i] / baseTemperature[i]))) ||
      isRelayOpen[i] >= (2 * NormalizationSteps)) {
        Serial.print("reset:");
        Serial.print(String(getCalibratedPressure(i)) + "|");
        Serial.print(String(pressure_event.pressure / temperature) + "|");
        Serial.print(String(basePressure[i] / baseTemperature[i]) + "|");
      bmp.reset();
      delay(100);
      bmp.begin();
      delay(100);
      totalDelay += 200;
      normalDelay = false;
      basePressureMeasureCount[i] = NormalizationSteps + 1;
      isRelayOpen[i] = 0;
    }
    // Reset & re-callibration
    else if (basePressureMeasureCount[i] >= NormalizationSteps + 1 && basePressureMeasureCount[i] < (2 * NormalizationSteps)) {
      Serial.print("re-callibration");
      isRelayOpen[i]++;
      digitalWrite(relaypin + i, LOW);
      fileLine += "1," + String(pressure_event.pressure) + "," + String(temperature) + ",1,";
      Serial.println(fileLine);
      averageAfterResetPressure[i] = (averageAfterResetPressure[i] * (basePressureMeasureCount[i] - NormalizationSteps) + pressure_event.pressure) / ((basePressureMeasureCount[i] - NormalizationSteps) + 1);
      averageAfterResetTemperature[i] = (averageAfterResetTemperature[i] * (basePressureMeasureCount[i] - NormalizationSteps) + temperature) / ((basePressureMeasureCount[i] - NormalizationSteps) + 1);
      basePressureMeasureCount[i]++;
      normalDelay = false;
      if (basePressureMeasureCount[i] >= (2 * NormalizationSteps) - 1)
        basePressureMeasureCount[i] = NormalizationSteps;
    }
    // Read valid data
    else {
      fileLine += String(isRelayOpen[i]) + "," + String(getCalibratedPressure(i)) + "," + String(temperature) + ",";
      if (getCalibratedPressure(i) > (averageAfterResetTemperature[i]* basePressure[i] / baseTemperature[i] + maxPressureToOpenValve) && (isRelayOpen[i] == 0)) {
        isRelayOpen[i]++;
        digitalWrite(relaypin + i, LOW);
        countOfDrain[i]++;
      }
      else if (getCalibratedPressure(i) < (averageAfterResetTemperature[i] * basePressure[i] / baseTemperature[i] + minPressureToCloseValve) && (isRelayOpen[i] > 0)) {
        isRelayOpen[i] = 0;
        digitalWrite(relaypin + i, HIGH);
      }
      if (isRelayOpen[i] > 0) {
        isRelayOpen[i]++;
      }
      //Serial.print(String(averageAfterResetPressure[i] - (averageAfterResetTemperature[i] * basePressure[i] / baseTemperature[i])) +","+String(pressure_event.pressure) + "," + String(temperature));
    }
  }
  if (myFile) {
    Serial.println(fileLine); 
    myFile.println(fileLine);
    myFile.close();
  }
  if (normalDelay) {
    totalDelay += loopDelay;
    delay(loopDelay);
  }
  else {
    totalDelay += 50;
    delay(50);
  }
}
  
int readKeypad(byte maxDigits, String displayText, int total) //added a parameter for max digits to accept
{
  Serial.print("Getting Key.. ");
  int digitCount = total == 0 ? 0 : (log10(total) + 1);
  char key;
  byte keyAsANumber;
  lcdPrint(displayText, String(total));
  while(1)
  {
    key = keypad.getKey();
    if (key == '#') //user signals complete
    {
      digitCount = 0;
      lcdPrint(displayText, String(total));
      return total;
    }
    else if (key == '*' && digitCount > 0)  //user wants to delete a digit
    {
      total = total / 10; //remove the last digit
      digitCount--; //decrement the count of digits entered
      lcdPrint(displayText, String(total));
    }
    if (key >= '0' && key <= '9' && digitCount < maxDigits) //only act on numeric keys
    {
      keyAsANumber = key - 48;
      total = total * 10;
      total = total + keyAsANumber; //add the new number
      digitCount++;
      lcdPrint(displayText, String(total));
    }
  }
}

void lcdPrint(String displayText, String userInput)
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(displayText);
  lcd.setCursor(0, 1);
  lcd.print(userInput);
}

void TCA9548A(uint8_t bus)
{
  Wire.beginTransmission(0x70);  // TCA9548A address is 0x70
  Wire.write(1 << bus);          // send byte to select bus
  Wire.endTransmission();
}

float getCalibratedPressure(int i) {
  TCA9548A(i);
  sensors_event_t pressure_event;
  bmp_pressure->getEvent(&pressure_event);
  return pressure_event.pressure - (averageAfterResetPressure[i] - (averageAfterResetTemperature[i] * basePressure[i] / baseTemperature[i]));
}

There is probably something wrong with your hardware or software. Which, we can't see.

Probably, nobody else is going to jump in, who has also put BMP 280s in 8 flasks. So you're depending on people to perform an analysis. For that you have to provide information.

Are the sensors rated for alcohol vapour?

There's no alcohol vapor. Besides they don't act normal when they are not connected to the system and there's only air!

See also reply #2

(I'm sorry: I saw "fermentation", and immediately thought "alcohol". It's just the way my mind works)

What kind of info would help?

HI Mhm

I recently completed a project which included a single BMP 280 and had some angst caused by the presets not .. .. preset !

bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. /
Adafruit_BMP280::SAMPLING_X2, /
Temp. oversampling /
Adafruit_BMP280::SAMPLING_X16, /
Pressure oversampling /
Adafruit_BMP280::FILTER_X16, /
Filtering. /
Adafruit_BMP280::STANDBY_MS_4000); /
Standby time. */

The data is in the datasheet but not very obvious;
I've no idea if this is relevant or not, but might be worth a check ?

MM

Thank you very much, I tried this settings in different modes, Norma and forced. But they did not help a lot, they even fried one of my sensors (I don't know why!!!).

Hi @mhm-k

Since you're using an Arduino Mega I guess you're powering the TCA9548A with 5V, but are you using 5V to 3.3V I2C level shifters on your BMP280 breakout boards?

Sometimes these boards come with level shifting built-in, sometimes not.

Hi,
We need to see your code?

Do you give each 280 time to respond before switching to the next.
In fact when you switch, do you give enough time for the switch to take place and connection made, then enough time for the reading to be received?

Can you please post a schematic?
You don't have to show all 8 280 connections, just 2 will do.

Can you please post picture(s) of your project so we can see your component layout?

Thanks.. Tom... :smiley: :+1: :coffee: :australia:

I use the 3.3 v from an external power supply with a common ground to Arduino. I didn't use level shifters. Although, I tried using Arduino 5 and 3.3v supply too but no difference. Actually when I used 3.3 volts I encountered limited mAmps for all the sensors and TCA9548A, this made me use an external power supply.

Hi,
Do have pullup resistors on the SDA and SCL lines, I'm not sure but the 9548 doesn't have them.
If you are using a module they may be fitted already.

Have you a 9548 module/breakout board, or just the IC?

Thanks.. Tom... :smiley: :+1: :coffee: :australia:

@mhm-k Might I ask which BMP280 breakout boards you're using?

There shouldn't be an issue powering these boards, even during meaurement each barometer only sips 720uA (typically).

Hi Tom,
I use breakouts both for the BMP280s and TCA9548A,
I updated my post with the photos and my code.

@MartinL
these are a clone exactly equal to the one which Adafruit provides. And also yes I know their current requirements are not too high, but during all my efforts I tried to replace any tiny suspicion I had, and this was one of them. Anyhow, it still does not work!

@mhm-k The Adafruit BMP280 breakout has I2C level shifters included, but does the breakout boards that you're using?

If your boards have I2C level shifting, you'll see either two 3-pin ICs, or a single 6-pin IC on the board, these are the level shifting transistors:

BMP280 Breakout

The BMP280 is very accurate and reliable and it uses almost no current.

You have to know what the hardware and software is doing, that means taking a few steps back. We also need a lot more information.
First of all, forget about your project. You will have to test the sensors one by one.
Can you give a link to where you bought the sensors and the I2C multiplexer ?
Do you have a 3.3V Arduino board ?

The sensors are 3.3V sensors and the Arduino Mega 2560 has a 5V I2C bus with 10k pullups to 5V. That can damage the sensors, don't connect the bare sensors directly to the Mega board.
The I2C multiplexer can do I2C voltage level shifting (power it with 3.3V).
The I2C bus has three wires: SDA, SCL, GND. I can not clearly see where the black GND wire is going.

In the sketch, each sensor needs its own object. I only see one 'bmp' object.

Suppose you finally can get all sensors working, then you can add the valves and maybe you have to start all over again if that disturbs the I2C bus too much.

1 Like

I guess the breakouts I bought does not have the level shifters, but the TCA9548A will do the job.
here is the link to what I bought, but not from this store, I have the exact same boards
https://www.aliexpress.com/item/32773684231.html?spm=a2g0o.search0304.0.0.2ef11cfdKYaiVM&algo_pvid=2375ae52-c389-45f2-a6a7-6112e7e9987f&algo_exp_id=2375ae52-c389-45f2-a6a7-6112e7e9987f-1

Dear Koepel;
Thanks for your response.
I bought sensors from a local store but they are exactly like this:
BMP280
As you mentioned I use Arduino mega2560 but the TCA9548 connected to I2C bus is connected to a 3.3 v supply, so are the sensors. doesn't it do the job?
The GND goes to the power supply and all the GNDs are connected to the Arduino GND. Also Arduino power is supplied by the same source but different voltage regulator (9volts).
I assumed the code would call the bmp for each individual sensor and takes that as a separate object.
I will be happy to give you any other info that could help.

@mhm-k The BMP280 is a 3.3V device that isn't 5V tolerant. If the BMP280 breakouts don't have level shifting then you'll need to use an addtional I2C level shifter, in order to protect each board, such as this 4-channel one from Adafruit: https://www.adafruit.com/product/757, (other level-shifters are available).

Each 4-channel level shifter can be used to level shift two breakout boards, just connect the 5V pin of your Mega to the higher voltage (HV) side and 3.3V to the lower voltage (LV), as well as ground. The HV side can be used to connect the 5V SCL and SDA from the TCA9548A, while on the LV side the 3.3V signals and power go to the sensor breakout(s).