Dead spot in BMP280 Temperature reading

I am just interested in if anyone else has seen a discontinuity in the BMP280 sensor's temperature output in the 7C to 8C range?
I've seen this in multiple sensors, attached to both ESP8266s and MEGA2560s, over the last year. The sensors have been co-located with a AHT10 module with the data saved every 10 minutes. Where the AHT10's data is smooth thru that temperature range, the BMP's has a glitch, regardless if the temperature is rising or falling. I have run dummy bit values thru both compensation methods documented in the Bosch datasheet and used in my codes. Both methods output smooth curves with the pseudo data. I've uploaded a plot comparing the AHT vs BMP data plotted against each other and a plot of the delta in temperatures vs the AHT temperature over a recent 5 day period. The anomaly is clearly seen.

Thanks
KS


Just to rule that out: what library are you using?
Given AHT10 returns correct temperature there seems to be a limitation at some value until the temperature jumps over a higher value. This might be a software error.

Thanks for the reply,
I've written my own functions for both the AHT10 and BMx280. After 5 or 6 iterations the latest you can see below. I've also upload the data from March.
March Data.txt (277.5 KB)

The AHT10 is using the generic scaling values and I know from past work with calibrated systems, those scalers are a bit off, but are easily modified in the code. I live about 2400 meters from the official weather station for the local airport and both sensor track with the NWS data. Outside of this bad range in the BMx280s sensors the air pressure values, corrected for local elevation is allows only 0.01 or 0.02 inHg different than the official readings. I've seen this dead stop in both BMP's and BME's type sensors. I you want take a look at the BMx280 code and let me know is you see anything obvious.

/*
K Shoop March 2022
Basic functions to use a I2C interface with either a BMP280 or BME280 T/P/RH sensor
Provision to accommodate a I2C multiple chip with address 0x77 are included in the code
Provisions for using a separate temperature compensation value (t_fine) based in the 2nd sensor in incorporated into read_all()
To use with BMP type sesnor, comment out the humitiy related code in int_BMP() and read_All() functions
  and reduce the requested number of byte to 6
Usage: Calling main function should define a doubles point to receive the read_All() returned values
    ie: double *readings = read_All(TCV); to use BMx t_fine = -205000 (-204825 is t_fine for -40 C)
    to use a temperture (in C) from 2nd sensor, t_fine = ((((int32_t)Temperature * 100)<<8)-128)/5
*/


#include <Arduino.h>
#include <Wire.h>
#include "bme280.h"

typedef uint32_t BME280_U32_t; //Type defined in the Bosch datasheet
typedef int32_t BME280_S32_t; //Type defined in the Bosch datasheet
typedef int64_t BME280_S64_t;  //Type defined in the Bosch datasheet

BME280_S32_t t_fine; //Temperature correction factor for pressure calculations

#define addr 0x76 //BMx280 I2C Address
//#define plexer

uint16_t dig_T1;  //Temperature compensation
int16_t dig_T2;
int16_t dig_T3;
uint16_t dig_P1;  //Pressure compensation
int16_t dig_P2;
int16_t dig_P3;
int16_t dig_P4;
int16_t dig_P5;
int16_t dig_P6;
int16_t dig_P7;
int16_t dig_P8;
int16_t dig_P9;
uint8_t dig_H1; //Humidity compensation - Comment out H1 thru H6 for BMP type sensor
int16_t dig_H2;
uint8_t dig_H3;
int16_t dig_H4;
int16_t dig_H5;
int8_t dig_H6;

uint8_t buffer[8]; //Array for the 8 byte reading values

//Unsigned short read - use this to read ALL compensation value
uint16_t readCompsU(int Addr, int reg) {
  Wire.beginTransmission(Addr);
  Wire.write(reg);
  if (Wire.endTransmission(true) != 0) {
    Serial.println("Connection Failed");
  }
  Wire.requestFrom(addr, 1);
  uint16_t temp1 = Wire.read();
  return temp1;
}

//Signed short read
int16_t readComps(int Addr, int reg) {
  Wire.beginTransmission(Addr);
  Wire.write(reg);
  if (Wire.endTransmission(true) != 0) {
    Serial.println("Connection Failed");
  }
  Wire.requestFrom(addr, 1);
  int16_t temp1 = Wire.read();
  return temp1;
}

