BMP390 I2C temperature readings wildly incorrect

Hi, I am trying to get temperature and pressure data from a BMP390 sensor by talking to it directly over I2C. I am testing with a mega328p.

I am able to get raw temperature and pressure data from the sensor, but I am having trouble converting them to pascals and celcius using the calibration coefficients.

The temp is wildly off, sometimes in the high 90s or in the negatives. The pressure just overflows, but I am focusing on fixing temp first, so I am not worrying about that for now.

Here are some samples of the kind of serial output I am getting:

Triggering manual measurement...                                                                                                                  
Raw Temp: 12591491                                                                                                                                
Computed Temp:                                                                                                                                    
97.64                                                                                                                                             
Raw Pressure: 45928                                                                                                                               
Computed Pressure:                                                                                                                                
2109572736.00                                                                                                                                     
Triggering manual measurement...                                                                                                                  
Raw Temp: 9347                                                                                                                                    
Computed Temp:                                                                                                                                    
-127.49                                                                                                                                           
Raw Pressure: 45928                                                                                                                               
Computed Pressure:                                                                                                                                
2109487872.00                    

I am using the calibration coefficients provided by the sensor and converting them to floats using the formulas in the datasheet.

I also believe the raw temperature values are being read correctly as per the results in the seril output. My maths for converting the temps is based on the example at the end of the datasheet.

Any help would be greatly appreciated.

Here is my sketch:

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

#define BMP390_ADDRESS 0x77

// BMP390 regs
#define REG_RESET 0x7E
#define REG_CONFIG 0x00
#define REG_PWR_CTRL 0x1B
#define REG_OSR 0x1C
#define REG_ODR 0x1D
#define REG_STATUS 0x03
#define REG_PRESSURE 0x04
#define REG_TEMPERATURE 0x07

#define NVM_PAR_T1_0 0x31
#define NVM_PAR_T1_1 0x32
#define NVM_PAR_T2_0 0x33
#define NVM_PAR_T2_1 0x34
#define NVM_PAR_T3 0x35
#define NVM_PAR_P1_0 0x36
#define NVM_PAR_P1_1 0x37
#define NVM_PAR_P2_0 0x38
#define NVM_PAR_P2_1 0x39
#define NVM_PAR_P3 0x3A
#define NVM_PAR_P4 0x3B
#define NVM_PAR_P5_0 0x3C
#define NVM_PAR_P5_1 0x3D
#define NVM_PAR_P6_0 0x3E
#define NVM_PAR_P6_1 0x3F
#define NVM_PAR_P7 0x40
#define NVM_PAR_P8 0x41
#define NVM_PAR_P9_0 0x42
#define NVM_PAR_P9_1 0x43
#define NVM_PAR_P10 0x44
#define NVM_PAR_P11 0x45


void writeRegister(uint8_t reg, uint8_t value) {
  Wire.beginTransmission(BMP390_ADDRESS);
  Wire.write(reg);
  Wire.write(value);
  Wire.endTransmission();
}

uint8_t readRegister(uint8_t reg) {
  Wire.beginTransmission(BMP390_ADDRESS);
  Wire.write(reg);
  Wire.endTransmission();
  Wire.requestFrom(BMP390_ADDRESS, 1);
  return Wire.read();
}

uint32_t read24bit(uint8_t reg) {
  Wire.beginTransmission(BMP390_ADDRESS);
  Wire.write(reg);
  Wire.endTransmission(false);

  Wire.requestFrom(BMP390_ADDRESS, 3);
  while (Wire.available() < 3);

  uint32_t msb = Wire.read();
  uint32_t lsb = Wire.read();
  uint32_t xlsb = Wire.read();
  uint32_t raw_value = (msb << 16) | (lsb << 8) | xlsb;

  return raw_value;
}

struct BMP390_Calibration {
  float par_p11;
  float par_p10;
  float par_p9;
  float par_p8;
  float par_p7;
  float par_p6;
  float par_p5;
  float par_p4;
  float par_p3;
  float par_p2;
  float par_p1;
  float par_t3;
  float par_t2;
  float par_t1;
  float t_lin;
};

