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

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 #include "mbed.h"
00002 
00003 /* sample code for the barometer BMP085 as provided on the breakout board from sparkfun.
00004 Pull up resistors for I2C are already provided (aka already on the board).
00005 Link to sparkfun board: https://www.sparkfun.com/products/11282?
00006 
00007 Datasheet of barometer: http://dlnmh9ip6v2uc.cloudfront.net/datasheets/Sensors/Pressure/BST-BMP085-DS000-06.pdf
00008 
00009 Comments:
00010  - pressure values returned by the moving average seems believable (1090 Hecto Pascal on 12/18/2012)
00011  - Temperature seems to oscillate between 365 and 87 and seems totally wrong.
00012  - Altitude is off (obviously fluctates with weather) but roughly speaking 
00013    indicates -800m when altitude should be 0m
00014 
00015 Example of output in the terminal:
00016 
00017 Temperature: 87 (raw value (uncomp): 61184)
00018 Pressure (Pa): 106626 (raw: 249856) Mov Avg: 62353
00019 altitude is (m): -432.270721
00020 wikipedia altitude (m): -432.061523
00021 
00022 Temperature: 365 (raw value (uncomp): 62464)
00023 Pressure (Pa): 109312 (raw: 244736) Mov Avg: 67558
00024 altitude is (m): -644.688416
00025 wikipedia altitude (m): -644.382446
00026 
00027 Temperature: 365 (raw value (uncomp): 62464)
00028 Pressure (Pa): 111417 (raw: 249856) Mov Avg: 72864
00029 altitude is (m): -808.229309
00030 wikipedia altitude (m): -807.841614
00031    
00032 */
00033 
00034 #define EE 22 //The EEPROM has 176bits of calibration data (176/8 = 22 Bytes)
00035 #define BMP085ADDR 0xEF // I2C address of the barometer
00036 
00037 //Calibration variables
00038 long b5;
00039 short ac1;
00040 short ac2;
00041 short ac3;
00042 unsigned short ac4;
00043 unsigned short ac5;
00044 unsigned short ac6;
00045 short b1;
00046 short b2;
00047 short mb;
00048 short mc;
00049 short md;
00050 
00051 // Function to compute the human interpretable values
00052 int calcPress(int upp, int oversampling_setting);
00053 int calcTemp(int ut);
00054 float calcAltitude(int pressure);
00055 float calcWikipediaAltitude(int pressure);
00056  
00057 //Moving average variables
00058 #define COEFZ 21 // 21 bits used for computing the moving average
00059 static int k[COEFZ]; // an array used for the moving average
00060 static int movAvgIntZ (int input);
00061 
00062 //address of commands
00063 #define CMD_READ_VALUE 0xF6
00064 #define CMD_READ_CALIBRATION 0xAA
00065 
00066 #define OVERSAMPLING_ULTRA_LOW_POWER 0
00067 #define OVERSAMPLING_STANDARD 1
00068 #define OVERSAMPLING_HIGH_RESOLUTION 2
00069 #define OVERSAMPLING_ULTRA_HIGH_RESOLUTION 3
00070 
00071 const float ALTITUDE_EXPONENT = 1/5.255;
00072 const float ALTITUDE_PRESSURE_REFERENCE = 101325.0; // In Pa
00073 const float ALTITUDE_COEFF = 44330.0;
00074 
00075 const float TEMPERATURE_LAPSE_RATE = 0.0065; // in K/m
00076 const float SEA_LEVEL_STANDARD_TEMPERATURE = 288.15; // in Kelvins
00077 const float GRAVITATIONAL_ACCELERATION = 9.81; // in m/(s*s)
00078 const float MOLAR_MASS_AIR = 0.0289644; // in Kg/mol
00079 const float R = 8.31447; // in J/(mol*K) universal gas constant
00080   
00081 const float WIKIPEDIA_EXPONENT = GRAVITATIONAL_ACCELERATION * MOLAR_MASS_AIR/ (R * TEMPERATURE_LAPSE_RATE);
00082 
00083  
00084 I2C i2c(p28, p27);        // sda, scl
00085 InterruptIn dr(p26);      // EOC, conversion notification
00086                           // Note that XCLR is floating = not connected (power for analog portion?)
00087 
00088 uint32_t drFlag;
00089 
00090 void cvt();  // Send I2C command to start pressure conversion (?)
00091 void drSub(); // set a flag to one. Pretty poorly written code IMO.
00092 
00093 Serial pc(USBTX, USBRX); // tx, rx
00094  
00095 int main() {
00096  
00097     // let hardware settle
00098     wait(1);
00099  
00100     /////////////////////////////////////////////////
00101     // set up timer to trigger pressure conversion
00102     /////////////////////////////////////////////////
00103     Ticker convert;
00104     convert.attach_us(&cvt, 50000); // 50 ms, 20 Hz //Pointer to a void function
00105  
00106     /////////////////////////////////////////////////
00107     // set up data ready interrupts
00108     /////////////////////////////////////////////////
00109     // set up interrupts
00110     __disable_irq();
00111     // ADC data ready
00112     drFlag = 0;
00113     dr.mode(PullDown);
00114     dr.rise(&drSub);
00115  
00116     /////////////////////////////////////////////////
00117     // set up i2c
00118     /////////////////////////////////////////////////
00119     char addr = BMP085ADDR; // define the I2C Address
00120     int oversampling_setting = OVERSAMPLING_HIGH_RESOLUTION; // called oss in the documentation
00121     // read page 12 of doc. 3 is ultra high precision and it requires a lot of time for the device to do the conversion
00122     char rReg[3] = {0,0,0};  // read registers for I2C
00123     char wReg[2] = {0,0};  // write registers for I2C
00124     char cmd = 0x00; 
00125  
00126     /////////////////////////////////////////////////
00127     // get EEPROM calibration parameters
00128     /////////////////////////////////////////////////
00129  
00130     char data[EE];
00131     cmd = CMD_READ_CALIBRATION; // EEPROM calibration command
00132  
00133     for (int i = 0; i < EE; i++) { // read over the 22 registers of the EEPROM
00134         i2c.write(addr, &cmd, 1);
00135         i2c.read(addr, rReg, 1);  // rReg is array? of size 3. We only read the 1st register???
00136         data[i] = rReg[0];
00137         cmd += 1;
00138         wait_ms(10);
00139     }
00140  
00141     // parameters AC1-AC6
00142     //The calibration is partioned in 11 words of 16 bits, each of them representing a coefficient
00143     ac1 =  (data[0] <<8) | data[1]; // AC1(0xAA, 0xAB)... and so on
00144     ac2 =  (data[2] <<8) | data[3];
00145     ac3 =  (data[4] <<8) | data[5];
00146     ac4 =  (data[6] <<8) | data[7];
00147     ac5 =  (data[8] <<8) | data[9];
00148     ac6 = (data[10] <<8) | data[11];
00149     // parameters B1,B2
00150     b1 =  (data[12] <<8) | data[13];
00151     b2 =  (data[14] <<8) | data[15];
00152     // parameters MB,MC,MD
00153     mb =  (data[16] <<8) | data[17];
00154     mc =  (data[18] <<8) | data[19];
00155     md =  (data[20] <<8) | data[21];
00156  
00157     // ready to start sampling loop
00158     __enable_irq();
00159     
00160     // END OF INITIALIZATION
00161  
00162     /////////////////////////////////////////////////
00163     // main
00164     /////////////////////////////////////////////////
00165     pc.printf("Starting barometer main()!!\n\r");
00166     while (1) {
00167         if (drFlag == 1) {
00168            // Documentation recommend the following chronology: 
00169            // read uncomp temp, read  uncomp pressure, compute true temp, 
00170            // compute true pressure
00171  
00172             /////////////////////////////////////////////////
00173             // uncomp temperature
00174             /////////////////////////////////////////////////
00175             wReg[0] = 0xF4;
00176             wReg[1] = 0x2E; 
00177             i2c.write(addr, wReg, 2); // write 0x2E in reg 0XF4
00178             wait_ms(4.5);
00179             cmd = CMD_READ_VALUE; // 0xF6
00180             i2c.write(addr, &cmd, 1); // set pointer on 0xF6 before reading it?
00181             i2c.read(addr, rReg, 2); // read 0xF6 (MSB) and 0xF7 (LSB)// rReg is 3 long though 
00182             int uncomp_temperature = (rReg[0] << 8) | rReg[1]; // UT = MSB << 8 + LSB
00183             
00184             /////////////////////////////////////////////////
00185             // uncomp pressure
00186             /////////////////////////////////////////////////
00187             
00188             //write 0x34 + (oss<<6) into reg 0xF4, wait (waiting time not specified in doc)
00189             int uncomp_pressure_cmd = 0x34 + (oversampling_setting<<6);
00190             wReg[0] = 0xF4;
00191             wReg[1] = uncomp_pressure_cmd;
00192             i2c.write(addr, wReg, 2);
00193             // See page 12 of documentation
00194             switch (oversampling_setting) {
00195               case OVERSAMPLING_ULTRA_LOW_POWER:
00196                 wait_ms(4.5);
00197                 break;
00198               case OVERSAMPLING_STANDARD:
00199                 wait_ms(7.5);
00200                 break;
00201               case OVERSAMPLING_HIGH_RESOLUTION:
00202                 wait_ms(13.5);
00203                 break;
00204               case OVERSAMPLING_ULTRA_HIGH_RESOLUTION:
00205                 wait_ms(25.5);
00206                 break;
00207             }
00208            
00209             cmd = CMD_READ_VALUE; // 0xF6
00210             i2c.write(addr, &cmd, 1);
00211             i2c.read(addr, rReg, 3); // read 0xF6 (MSB), 0xF7 (LSB), 0xF8 (XLSB)
00212             int uncomp_pressure = ((rReg[0] << 16) | (rReg[1] << 8) | rReg[2]) >> (8 - oversampling_setting);
00213             // UP = (MSB<<16 + LSB<<8 + XLSB)>>(8-oss)
00214             
00215             // true temperature
00216             int temperature = calcTemp(uncomp_temperature);
00217             pc.printf("Temperature: %d (raw value (uncomp): %d)\n\r", temperature, uncomp_temperature);
00218             // even though implementation seems right, 
00219             //returned value is wrong hostilating between 87 and 365 (i.e. 8.7C and 36.5C)
00220             
00221             // True pressure
00222             int pressure = calcPress(uncomp_pressure, oversampling_setting);
00223             int avg_pressure = movAvgIntZ(pressure);
00224             pc.printf("Pressure (Pa): %d (raw: %d) Mov Avg: %d\n\r", pressure, uncomp_pressure, avg_pressure);
00225  
00226             //Compute an estimate of altitude
00227             // see page 15 of manual
00228             float altitude = calcAltitude(pressure);
00229             pc.printf("altitude is (m): %f\n\r", altitude); // note that the implementation is correct but returns wrong absolute numbers
00230 
00231             float wiki_altitude = calcWikipediaAltitude(pressure);
00232             pc.printf("wikipedia altitude (m): %f\n\r", wiki_altitude); // results are VERY similar and therefore not 
00233 
00234             /////////////////////////////////////////////////
00235             // data ready cleanup tasks
00236             /////////////////////////////////////////////////
00237             drFlag = 0; // This should stop the while loop... but it does not!
00238             pc.printf("\n\r");
00239         }
00240     wait(3); // just to make reading in the terminal easier
00241     } //end while
00242 }
00243  
00244 ////////////////////////////////////////////////////////////////////////////////////
00245 // start pressure conversion
00246 ////////////////////////////////////////////////////////////////////////////////////
00247 void cvt() { //This is only called once during the initialization phase!
00248     char w[2] = {0xF4, 0xF4};
00249     i2c.write(BMP085ADDR, w, 2);
00250 }
00251  
00252 ////////////////////////////////////////////////////////////////////////////////////
00253 // Handle data ready interrupt, just sets data ready flag
00254 ////////////////////////////////////////////////////////////////////////////////////
00255 void drSub() {
00256     drFlag = 1;
00257 }
00258  
00259 /////////////////////////////////////////////////
00260 // calculate compensated pressure
00261 /////////////////////////////////////////////////
00262 int calcPress(int upp, int oversampling_setting) {
00263    // Return the pressure in Pa (Pascals)
00264  
00265     long pressure, x1, x2, x3, b3, b6;
00266     unsigned long b4, b7;
00267     // read page 12 of doc. This is ultra high precision and it requires a lot of time for the device to do the conversion
00268  
00269     unsigned long up = (unsigned long)upp;
00270  
00271     b6 = b5 - 4000;
00272     // calculate B3
00273     x1 = (b6*b6) >> 12; // full formula(b2*(b6*b6)/pow(2,12))/pow(2,11)
00274     x1 *= b2;
00275     x1 >>= 11;
00276  
00277     x2 = (ac2*b6);
00278     x2 >>= 11;
00279  
00280     x3 = x1 + x2;
00281  
00282     b3 = (((((long)ac1 )*4 + x3) <<oversampling_setting) + 2) >> 2; // doc says /4!
00283  
00284     // calculate B4
00285     x1 = (ac3* b6) >> 13;
00286     x2 = (b1 * ((b6*b6) >> 12) ) >> 16;
00287     x3 = ((x1 + x2) + 2) >> 2; 
00288     b4 = (ac4 * (unsigned long) (x3 + 32768)) >> 15;
00289  
00290     b7 = ((unsigned long) up - b3) * (50000>>oversampling_setting);
00291     if (b7 < 0x80000000) {
00292         pressure = (b7 << 1) / b4; 
00293     } else {
00294         pressure = (b7 / b4) << 1; 
00295     }
00296  
00297     x1 = pressure >> 8;
00298     x1 *= x1; // pressure/pow(2,8) * pressure/pow(2, 8)
00299     x1 = (x1 * 3038) >> 16;
00300     x2 = (pressure * -7357) >> 16;
00301     pressure += (x1 + x2 + 3791) >> 4;    // pressure in Pa
00302  
00303     return (pressure);
00304 }
00305  
00306 /////////////////////////////////////////////////
00307 // calculate compensated temp from uncompensated
00308 /////////////////////////////////////////////////
00309 int calcTemp(int ut) {
00310     // Returns the temperature in °C
00311  
00312     int temp;
00313     long x1, x2;
00314  
00315     x1 = (((long) ut - (long) ac6) * (long) ac5) >> 15; // aka (ut-ac6) * ac5/pow(2,15)
00316     x2 = ((long) mc << 11) / (x1 + md); // aka mc * pow(2, 11) / (x1 + md)
00317     b5 = x1 + x2;
00318     temp = ((b5 + 8) >> 4); // (b5+8)/pow(2, 4)
00319     // temperature in 0.1°C (aka 1903 = 190.3°C) 
00320     return (temp);
00321 }
00322  
00323 ////////////////////////////////////////////////////////////////////////////////////
00324 // int version of moving average filter
00325 ////////////////////////////////////////////////////////////////////////////////////
00326  
00327 static int movAvgIntZ (int input) {
00328     int cum = 0;
00329     for (int i = 0; i < COEFZ; i++) {
00330         k[i] = k[i+1]; // do we really need k as a global variable?
00331     }
00332     k[COEFZ - 1] = input;
00333     for (int i = 0; i < COEFZ; i++) {
00334         cum += k[i];
00335     }
00336     return (cum/COEFZ) ;
00337 }
00338 
00339 float calcAltitude(int pressure) {
00340   //Compute an estimate of altitude
00341   // see page 15 of manual
00342   float float_comp_pressure = (float) pressure;
00343   float altitude = float_comp_pressure/ALTITUDE_PRESSURE_REFERENCE;
00344   altitude = pow(altitude, ALTITUDE_EXPONENT);
00345   altitude = 1.0 - altitude;
00346   altitude *= ALTITUDE_COEFF;
00347   // formula is: altitude = 44330 * (1-pow((p/p0), 1/5.255))
00348   return altitude;
00349 }
00350 
00351 float calcWikipediaAltitude(int pressure) {
00352   // compute the altitude in meters
00353   // per: http://en.wikipedia.org/wiki/Atmospheric_pressure
00354   float float_comp_pressure = (float) pressure;
00355 
00356   float altitude = float_comp_pressure/ALTITUDE_PRESSURE_REFERENCE;
00357   float exponent = 1.0/WIKIPEDIA_EXPONENT;
00358   altitude = pow(altitude, exponent);
00359   altitude = 1.0 - altitude;
00360   altitude *= (SEA_LEVEL_STANDARD_TEMPERATURE/TEMPERATURE_LAPSE_RATE);
00361   
00362   return altitude;
00363 }