//Unsigned char read 
uint8_t readCompUChar(int Addr, int reg) {
  Wire.beginTransmission(Addr);
  Wire.write(reg);
  if (Wire.endTransmission(true) != 0) {
    Serial.println("Connection Failed");
  }
  Wire.requestFrom(addr, 1);
  uint8_t temp1 = Wire.read();
  return temp1;
}

//Configure the sensor and read in compensation values, Returns TRUE on completion of all steps
bool init_BME() {
//If using a I2C multiplex chip to issolate the sensors   
#ifdef plexer
  Wire.beginTransmission(0x77);  //Make the i2c connection to 0x76
  Wire.write(0x80);              //point to config register
  // Wire.write(0x01); //Set osrs_h = 1 (0000)
  if (Wire.endTransmission(true) != 0) {
    return false;  //Hard end connection
  }
#endif
  //Configure the humidity function
  Wire.beginTransmission(addr);  //Make the i2c connection to 0x76
  Wire.write(0xF2);              //point to H config register
  Wire.write(0x01);              //Set osrs_h = 1 (0001)
  if (Wire.endTransmission(true) != 0) {//Hard end connection
    return false;  
  }
  //Configure the temperature & pressure functions and set sampling mode type
  Wire.beginTransmission(addr);  //Make the i2c connection to 0x76
  Wire.write(0xF4);              //point to ctrl_meas register
  Wire.write(0x26);              //osrs_t = 1x; osrs_p = 1x; mode = forced (00100110)
  if (Wire.endTransmission(true) != 0) {//Hard end connection
    return false;  
  }
  //Configure the standby time, filter and spi function
  Wire.beginTransmission(addr);  //Make the i2c connection to 0x76
  Wire.write(0xF5);              //point to config register
  Wire.write(0x60);              //t_sb = 0.25 sec; filter = OFF; spi = OFF (01100000)
  if (Wire.endTransmission(true) != 0) {//Hard end connection
    return false;  
  }

  //Get the compensation factors - can't use a burts read, chip locks up after first read
  //Need to read values one at a time
  dig_T1 = (readCompsU(addr, 0X89) << 8) | (readCompsU(addr, 0X88));              // Returns 27554  (datasheet example has 27504)
  dig_T2 = (readCompsU(addr, 0X8B) << 8) | (readCompsU(addr, 0X8A));              // Returns 25544  (datasheet example has 26435)
  dig_T3 = (readCompsU(addr, 0X8D) << 8) | (readCompsU(addr, 0X8C));              // Returns 50     (datasheet example has -1000)
  dig_P1 = (readCompsU(addr, 0X8F) << 8) | (readCompsU(addr, 0X8E));              // Returns 36896  (datasheet example has 36477)
  dig_P2 = (readCompsU(addr, 0X91) << 8) | (readCompsU(addr, 0X90));              // Returns -10678 (datasheet example has -10685)
  dig_P3 = (readCompsU(addr, 0X93) << 8) | (readCompsU(addr, 0X92));              // Returns 3024   (datasheet example has 3024)
  dig_P4 = (readCompsU(addr, 0X95) << 8) | (readCompsU(addr, 0X94));              // Returns 5200   (datasheet example has 2855)
  dig_P5 = (readCompsU(addr, 0X97) << 8) | (readCompsU(addr, 0X96));              // Returns 65     (datasheet example has 140)
  dig_P6 = (readCompsU(addr, 0X99) << 8) | (readCompsU(addr, 0X98));              // Returns -7     (datasheet example has -7)
  dig_P7 = (readCompsU(addr, 0X9B) << 8) | (readCompsU(addr, 0X9A));              // Returns 15500  (datasheet example has 15500)
  dig_P8 = (readCompsU(addr, 0X9D) << 8) | (readCompsU(addr, 0X9C));              // Returns -14600 (datasheet example has -14600)
  dig_P9 = (readCompsU(addr, 0X9F) << 8) | (readCompsU(addr, 0X9E));              // Returns 6000   (datasheet example has 6000)
  dig_H1 = readCompUChar(addr, 0XA1);                                             // Returns 5200   (datasheet example has 2855)
  dig_H2 = (readComps(addr, 0XE2) << 8) | (readComps(addr, 0XE1));                // Returns 65     (datasheet example has 140)
  dig_H3 = readCompUChar(addr, 0XE3);                                             // Returns -7     (datasheet example has -7)
  dig_H4 = (readComps(addr, 0XE4) << 4) | (readComps(addr, 0XE5) & 0x0F);         // Returns 15500  (datasheet example has 15500)
  dig_H5 = ((readComps(addr, 0XE6) << 8) | (readComps(addr, 0XE5) & 0xF0) >> 4);  // Returns -14600 (datasheet example has -14600)
  dig_H6 = readCompUChar(addr, 0XE7);                                             // Returns 6000   (datasheet example has 6000)
  return true;
}

/* Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC.
 t_fine carries fine temperature as global value.
 Page 25 - BST-BME280-DS001-23 Revision_1.23_012022 */
BME280_S32_t BME280_compensate_T_int32(BME280_S32_t adc_T) {
  BME280_S32_t var1, var2, T;
  var1 = ((((adc_T >> 3) - ((BME280_S32_t)dig_T1 << 1))) * ((BME280_S32_t)dig_T2)) >> 11;
  var2 = (((((adc_T >> 4) - ((BME280_S32_t)dig_T1)) * ((adc_T >> 4) - ((BME280_S32_t)dig_T1))) >> 12) * ((BME280_S32_t)dig_T3)) >> 14;
  t_fine = var1 + var2;
  T = (t_fine * 5 + 128) >> 8;
  return T;
}

/* "Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits).
 Output value of “24674867” represents 24674867/256 = 96386.2 Pa = 963.862 hPa"
 Page 25 - BST-BME280-DS001-23 Revision_1.23_012022 */
BME280_U32_t BME280_compensate_P_int64(BME280_S32_t adc_P) {
  BME280_S64_t var1, var2, p;
  var1 = ((BME280_S64_t)t_fine) - 128000;
  var2 = var1 * var1 * (BME280_S64_t)dig_P6;
  var2 = var2 + ((var1 * (BME280_S64_t)dig_P5) << 17);
  var2 = var2 + (((BME280_S64_t)dig_P4) << 35);
  var1 = ((var1 * var1 * (BME280_S64_t)dig_P3) >> 8) + ((var1 * (BME280_S64_t)dig_P2) << 12);
  var1 = (((((BME280_S64_t)1) << 47) + var1)) * ((BME280_S64_t)dig_P1) >> 33;
  // avoid exception caused by division by zero
  if (var1 == 0) {
    return 0;
  }
  p = 1048576 - adc_P;
  p = (((p << 31) - var2) * 3125) / var1;
  var1 = (((BME280_S64_t)dig_P9) * (p >> 13) * (p >> 13)) >> 25;
  var2 = (((BME280_S64_t)dig_P8) * p) >> 19;
  p = ((p + var1 + var2) >> 8) + (((BME280_S64_t)dig_P7) << 4);
  return (BME280_U32_t)p;
}

/* Returns humidity in %RH as unsigned 32 bit integer in Q22.10 format (22 integer and 10 fractional bits).
 Output value of “47445” represents 47445/1024 = 46.333 %RH
 Page 25,26 - BST-BME280-DS001-23 Revision_1.23_012022 */
BME280_U32_t BME280_compensate_H_int32(BME280_S32_t adc_H) {
  BME280_S32_t v_x1_u32r;
  v_x1_u32r = (t_fine - ((BME280_S32_t)76800));
  v_x1_u32r = (((((adc_H << 14) - (((BME280_S32_t)dig_H4) << 20) - (((BME280_S32_t)dig_H5) * v_x1_u32r)) + ((BME280_S32_t)16384)) >> 15) * (((((((v_x1_u32r * ((BME280_S32_t)dig_H6)) >> 10) * (((v_x1_u32r * ((BME280_S32_t)dig_H3)) >> 11) + ((BME280_S32_t)32768))) >> 10) + ((BME280_S32_t)2097152)) * ((BME280_S32_t)dig_H2) + 8192) >> 14));
  v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((BME280_S32_t)dig_H1)) >> 4));
  v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r);
  v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);
  return (BME280_U32_t)(v_x1_u32r >> 12);
}

//Check the data read/ready status - not functioning and not used
bool checkStatus() {
  uint32_t startTime = millis();
  if (millis() - startTime < 2000) {

#ifdef plexer
  Wire.beginTransmission(0x77);  //Make the i2c connection to mplex chip 0x77
  Wire.write(0x80);              //point to config register
  if (Wire.endTransmission(true) != 0) {
    return false;  //Hard end connection
  }
#endif

    Wire.beginTransmission(addr);
    Wire.write(0xF3);
    if (Wire.endTransmission(true) != 0) {
      return false;
    }

    delay(10);
    Wire.requestFrom(addr, 1);
    if (Wire.read() == 0) {
      return true;
    }
  }
  return false;
}

