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

Revision:
0:184eb17ff158
--- /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