void getTempCoefficients(BMP390_Calibration *cal) {

  uint16_t par_t1 = (uint16_t)readRegister(NVM_PAR_T1_0) | ((uint16_t)readRegister(NVM_PAR_T1_1) << 8);
  int16_t par_t2 = (int16_t)readRegister(NVM_PAR_T2_0) | ((int16_t)readRegister(NVM_PAR_T2_1) << 8);
  int8_t par_t3 = (int8_t)readRegister(NVM_PAR_T3);

  int8_t par_p11 = (int8_t)readRegister(NVM_PAR_P11);
  int8_t par_p10 = (int8_t)readRegister(NVM_PAR_P10);
  int16_t par_p9 = (int16_t)readRegister(NVM_PAR_P9_0) | ((int16_t)readRegister(NVM_PAR_P9_1) << 8);
  int8_t par_p8 = (int8_t)readRegister(NVM_PAR_P8);
  int8_t par_p7 = (int8_t)readRegister(NVM_PAR_P7);
  uint16_t par_p6 = (uint16_t)readRegister(NVM_PAR_P6_0) | ((uint16_t)readRegister(NVM_PAR_P6_1) << 8);
  uint16_t par_p5 = (uint16_t)readRegister(NVM_PAR_P5_0) | ((uint16_t)readRegister(NVM_PAR_P5_1) << 8);
  int8_t par_p4 = (int8_t)readRegister(NVM_PAR_P4);
  int8_t par_p3 = (int8_t)readRegister(NVM_PAR_P3);
  int16_t par_p2 = (int16_t)readRegister(NVM_PAR_P2_0) | ((int16_t)readRegister(NVM_PAR_P2_1) << 8);
  int16_t par_p1 = (int16_t)readRegister(NVM_PAR_P1_0) | ((int16_t)readRegister(NVM_PAR_P1_1) << 8);

  cal->par_t1 = par_t1 / 0.00390625f; // precomputed 2^(-8) = 0.00390625
  cal->par_t2 = par_t2 / 1073741824.0f; // precomputed 2^30 = 1073741824
  cal->par_t3 = par_t3 / 281474976710656.0f; // precomputed 2^48 = 281474976710656

  Serial.println("Calibration Coefficients:");
  Serial.print("par_t1: "); Serial.println(cal->par_t1, 6);
  Serial.print("par_t2: "); Serial.println(cal->par_t2, 6);
  Serial.print("par_t3: "); Serial.println(cal->par_t3, 6);

  cal->par_p11 = par_p11 / 36893488147419103232.0f; // precomputed 2^65 = 36893488147419103232.0
  cal->par_p10 = par_p10 / 281474976710656.0f; // precomputed 2^48 = 281474976710656.0
  cal->par_p9 = par_p9 / 281474976710656.0f; // precomputed 2^48 = 281474976710656.0
  cal->par_p8 = par_p8 / 32768.0f; // precomputed 2^15 = 32768
  cal->par_p7 = par_p7 / 256.0f; // precomputed 2^8 = 256
  cal->par_p6 = par_p6 / 64.0f; // precomputed 2^6 = 64
  cal->par_p5 = par_p5 / 0.125f; // precomputed 2^(-3) = 0.125
  cal->par_p4 = par_p4 / 137438953472.0f; // precomputed 2^37 = 137438953472
  cal->par_p3 = par_p3 / 4294967296.0f; // precomputed 2^32 = 4294967296
  cal->par_p2 = (par_p2 - 16384) / 536870912.0f; // precomputed 2^14 = 16384, 2^29 = 536870912
  cal->par_p1 = (par_p1 - 16384) / 1048576.0f; // precomputed 2^14 = 16384, 2^20 = 1048576

}

float compensateTemp(uint32_t uncompTemp, struct BMP390_Calibration *cal) {
  float partialData1;
  float partialData2;

  partialData1 = (float)(uncompTemp - cal->par_t1);
  partialData2 = (float)(partialData1 * cal->par_t2);

  cal->t_lin = partialData2 + (partialData1 * partialData1) * cal->par_t3;

  return cal->t_lin;

}

