bring up code for the Bosch barometer and thermometer BMP085. This code is forked from http://mbed.org/users/tkreyche/notebook/bmp085-pressure-sensor/ but with a lot more comments and links to references. The extraction of the altitude via 2 methods is also implemented. Overall temperature seems way off (but implementation is correct). Altitude is off too (but implementation is correct).

Dependencies:   mbed

Files at this revision

API Documentation at this revision

Comitter:
Rom
Date:
Fri Dec 28 06:09:46 2012 +0000
Commit message:
Bring up program of the Bosch Barometer + temperature. Absolute values are wrong despite correct implementation.

Changed in this revision

main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r 184eb17ff158 main.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Fri Dec 28 06:09:46 2012 +0000
@@ -0,0 +1,363 @@
+#include "mbed.h"
+
+/* sample code for the barometer BMP085 as provided on the breakout board from sparkfun.
+Pull up resistors for I2C are already provided (aka already on the board).
+Link to sparkfun board: https://www.sparkfun.com/products/11282?
+
+Datasheet of barometer: http://dlnmh9ip6v2uc.cloudfront.net/datasheets/Sensors/Pressure/BST-BMP085-DS000-06.pdf
+
+Comments:
+ - pressure values returned by the moving average seems believable (1090 Hecto Pascal on 12/18/2012)
+ - Temperature seems to oscillate between 365 and 87 and seems totally wrong.
+ - Altitude is off (obviously fluctates with weather) but roughly speaking 
+   indicates -800m when altitude should be 0m
+
+Example of output in the terminal:
+
+Temperature: 87 (raw value (uncomp): 61184)
+Pressure (Pa): 106626 (raw: 249856) Mov Avg: 62353
+altitude is (m): -432.270721
+wikipedia altitude (m): -432.061523
+
+Temperature: 365 (raw value (uncomp): 62464)
+Pressure (Pa): 109312 (raw: 244736) Mov Avg: 67558
+altitude is (m): -644.688416
+wikipedia altitude (m): -644.382446
+
+Temperature: 365 (raw value (uncomp): 62464)
+Pressure (Pa): 111417 (raw: 249856) Mov Avg: 72864
+altitude is (m): -808.229309
+wikipedia altitude (m): -807.841614
+   
+*/
+
+#define EE 22 //The EEPROM has 176bits of calibration data (176/8 = 22 Bytes)
+#define BMP085ADDR 0xEF // I2C address of the barometer
+
+//Calibration variables
+long b5;
+short ac1;
+short ac2;
+short ac3;
+unsigned short ac4;
+unsigned short ac5;
+unsigned short ac6;
+short b1;
+short b2;
+short mb;
+short mc;
+short md;
+
+// Function to compute the human interpretable values
+int calcPress(int upp, int oversampling_setting);
+int calcTemp(int ut);
+float calcAltitude(int pressure);
+float calcWikipediaAltitude(int pressure);
+ 
+//Moving average variables
+#define COEFZ 21 // 21 bits used for computing the moving average
+static int k[COEFZ]; // an array used for the moving average
+static int movAvgIntZ (int input);
+
+//address of commands
+#define CMD_READ_VALUE 0xF6
+#define CMD_READ_CALIBRATION 0xAA
+
+#define OVERSAMPLING_ULTRA_LOW_POWER 0
+#define OVERSAMPLING_STANDARD 1
+#define OVERSAMPLING_HIGH_RESOLUTION 2
+#define OVERSAMPLING_ULTRA_HIGH_RESOLUTION 3
+
+const float ALTITUDE_EXPONENT = 1/5.255;
+const float ALTITUDE_PRESSURE_REFERENCE = 101325.0; // In Pa
+const float ALTITUDE_COEFF = 44330.0;
+
+const float TEMPERATURE_LAPSE_RATE = 0.0065; // in K/m
+const float SEA_LEVEL_STANDARD_TEMPERATURE = 288.15; // in Kelvins
+const float GRAVITATIONAL_ACCELERATION = 9.81; // in m/(s*s)
+const float MOLAR_MASS_AIR = 0.0289644; // in Kg/mol
+const float R = 8.31447; // in J/(mol*K) universal gas constant
+  
+const float WIKIPEDIA_EXPONENT = GRAVITATIONAL_ACCELERATION * MOLAR_MASS_AIR/ (R * TEMPERATURE_LAPSE_RATE);
+
+ 
+I2C i2c(p28, p27);        // sda, scl
+InterruptIn dr(p26);      // EOC, conversion notification
+                          // Note that XCLR is floating = not connected (power for analog portion?)
+
+uint32_t drFlag;
+
+void cvt();  // Send I2C command to start pressure conversion (?)
+void drSub(); // set a flag to one. Pretty poorly written code IMO.
+
+Serial pc(USBTX, USBRX); // tx, rx
+ 
+int main() {
+ 
+    // let hardware settle
+    wait(1);
+ 
+    /////////////////////////////////////////////////
+    // set up timer to trigger pressure conversion
+    /////////////////////////////////////////////////
+    Ticker convert;
+    convert.attach_us(&cvt, 50000); // 50 ms, 20 Hz //Pointer to a void function
+ 
+    /////////////////////////////////////////////////
+    // set up data ready interrupts
+    /////////////////////////////////////////////////
+    // set up interrupts
+    __disable_irq();
+    // ADC data ready
+    drFlag = 0;
+    dr.mode(PullDown);
+    dr.rise(&drSub);
+ 
+    /////////////////////////////////////////////////
+    // set up i2c
+    /////////////////////////////////////////////////
+    char addr = BMP085ADDR; // define the I2C Address
+    int oversampling_setting = OVERSAMPLING_HIGH_RESOLUTION; // called oss in the documentation
+    // read page 12 of doc. 3 is ultra high precision and it requires a lot of time for the device to do the conversion
+    char rReg[3] = {0,0,0};  // read registers for I2C
+    char wReg[2] = {0,0};  // write registers for I2C
+    char cmd = 0x00; 
+ 
+    /////////////////////////////////////////////////
+    // get EEPROM calibration parameters
+    /////////////////////////////////////////////////
+ 
+    char data[EE];
+    cmd = CMD_READ_CALIBRATION; // EEPROM calibration command
+ 
+    for (int i = 0; i < EE; i++) { // read over the 22 registers of the EEPROM
+        i2c.write(addr, &cmd, 1);
+        i2c.read(addr, rReg, 1);  // rReg is array? of size 3. We only read the 1st register???
+        data[i] = rReg[0];
+        cmd += 1;
+        wait_ms(10);
+    }
+ 
+    // parameters AC1-AC6
+    //The calibration is partioned in 11 words of 16 bits, each of them representing a coefficient
+    ac1 =  (data[0] <<8) | data[1]; // AC1(0xAA, 0xAB)... and so on
+    ac2 =  (data[2] <<8) | data[3];
+    ac3 =  (data[4] <<8) | data[5];
+    ac4 =  (data[6] <<8) | data[7];
+    ac5 =  (data[8] <<8) | data[9];
+    ac6 = (data[10] <<8) | data[11];
+    // parameters B1,B2
+    b1 =  (data[12] <<8) | data[13];
+    b2 =  (data[14] <<8) | data[15];
+    // parameters MB,MC,MD
+    mb =  (data[16] <<8) | data[17];
+    mc =  (data[18] <<8) | data[19];
+    md =  (data[20] <<8) | data[21];
+ 
+    // ready to start sampling loop
+    __enable_irq();
+    
+    // END OF INITIALIZATION
+ 
+    /////////////////////////////////////////////////
+    // main
+    /////////////////////////////////////////////////
+    pc.printf("Starting barometer main()!!\n\r");
+    while (1) {
+        if (drFlag == 1) {
+           // Documentation recommend the following chronology: 
+           // read uncomp temp, read  uncomp pressure, compute true temp, 
+           // compute true pressure
+ 
+            /////////////////////////////////////////////////
+            // uncomp temperature
+            /////////////////////////////////////////////////
+            wReg[0] = 0xF4;
+            wReg[1] = 0x2E; 
+            i2c.write(addr, wReg, 2); // write 0x2E in reg 0XF4
+            wait_ms(4.5);
+            cmd = CMD_READ_VALUE; // 0xF6
+            i2c.write(addr, &cmd, 1); // set pointer on 0xF6 before reading it?
+            i2c.read(addr, rReg, 2); // read 0xF6 (MSB) and 0xF7 (LSB)// rReg is 3 long though 
+            int uncomp_temperature = (rReg[0] << 8) | rReg[1]; // UT = MSB << 8 + LSB
+            
+            /////////////////////////////////////////////////
+            // uncomp pressure
+            /////////////////////////////////////////////////
+            
+            //write 0x34 + (oss<<6) into reg 0xF4, wait (waiting time not specified in doc)
+            int uncomp_pressure_cmd = 0x34 + (oversampling_setting<<6);
+            wReg[0] = 0xF4;
+            wReg[1] = uncomp_pressure_cmd;
+            i2c.write(addr, wReg, 2);
+            // See page 12 of documentation
+            switch (oversampling_setting) {
+              case OVERSAMPLING_ULTRA_LOW_POWER:
+                wait_ms(4.5);
+                break;
+              case OVERSAMPLING_STANDARD:
+                wait_ms(7.5);
+                break;
+              case OVERSAMPLING_HIGH_RESOLUTION:
+                wait_ms(13.5);
+                break;
+              case OVERSAMPLING_ULTRA_HIGH_RESOLUTION:
+                wait_ms(25.5);
+                break;
+            }
+           
+            cmd = CMD_READ_VALUE; // 0xF6
+            i2c.write(addr, &cmd, 1);
+            i2c.read(addr, rReg, 3); // read 0xF6 (MSB), 0xF7 (LSB), 0xF8 (XLSB)
+            int uncomp_pressure = ((rReg[0] << 16) | (rReg[1] << 8) | rReg[2]) >> (8 - oversampling_setting);
+            // UP = (MSB<<16 + LSB<<8 + XLSB)>>(8-oss)
+            
+            // true temperature
+            int temperature = calcTemp(uncomp_temperature);
+            pc.printf("Temperature: %d (raw value (uncomp): %d)\n\r", temperature, uncomp_temperature);
+            // even though implementation seems right, 
+            //returned value is wrong hostilating between 87 and 365 (i.e. 8.7C and 36.5C)
+            
+            // True pressure
+            int pressure = calcPress(uncomp_pressure, oversampling_setting);
+            int avg_pressure = movAvgIntZ(pressure);
+            pc.printf("Pressure (Pa): %d (raw: %d) Mov Avg: %d\n\r", pressure, uncomp_pressure, avg_pressure);
+ 
+            //Compute an estimate of altitude
+            // see page 15 of manual
+            float altitude = calcAltitude(pressure);
+            pc.printf("altitude is (m): %f\n\r", altitude); // note that the implementation is correct but returns wrong absolute numbers
+
+            float wiki_altitude = calcWikipediaAltitude(pressure);
+            pc.printf("wikipedia altitude (m): %f\n\r", wiki_altitude); // results are VERY similar and therefore not 
+
+            /////////////////////////////////////////////////
+            // data ready cleanup tasks
+            /////////////////////////////////////////////////
+            drFlag = 0; // This should stop the while loop... but it does not!
+            pc.printf("\n\r");
+        }
+    wait(3); // just to make reading in the terminal easier
+    } //end while
+}
+ 
+////////////////////////////////////////////////////////////////////////////////////
+// start pressure conversion
+////////////////////////////////////////////////////////////////////////////////////
+void cvt() { //This is only called once during the initialization phase!
+    char w[2] = {0xF4, 0xF4};
+    i2c.write(BMP085ADDR, w, 2);
+}
+ 
+////////////////////////////////////////////////////////////////////////////////////
+// Handle data ready interrupt, just sets data ready flag
+////////////////////////////////////////////////////////////////////////////////////
+void drSub() {
+    drFlag = 1;
+}
+ 
+/////////////////////////////////////////////////
+// calculate compensated pressure
+/////////////////////////////////////////////////
+int calcPress(int upp, int oversampling_setting) {
+   // Return the pressure in Pa (Pascals)
+ 
+    long pressure, x1, x2, x3, b3, b6;
+    unsigned long b4, b7;
+    // read page 12 of doc. This is ultra high precision and it requires a lot of time for the device to do the conversion
+ 
+    unsigned long up = (unsigned long)upp;
+ 
+    b6 = b5 - 4000;
+    // calculate B3
+    x1 = (b6*b6) >> 12; // full formula(b2*(b6*b6)/pow(2,12))/pow(2,11)
+    x1 *= b2;
+    x1 >>= 11;
+ 
+    x2 = (ac2*b6);
+    x2 >>= 11;
+ 
+    x3 = x1 + x2;
+ 
+    b3 = (((((long)ac1 )*4 + x3) <<oversampling_setting) + 2) >> 2; // doc says /4!
+ 
+    // calculate B4
+    x1 = (ac3* b6) >> 13;
+    x2 = (b1 * ((b6*b6) >> 12) ) >> 16;
+    x3 = ((x1 + x2) + 2) >> 2; 
+    b4 = (ac4 * (unsigned long) (x3 + 32768)) >> 15;
+ 
+    b7 = ((unsigned long) up - b3) * (50000>>oversampling_setting);
+    if (b7 < 0x80000000) {
+        pressure = (b7 << 1) / b4; 
+    } else {
+        pressure = (b7 / b4) << 1; 
+    }
+ 
+    x1 = pressure >> 8;
+    x1 *= x1; // pressure/pow(2,8) * pressure/pow(2, 8)
+    x1 = (x1 * 3038) >> 16;
+    x2 = (pressure * -7357) >> 16;
+    pressure += (x1 + x2 + 3791) >> 4;    // pressure in Pa
+ 
+    return (pressure);
+}
+ 
+/////////////////////////////////////////////////
+// calculate compensated temp from uncompensated
+/////////////////////////////////////////////////
+int calcTemp(int ut) {
+    // Returns the temperature in °C
+ 
+    int temp;
+    long x1, x2;
+ 
+    x1 = (((long) ut - (long) ac6) * (long) ac5) >> 15; // aka (ut-ac6) * ac5/pow(2,15)
+    x2 = ((long) mc << 11) / (x1 + md); // aka mc * pow(2, 11) / (x1 + md)
+    b5 = x1 + x2;
+    temp = ((b5 + 8) >> 4); // (b5+8)/pow(2, 4)
+    // temperature in 0.1°C (aka 1903 = 190.3°C) 
+    return (temp);
+}
+ 
+////////////////////////////////////////////////////////////////////////////////////
+// int version of moving average filter
+////////////////////////////////////////////////////////////////////////////////////
+ 
+static int movAvgIntZ (int input) {
+    int cum = 0;
+    for (int i = 0; i < COEFZ; i++) {
+        k[i] = k[i+1]; // do we really need k as a global variable?
+    }
+    k[COEFZ - 1] = input;
+    for (int i = 0; i < COEFZ; i++) {
+        cum += k[i];
+    }
+    return (cum/COEFZ) ;
+}
+
+float calcAltitude(int pressure) {
+  //Compute an estimate of altitude
+  // see page 15 of manual
+  float float_comp_pressure = (float) pressure;
+  float altitude = float_comp_pressure/ALTITUDE_PRESSURE_REFERENCE;
+  altitude = pow(altitude, ALTITUDE_EXPONENT);
+  altitude = 1.0 - altitude;
+  altitude *= ALTITUDE_COEFF;
+  // formula is: altitude = 44330 * (1-pow((p/p0), 1/5.255))
+  return altitude;
+}
+
+float calcWikipediaAltitude(int pressure) {
+  // compute the altitude in meters
+  // per: http://en.wikipedia.org/wiki/Atmospheric_pressure
+  float float_comp_pressure = (float) pressure;
+
+  float altitude = float_comp_pressure/ALTITUDE_PRESSURE_REFERENCE;
+  float exponent = 1.0/WIKIPEDIA_EXPONENT;
+  altitude = pow(altitude, exponent);
+  altitude = 1.0 - altitude;
+  altitude *= (SEA_LEVEL_STANDARD_TEMPERATURE/TEMPERATURE_LAPSE_RATE);
+  
+  return altitude;
+}
\ No newline at end of file
diff -r 000000000000 -r 184eb17ff158 mbed.bld
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Fri Dec 28 06:09:46 2012 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/63cdd78b2dc1
\ No newline at end of file