//Read sensors and convert to engineering units - returns pointer with 3 double values
double *read_All(int32_t T_FINE) {
  static double answers[3] = { 0, 0, 0 }; //Init array to be returned - calling sketch can determine data validity by the pressure and humidity values
  if (init_BME()) { //Set up sensor - this is here to allow hot swapping the BMx280 module
    //Read Sensor
    Wire.beginTransmission(addr);
    Wire.write(0xF7); //point to first data registor
    if (Wire.endTransmission(true) != 0) {
      Serial.println("Data Read Failed");
      return answers;
    }
    delay(50); //wait for the forced read to occor and the data to be placed in buffer
    
    Wire.requestFrom(addr, 8); //Request the 8 data byte readings - Change 8 to 6 for BMP type sensor
    for (int i = 0; i < 8; i++) { //Change 8 to 6 for BMP type sensor
      buffer[i] = Wire.read();
    }

    //Process the temperature reading into 32-bit signed intiger
    int32_t temp = ((((int32_t)buffer[3]) << 12) | (((int32_t)buffer[4]) << 4) | (((int32_t)buffer[5])) >> 4); //build temperature raw value
    int32_t T = BME280_compensate_T_int32(temp); //Call the temperature porcessing

    if (T_FINE > -204999) { //If incoming parameter is greater the the lowest possible t_fine for -40 C  
      t_fine = T_FINE;  //Set t_fine = incoming parameter
    }
    
    //Process the pressure reading into 64-bit signed intiger
    temp = ((((int32_t)buffer[0]) << 12) | (((int32_t)buffer[1]) << 4) | (((int32_t)buffer[2])) >> 4);
    int64_t P = BME280_compensate_P_int64(temp);
    
    //Process the humidity into 32-bit signed intiger - Comment out the next 2 lines for BMP type sensor
    temp = ((int32_t)buffer[6]) << 8 | ((int32_t)buffer[7]);
    int32_t H = BME280_compensate_H_int32(temp);
    
    //Convert the temperature bytes to engineering unit, degrees C
    answers[0] = (double)T / 100.0;
    //Convert the pressure bytes to engineering unit, in-Hg C, corrected for 287 meters elevation
    answers[1] = ((double)P / 256.0 / 6894.76 + 0.5108) * 2.03602;
    //Convert the humidity bytes to engineering unit, % - Comment out for BMP type sensor
    answers[2] = (double)H / 1024.0;
  }
  return answers; //return the 3-doubles array
}

Dangerous construct. Returning a pointer to a staticly allocated array shouldn't be used in modern code.

Either provide the pointer to the function or get the values by reference (or even return an object).

The argument should be false (repeated start condition).

Remove that line. There is no need to wait here, you just told the device which address you want to read, so read it. The data is already in the register.

I cannot see what this might be useful for.

There are libraries for the Bosch sensors available in the library manager of the IDE. Why do you invent the wheel again, even with errors not found in the other libraries?
I don't want to say that these libraries don't contain errors but they are tested by far more people so the probability for an error is much smaller than if you do them yourself.

It is not a bug, but you have three functions that do the same.
The "readCompsU()", "readComps()" and "readCompUChar()" all read a single byte.

I get a déjà vu feeling. I have seen this before. The Bosch sensors need complex calculations, and sometimes the pressure or temperature is wrong or when the library is used on a ESP32, then suddenly it has a glitch. I agree with pylon, try for example a Adafruit library.

On the other hand, you have tested it thoroughly. Could it be a counterfeit sensor ? Where did you buy it ?

Thanks for the input. I've just loaded a version of the code that should record the raw byte values and the calculated values to one of my database tables. This week's weather forecast predicts temperature swings thru the troubled range.

Koepel - I know the functions look redundant, but I played around quite a bit with returning the correct compensation values last year and found one thing is not always like the other. I started with the BMP type sensor, so I may have a single function that will return the T and P values somewhere in the archives, I'll have to look.

pylon - I'll try your recommendations. The main reason I like to write my own code is it helps me understand what is going on. I also find most of the available libraries, particularly AdaFruit, basically contain too much code and I only need or want a small part of it. My implementation may not be clean and modern, but it's usually solid and functional. I have imbedded systems and SCADA out in the real world that have been running for decades.

Input is always appreciated, I'll let you know if I see anything in the raw data next week.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.