float compensatePressure(uint32_t uncompPressure, struct BMP390_Calibration *cal) {
  float compensatedPressure;

  float partialData1;
  float partialData2;
  float partialData3;
  float partialData4;
  float partialOut1;
  float partialOut2;
  
  partialData1 = cal->par_p6 * cal->t_lin;
  partialData2 = cal->par_p7 * (cal->t_lin * cal->t_lin);
  partialData3 = cal->par_p8 * (cal->t_lin * cal->t_lin * cal->t_lin);
  partialOut1 = cal->par_p5 + partialData1 + partialData2 + partialData3;

  partialData1 = cal->par_p2 * cal->t_lin;
  partialData2 = cal->par_p3 * (cal->t_lin * cal->t_lin);
  partialData3 = cal->par_p4 * (cal->t_lin * cal->t_lin * cal->t_lin);

  partialOut2 = (float)uncompPressure *
    (cal->par_p1 + partialData1 + partialData2 + partialData3);

  partialData1 = (float)uncompPressure * (float)uncompPressure;
  partialData2 = cal->par_p9 + cal->par_p10 * cal->t_lin;
  partialData3 = partialData1 + partialData2;
  partialData4 = partialData3 + ((float)uncompPressure * (float)uncompPressure * (float)uncompPressure) * cal->par_p11;
  compensatedPressure = partialOut1 + partialOut2 + partialData4;

  return compensatedPressure;

}

BMP390_Calibration coefficients;

void setup() {

  Wire.begin();
  Serial.begin(9600);

  Serial.println("Initializing BMP390...");

  // Perform soft reset
  writeRegister(REG_RESET, 0xB6);
  delay(10);

  getTempCoefficients(&coefficients);

  // Disable IIR filter
  writeRegister(REG_CONFIG, 0b00000000);

  // Set oversampling
  writeRegister(REG_OSR, 0b00010011);
  
  // Set output data rate
  writeRegister(REG_ODR, 0x02); // 50Hz

  Serial.println("BMP390 Initialized.");
}

void loop() {
  Serial.println("Triggering manual measurement...");

  writeRegister(REG_PWR_CTRL, 0b00100011);
  delay(100);

  uint32_t rawTemp, rawPressure;
  rawTemp = read24bit(REG_TEMPERATURE);
  rawPressure = read24bit(REG_PRESSURE);

  Serial.print("Raw Temp: ");
  Serial.println(rawTemp);
  Serial.println("Computed Temp: ");
  Serial.println(compensateTemp(rawTemp, &coefficients));

  Serial.print("Raw Pressure: ");
  Serial.println(rawPressure);
  Serial.println("Computed Pressure: ");
  Serial.println(compensatePressure(rawPressure, &coefficients));
    
  delay(1000);
}

Did you try this library?

Yes, I have tried that library. Both the pressure and temp work so the sensor is not defective. However, I am not using it because I want to learn how to work with I2C devices without a library and also in hope of reducing the program size.

Wow, that's a lot of code. Anytime I needed to read a temp sensor it was one line of code.

Excellent.

Maybe you can read some of the code in that library and it will help you figure out what you got wrong. I guess it must be evaluating the same formulae from the data sheet that your code does. Perhaps you could add some extra Serial.print()s to the library code to see if the input or output values are different.

According to the datasheet, the 24 bit raw values are stored in the registers with low-order byte first. You are treating them as if they are high-order byte first.
Try changing the code in the read24bit function to this:

  uint32_t xlsb = Wire.read();
  uint32_t lsb = Wire.read();
  uint32_t msb = Wire.read();

If that makes it worse, print out the raw values of all of par_t1, par_t2, par_t3 and par_p1 to par_p11.

Pete

1 Like

Thank you! That was indeed the problem. I changed my code to read the lowest byte first and now I am getting accurate temperature.

I solved the pressure overflow problem by using doubles instead of floats, despite the datasheet example using floats. I suppose I could dig deeper to find out where the overflow is in order to keep using floats where possible and shave off some ram usage. But I don't think it's worth it.

I'm glad that fixed the problem.

Pete

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