Barometric Presure sensor BMP180

This is the code which I was using

 B6 = latest_B5 - 4000;
  X1 = ((int32_t)b2 * ( (B6 * B6)>>12 )) >> 11;
  X2 = ((int32_t)ac2 * B6) >> 11;
  X3 = X1 + X2;
  B3 = ((((int32_t)ac1*4 + X3) << oversampling) + 2) / 4;

  X1 = ((int32_t)ac3 * B6) >> 13;
  X2 = ((int32_t)b1 * ((B6 * B6) >> 12)) >> 16;
  X3 = ((X1 + X2) + 2) >> 2;
  B4 = ((uint32_t)ac4 * (uint32_t)(X3 + 32768)) >> 15;
  B7 = ((uint32_t)latest_raw_pressure - B3) * (uint32_t)( 50000UL >> oversampling );

Oh, and they all read MB = -32768

If you look at the code, MB is not even actually used !

**
Karma: 58
Posts: 2794
View Profile
Email
Personal Message (Offline)

Re: Barometric Presure sensor BMP180
« Reply #31 on: Today at 01:29:29 am »
Bigger Bigger Smaller Smaller Reset Reset Reply with quoteQuote Modify messageModify Remove messageRemove
Looking at the code I have here, I seem to have two different versions of the temperature calculation, which are different. I had to "fix" it, for some reason, which I don't fully recall now.

I am not 100% certain, but it appears that the first calculation is the wrong one, and the second one is correct.

Code:

X1 = ((latest_raw_temp - (int32_t)ac6) * (int32_t)ac5) >> 15;
X2 = ((int32_t)mc << 11) - (X1 + md)/2; // round up
X2 /= (X1 + md);
B5 = X1 + X2;

Code:

int32_t X1 = ((latest_raw_temp - (int32_t)ac6) * (int32_t)ac5) >> 15;
int32_t X2 = ((int32_t)mc << 11) / (X1 + (int32_t)md);
int32_t B5 = X1 + X2;
float temp = (float)((B5 + 8) >> 4);
temp /= 10; // divide by 10

This algorithm may have been intended for a device with a 32 bit int type.

Two further comments on this.

The mc<<11 will also potentially overflow if it is done as a 16 bit calculation. It is important to have the cast in this line to ensure this happens.

The apparent big difference between the two calculations of X2 is not what it seems. The result will actually the almost the same. By subtracting half of the amount you are going to divide by, before doing the division, this has the effect of correcting the rounding of the least significant bit in the result of the division.

michinyon:
The sensors which have a big value for AC1, don't work, because AC1 x 4 will overflow in the calculation of B3, because it is performing a 16 bit calculation there, unless you tell it otherwise.

But if the sensor happens to have small value of AC1, you don't get this problem, because it still fits in 16 bits.

You will observe that in Bosch's calculation, AC1 is 408 and Leo's example ha 408 also, but Jacob and Erhja have 8000 and 9000 there, which will overflow when multiplied by 4.

Thanks for looking into that - I had a suspicion that the 'default' calculation depth is 16 bit, but did not know where to find out. Hard to work out, when it is a 8 bit platform, but may or may not be signed etc. Hence the stressing of needing to cast stuff - if even I am using the right terminology - I am new to "C/C++" (I come from other language backgrounds - ones very strict on fiddling like this - both a pain and godsend).

I will merge yours with what I have (basically Leo's) to ensure all of possibilities of overflow are caught, in the case of these ebay wonders :slight_smile:

heers

michinyon:

Oh, and they all read MB = -32768

If you look at the code, MB is not even actually used !

Well, we can save a one wire read and 2 bytes of ram potentially, although it may still be needed to initialise the device correctly, used externally or not...

Cheers
Jacob

It appears (supposedly) as a constant in the code, perhaps it was intended to be a configurable number.

In the actual code, it's use is related to least-significant-bit rounding, and not as an actual numerical context at all. If you add -32768, and then right shift 15 bits, that is what it does.

Ah, then perhaps it was used - for the BMP085, which was a less accurate device. Unprofessional Guess...

askjacob:
ac1 = 8240;
ac2 = -1128;
ac3 = -1433;
ac4 = 33630;
ac5 = 25458 ;
ac6 = 18542 ;
b1 = 6515;
b2 = 42 ;
mb =-32768 ;
mc = -11786;
md = 2541;

UP= 85170
UT=26199
OK, my values ADDED IN ABOVE
....
Cheers
Jacob

I've taken the above values and come up with the following:

b3 = -8020 (ac1 * 4 + x3)
Pressure: 217.55 mbar, 6.42 inHg
Pressure sensor reading out of range!!!!

b3 = 1073733804 (uint32_t)(ac1 * 4 + x3)
Pressure: 217.55 mbar, 6.42 inHg
Pressure sensor reading out of range!!!!

b3 = 8364 ((int32_t)ac1 * 4 + x3)
Pressure: 2249.15 mbar, 66.42 inHg
Pressure sensor reading out of range!!!!

ehrja:
here:

Sensor calibration data:

AC1 = 9060
AC2 = -1244
AC3 = -14867
AC4 = 33491
AC5 = 25092
AC6 = 15143
B1 = 6515
B2 = 52
MB = -32768
MC = -11786
MD = 2764

Temperature: 34.00 C, 93.20 F
Pressure: 1014.22 mbar, 29.95 inHg

UT: 42153
UP: 337220




regards

b3 = -9321 (ac1 * 4 + x3)
Pressure: 107.73 mbar, 3.18 inHg
Pressure sensor reading out of range!!!!

b3 = 1073732503 (uint32_t)(ac1 * 4 + x3)
Pressure: 107.73 mbar, 3.18 inHg
Pressure sensor reading out of range!!!!

b3 = 7063 ((int32_t)ac1 * 4 + x3)
Pressure: 2584.11 mbar, 76.31 inHg
Pressure sensor reading out of range!!!!

With both examples I'm unable to produce valid pressure readings.

Please ignore my last 2 posts.

I have found something interesting concerning the Oversampling settings. I need to look into that in more detail.

Keep at it! This pressure sensor is next on my list of hardware I need to get working. Just reading through the stuff you've been doing & working on is getting me tired!

Thanks for listing all of this.

-jim lee

:slight_smile: last thing you want is a "Don't worry I fixed it" post with no follow up. The scourge of the internet search...

OK, the problem I got myself into is that I neglected to consider that we might all be using different OSS settings.
e.g. I'm using setting "3", the data sheet example uses setting "0". That is why I couldn't understand why I wasn't getting anything useful from your examples.

I've updated the code and would ask that you use this for further analysis:

/**********************************************************
  Bosch Pressure Sensor BMP085 / BMP180 readout routine
  for the Arduino platform.

  Version 1.1
  7/2014             
     
  Compiled by Leo Nutz
  www.ALTDuino.de
 **********************************************************/

#include <Wire.h>

#define ADDRESS_SENSOR 0x77                 // Sensor address

int16_t  ac1, ac2, ac3, b1, b2, mb, mc, md; // Store sensor PROM values from BMP180
uint16_t ac4, ac5, ac6;                     // Store sensor PROM values from BMP180

// Ultra Low Power       OSS = 0, OSD =  5ms
// Standard              OSS = 1, OSD =  8ms
// High                  OSS = 2, OSD = 14ms
// Ultra High Resolution OSS = 3, OSD = 26ms
const uint8_t oss = 3;                      // Set oversampling setting
      uint8_t osd;                          // Corresponding oversampling delay 

float T, P;                                 // Set global variables for temperature and pressure 

void setup()
{
  Wire.begin();                             // Activate I2C
  
  Serial.begin(9600);                       // Set up serial port

  Serial.println("");
  Serial.print("Oversampling setting (oss) = "); Serial.println(oss);

       if(oss == 0 ) osd =  5;
  else if(oss == 1 ) osd =  8;
  else if(oss == 2 ) osd = 14;
  else if(oss == 3 ) osd = 26;
  else { Serial.print("Incorrect OSS value defined!"); while(1){} }

  init_SENSOR();                            // Initialize baro sensor variables

  delay(100);
}

void loop()
{
  int32_t b5;
 
  b5 = temperature();                       // Read and calculate temperature (T) 

  Serial.print("Temperature: ");
  Serial.print(T, 2);
  Serial.print(" C, ");
  Serial.print(1.8 * T + 32.0, 2);
  Serial.println(" F");

  P = pressure(b5);                         // Read and calculate pressure (P) 

  Serial.print("Pressure: ");
  Serial.print(P, 2);
  Serial.print(" mbar, ");
  Serial.print(P * 0.0295299830714, 2);
  Serial.println(" inHg");
  if(P < 300 || P > 1100) Serial.println("Pressure sensor reading out of range!!!!"); // Pressure range according to the data sheet

  float alt = (pow(1013.25 / P, 0.190223f) - 1.0f) * 44330.08f;                       // Use static SI standard temperature (15°C) 
  
  Serial.print("Altitude: "); Serial.print(alt); Serial.print(" m, "); Serial.print(alt * 3.28084); Serial.println(" ft");
  Serial.println("-----------------------------------------");

  delay(500);                               // Delay between each readout
  
}

/**********************************************
  Initialize sensor variables
 **********************************************/
void init_SENSOR()
{
  ac1 = read_2_bytes(0xAA);
  ac2 = read_2_bytes(0xAC);
  ac3 = read_2_bytes(0xAE);
  ac4 = read_2_bytes(0xB0);
  ac5 = read_2_bytes(0xB2);
  ac6 = read_2_bytes(0xB4);
  b1  = read_2_bytes(0xB6);
  b2  = read_2_bytes(0xB8);
  mb  = read_2_bytes(0xBA);
  mc  = read_2_bytes(0xBC);
  md  = read_2_bytes(0xBE);

  Serial.println("");
  Serial.println("Sensor calibration data:");
  Serial.print(F("ac1 = ")); Serial.print(ac1); Serial.println(F(";"));
  Serial.print(F("ac2 = ")); Serial.print(ac2); Serial.println(F(";"));
  Serial.print(F("ac3 = ")); Serial.print(ac3); Serial.println(F(";"));
  Serial.print(F("ac4 = ")); Serial.print(ac4); Serial.println(F(";"));
  Serial.print(F("ac5 = ")); Serial.print(ac5); Serial.println(F(";"));
  Serial.print(F("ac6 = ")); Serial.print(ac6); Serial.println(F(";"));
  Serial.print(F(" b1 = ")); Serial.print(b1);  Serial.println(F(";"));
  Serial.print(F(" b2 = ")); Serial.print(b2);  Serial.println(F(";"));
  Serial.print(F(" mb = ")); Serial.print(mb);  Serial.println(F(";"));
  Serial.print(F(" mc = ")); Serial.print(mc);  Serial.println(F(";"));
  Serial.print(F(" md = ")); Serial.print(md);  Serial.println(F(";"));
  Serial.println("----------------");
}

/**********************************************
  Calcualte pressure readings
 **********************************************/
float pressure(int32_t b5)
{
  int32_t x1, x2, x3, b3, b6, p, UP;
  uint32_t b4, b7; 

  UP = read_pressure();                         // Read raw pressure
  Serial.print(F("UP = ")); Serial.print(UP); Serial.println(F(";"));

  b6 = b5 - 4000;
  x1 = (b2 * (b6 * b6 >> 12)) >> 11; 
  x2 = ac2 * b6 >> 11;
  x3 = x1 + x2;
  b3 = (((ac1 * 4 + x3) << oss) + 2) >> 2;
  x1 = ac3 * b6 >> 13;
  x2 = (b1 * (b6 * b6 >> 12)) >> 16;
  x3 = ((x1 + x2) + 2) >> 2;
  b4 = (ac4 * (uint32_t)(x3 + 32768)) >> 15;
  b7 = ((uint32_t)UP - b3) * (50000 >> oss);
  if(b7 < 0x80000000) { p = (b7 << 1) / b4; } else { p = (b7 / b4) << 1; } // or p = b7 < 0x80000000 ? (b7 * 2) / b4 : (b7 / b4) * 2;
  x1 = (p >> 8) * (p >> 8);
  x1 = (x1 * 3038) >> 16;
  x2 = (-7357 * p) >> 16;
  return (p + ((x1 + x2 + 3791) >> 4)) / 100.0f; // Return pressure in mbar
}

/**********************************************
  Read uncompensated temperature
 **********************************************/
int32_t temperature()
{
  int32_t x1, x2, b5, UT;

  Wire.beginTransmission(ADDRESS_SENSOR); // Start transmission to device 
  Wire.write(0xf4);                       // Sends register address
  Wire.write(0x2e);                       // Write data
  Wire.endTransmission();                 // End transmission
  delay(5);                               // Datasheet suggests 4.5 ms
  
  UT = read_2_bytes(0xf6);                // Read uncompensated TEMPERATURE value
  Serial.print(F("UT = ")); Serial.print(UT); Serial.println(F(";"));

  // Calculate true temperature
  x1 = (UT - (int32_t)ac6) * (int32_t)ac5 >> 15;
  x2 = ((int32_t)mc << 11) / (x1 + (int32_t)md);
  b5 = x1 + x2;
  T  = (b5 + 8) >> 4;
  T = T / 10.0;                           // Temperature in celsius 
  return b5;  
}

/**********************************************
  Read uncompensated pressure value
 **********************************************/
int32_t read_pressure()
{
  int32_t value; 
  Wire.beginTransmission(ADDRESS_SENSOR);   // Start transmission to device 
  Wire.write(0xf4);                         // Sends register address to read from
  Wire.write(0x34 + (oss << 6));            // Write data
  Wire.endTransmission();                   // SEd transmission
  delay(osd);                               // Oversampling setting delay
  Wire.beginTransmission(ADDRESS_SENSOR);
  Wire.write(0xf6);                         // Register to read
  Wire.endTransmission();
  Wire.requestFrom(ADDRESS_SENSOR, 3);      // Request three bytes
  if(Wire.available() >= 3)
  {
    value = (((int32_t)Wire.read() << 16) | ((int32_t)Wire.read() << 8) | ((int32_t)Wire.read())) >> (8 - oss);
  }
  return value;                             // Return value
}

/**********************************************
  Read 1 byte from the BMP sensor
 **********************************************/
uint8_t read_1_byte(uint8_t code)
{
  uint8_t value;
  Wire.beginTransmission(ADDRESS_SENSOR);         // Start transmission to device 
  Wire.write(code);                               // Sends register address to read from
  Wire.endTransmission();                         // End transmission
  Wire.requestFrom(ADDRESS_SENSOR, 1);            // Request data for 1 byte to be read
  if(Wire.available() >= 1)
  {
    value = Wire.read();                          // Get 1 byte of data
  }
  return value;                                   // Return value
}

/**********************************************
  Read 2 bytes from the BMP sensor
 **********************************************/
uint16_t read_2_bytes(uint8_t code)
{
  uint16_t value;
  Wire.beginTransmission(ADDRESS_SENSOR);         // Start transmission to device 
  Wire.write(code);                               // Sends register address to read from
  Wire.endTransmission();                         // End transmission
  Wire.requestFrom(ADDRESS_SENSOR, 2);            // Request 2 bytes from device
  if(Wire.available() >= 2)
  {
    value = (Wire.read() << 8) | Wire.read();     // Get 2 bytes of data
  }
  return value;                                   // Return value
}

Here is an example of my test using a UNO R3 and BMP180:

Oversampling setting (oss) = 3

Sensor calibration data:
ac1 = 6759;
ac2 = -1131;
ac3 = -14633;
ac4 = 33667;
ac5 = 24876;
ac6 = 20348;
b1 = 6515;
b2 = 42;
mb = -32768;
mc = -11786;
md = 2895;

UT = 28666;
Temperature: 23.10 C, 73.58 F
UP = 317365;
Pressure: 971.93 mbar, 28.70 inHg
Altitude: 352.49 m, 1156.45 ft

I practically get the same readings using oss 0 thru 3.

Out of range!:

Oversampling setting (oss) = 3

Sensor calibration data:
ac1 = 9060;
ac2 = -1244;
ac3 = -14867;
ac4 = 33491;
ac5 = 25092;
ac6 = 15143;
 b1 = 6515;
 b2 = 52;
 mb = -32768;
 mc = -11786;
 md = 2764;
----------------
UT = 24706;
Temperature: 30.80 C, 87.44 F
UP = 339901;
Pressure: 1512.55 mbar, 44.67 inHg
Pressure sensor reading out of range!!!!
Altitude: -3252.86 m, -10672.13 ft
-----------------------------------------

:cold_sweat:

Please also try oss = 0, 1 and 2 by changing this line:

const uint8_t oss = 3; // Set oversampling setting

case 3:

Oversampling setting (oss) = 3

Sensor calibration data:
ac1 = 9060;
ac2 = -1244;
ac3 = -14867;
ac4 = 33491;
ac5 = 25092;
ac6 = 15143;
 b1 = 6515;
 b2 = 52;
 mb = -32768;
 mc = -11786;
 md = 2764;
----------------
UT = 24691;
Temperature: 30.70 C, 87.26 F
UP = 339951;
Pressure: 1512.45 mbar, 44.66 inHg
Pressure sensor reading out of range!!!!
Altitude: -3252.35 m, -10670.43 ft
-----------------------------------------

case 2:

Oversampling setting (oss) = 2

Sensor calibration data:
ac1 = 9060;
ac2 = -1244;
ac3 = -14867;
ac4 = 33491;
ac5 = 25092;
ac6 = 15143;
 b1 = 6515;
 b2 = 52;
 mb = -32768;
 mc = -11786;
 md = 2764;
----------------
UT = 24738;
Temperature: 31.00 C, 87.80 F
UP = 169871;
Pressure: 1512.73 mbar, 44.67 inHg
Pressure sensor reading out of range!!!!
Altitude: -3253.79 m, -10675.18 ft
-----------------------------------------

case 1:

Oversampling setting (oss) = 1

Sensor calibration data:
ac1 = 9060;
ac2 = -1244;
ac3 = -14867;
ac4 = 33491;
ac5 = 25092;
ac6 = 15143;
 b1 = 6515;
 b2 = 52;
 mb = -32768;
 mc = -11786;
 md = 2764;
----------------
UT = 24690;
Temperature: 30.70 C, 87.26 F
UP = 84990;
Pressure: 1512.43 mbar, 44.66 inHg
Pressure sensor reading out of range!!!!
Altitude: -3252.24 m, -10670.09 ft
-----------------------------------------

case 0:

Oversampling setting (oss) = 0

Sensor calibration data:
ac1 = 9060;
ac2 = -1244;
ac3 = -14867;
ac4 = 33491;
ac5 = 25092;
ac6 = 15143;
 b1 = 6515;
 b2 = 52;
 mb = -32768;
 mc = -11786;
 md = 2764;
----------------
UT = 24696;
Temperature: 30.80 C, 87.44 F
UP = 42494;
Pressure: 1512.57 mbar, 44.67 inHg
Pressure sensor reading out of range!!!!
Altitude: -3252.97 m, -10672.46 ft
-----------------------------------------

OK, all show the same results.

Please try this by replacing

b3 = (((ac1 * 4 + x3) << oss) + 2) >> 2; // as per data sheet

with

// b3 = (((ac1 * 4 + x3) << oss) + 2) >> 2; // as per data sheet
int32_t b3_tmp = ac1; // Split up the equation...
b3_tmp = (b3_tmp * 4 + x3) << oss;
b3     = (b3_tmp + 2) / 4;

and run all 4 oss values.

OK

case 0:

-----------------------------------------
UT = 24677;
Temperature: 30.60 C, 87.08 F
UP = 42502;
Pressure: 1014.90 mbar, 29.97 inHg
Altitude: -13.72 m, -45.01 ft
-----------------------------------------

case 1:

-----------------------------------------
UT = 24740;
Temperature: 31.00 C, 87.80 F
UP = 84929;
Pressure: 1014.88 mbar, 29.97 inHg
Altitude: -13.55 m, -44.47 ft
-----------------------------------------

case 2:

-----------------------------------------
UT = 24760;
Temperature: 31.10 C, 87.98 F
UP = 169818;
Pressure: 1014.92 mbar, 29.97 inHg
Altitude: -13.89 m, -45.56 ft
-----------------------------------------

case 3:

-----------------------------------------
UT = 24776;
Temperature: 31.20 C, 88.16 F
UP = 339559;
Pressure: 1014.91 mbar, 29.97 inHg
Altitude: -13.80 m, -45.29 ft
-----------------------------------------

XD

That looks OK.

Didn't you figure out yesterday, you need to correct the type casting in that line of the program ?

Your problem is, -14657 x 4 + x3 << 3

will be evaluated on the arduino as a 16 bit number, and it will overflow. That is your problem, right there. You need to force that calculation to be done as a 32 bit int, before you get to the bit where it overflows, not afterwards.