Driver for Bosch Sensortec BME280 combined humidity and pressure sensor

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers bme280.cpp Source File

bme280.cpp

00001 /*
00002   bme280.cpp - driver for Bosch Sensortec BME280 combined humidity and pressure sensor.
00003 
00004   Copyright (c) 2015 Elektor
00005 
00006   26/11/2015 - CPV, Initial release.
00007   
00008   This library is free software; you can redistribute it and/or
00009   modify it under the terms of the GNU Lesser General Public
00010   License as published by the Free Software Foundation; either
00011   version 2.1 of the License, or (at your option) any later version.
00012 
00013   This library is distributed in the hope that it will be useful,
00014   but WITHOUT ANY WARRANTY; without even the implied warranty of
00015   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016   Lesser General Public License for more details.
00017 
00018   You should have received a copy of the GNU Lesser General
00019   Public License along with this library; if not, write to the
00020   Free Software Foundation, Inc., 59 Temple Place, Suite 330,
00021   Boston, MA  02111-1307  USA
00022 
00023 */
00024 
00025 #include "bme280.h"
00026 
00027 
00028 BME280::BME280(void)
00029 {
00030   _i2c_address = 0;
00031   _t_fine = 0;
00032   _temperature = 0;
00033   _pressure = 0;
00034   _humidity = 0;
00035   clearCalibrationData();
00036 }
00037 
00038 
00039 void BME280::clearCalibrationData(void)
00040 {
00041   _dig_T1 = 0;
00042   _dig_T2 = 0;
00043   _dig_T3 = 0;
00044   _dig_P1 = 0;
00045   _dig_P2 = 0;
00046   _dig_P3 = 0;
00047   _dig_P4 = 0;
00048   _dig_P5 = 0;
00049   _dig_P6 = 0;
00050   _dig_P7 = 0;
00051   _dig_P8 = 0;
00052   _dig_P9 = 0;
00053   _dig_H1 = 0;
00054   _dig_H2 = 0;
00055   _dig_H3 = 0;
00056   _dig_H4 = 0;
00057   _dig_H5 = 0;
00058   _dig_H6 = 0;
00059 }
00060 
00061 
00062 uint8_t BME280::begin(uint8_t i2cAddress)
00063 {
00064   _i2c_address = i2cAddress;
00065   if (readId()==BME280_ID)
00066   {
00067     clearCalibrationData();
00068     readCalibrationData();
00069     return 0;
00070   }
00071   return (uint8_t)-1;
00072 }
00073 
00074 
00075 void BME280::busWrite(uint8_t *p_data, uint8_t data_size, uint8_t repeated_start)
00076 {
00077   if (_i2c_address==BME280_I2C_ADDRESS1 || _i2c_address==BME280_I2C_ADDRESS2)
00078   {
00079     // Assume I2C bus.
00080     i2cWrite(_i2c_address,p_data,data_size,repeated_start);
00081   }
00082   else
00083   {
00084     // Assume SPI bus.
00085     // First byte is supposed to be the address of the register to write to, set R/~W bit to 0.
00086     p_data[0] &= 0x7f;
00087     spiWrite(p_data,data_size);
00088   }
00089 }
00090 
00091 
00092 void BME280::busRead(uint8_t *p_data, uint8_t data_size)
00093 {
00094   if (_i2c_address==BME280_I2C_ADDRESS1 || _i2c_address==BME280_I2C_ADDRESS2)
00095   {
00096     // Assume I2C bus.
00097     i2cRead(_i2c_address,p_data,data_size);
00098   }
00099   else
00100   {
00101     // Assume SPI bus.
00102     // First byte is supposed to be the address of the register to write to, set R/~W bit to 1.
00103     p_data[0] |= 0x80;
00104     spiRead(p_data,data_size);
00105   }
00106 }
00107 
00108 
00109 uint8_t BME280::readUint8(uint8_t reg)
00110 {
00111   uint8_t data;
00112   busWrite(&reg,1,1); // Use repeated start.
00113   busRead(&data,1); // Read one byte.
00114   return data;
00115 }
00116 
00117 
00118 uint16_t BME280::readUint16(uint8_t reg)
00119 {
00120   uint8_t data[2];
00121   uint16_t value;
00122   busWrite(&reg,1,1); // Use repeated start.
00123   busRead(data,2); // Read two bytes.
00124   // Process as little endian, which is the case for calibration data.
00125   value = data[1];
00126   value = (value<<8) | data[0];
00127   return value;
00128 }
00129 
00130 
00131 void BME280::readCalibrationData(void)
00132 {
00133   _dig_T1 = readUint16(BME280_CAL_T1);
00134   _dig_T2 = (int16_t) readUint16(BME280_CAL_T2);
00135   _dig_T3 = (int16_t) readUint16(BME280_CAL_T3);
00136   _dig_P1 = readUint16(BME280_CAL_P1);
00137   _dig_P2 = (int16_t) readUint16(BME280_CAL_P2);
00138   _dig_P3 = (int16_t) readUint16(BME280_CAL_P3);
00139   _dig_P4 = (int16_t) readUint16(BME280_CAL_P4);
00140   _dig_P5 = (int16_t) readUint16(BME280_CAL_P5);
00141   _dig_P6 = (int16_t) readUint16(BME280_CAL_P6);
00142   _dig_P7 = (int16_t) readUint16(BME280_CAL_P7);
00143   _dig_P8 = (int16_t) readUint16(BME280_CAL_P8);
00144   _dig_P9 = (int16_t) readUint16(BME280_CAL_P9);
00145   _dig_H1 = readUint8(BME280_CAL_H1);
00146   _dig_H2 = (int16_t) readUint16(BME280_CAL_H2);
00147   _dig_H3 = readUint8(BME280_CAL_H3);
00148   // H4 & H5 share a byte.
00149   uint8_t temp1 = readUint8(BME280_CAL_H4);
00150   uint8_t temp2 = readUint8(BME280_CAL_H45);
00151   uint8_t temp3 = readUint8(BME280_CAL_H5);
00152   _dig_H4 = (temp1<<4) | (temp2&0x0f);
00153   _dig_H5 = (temp3<<4) | (temp2>>4);
00154   _dig_H6 = (int8_t) readUint8(BME280_CAL_H6);
00155 }
00156 
00157 
00158 uint8_t BME280::readFrom(uint8_t reg, uint8_t data_size, uint8_t *p_data)
00159 {
00160   // Set start address to read from.
00161   busWrite(&reg,1,1); // Use repeated start.
00162   // Now read the requested number of bytes.
00163   busRead(p_data,data_size);
00164   return data_size;
00165 }
00166 
00167 
00168 void BME280::read(void)
00169 {
00170   // Get all the measurements in one burst (recommended).
00171   uint8_t data[BME280_MEASUREMENT_SIZE];
00172   readFrom(BME280_MEASUREMENT_REGISTER,BME280_MEASUREMENT_SIZE,data);
00173   // We assume Normal mode, so it is not necessary to reissue a Forced mode command here.
00174 
00175   // Process data.
00176   int32_t p = assembleRawValue(&data[0],1);
00177   int32_t t = assembleRawValue(&data[3],1);
00178   int32_t h = assembleRawValue(&data[6],0);
00179 
00180   _temperature = compensateTemperature(t); // First call this before calling the other compensate functions.
00181   _pressure = compensatePressure(p); // Uses value calculated by compensateTemperature.
00182   _humidity = compensateHumidity(h); // Uses value calculated by compensateTemperature.
00183 }
00184 
00185 
00186 int32_t BME280::assembleRawValue(uint8_t *p_data, uint8_t has_xlsb)
00187 {
00188   // Needed to decode sensor data.
00189   uint32_t value = p_data[0];
00190   value <<= 8;
00191   value |= p_data[1];
00192   if (has_xlsb!=0)
00193   {
00194     value <<= 4;
00195     value |= (p_data[2]>>4);
00196   }
00197   return (int32_t) value;
00198 }
00199 
00200 
00201 void BME280::writeControlRegisters(uint8_t osrs_t, uint8_t osrs_p, uint8_t osrs_h, uint8_t mode)
00202 {
00203   uint8_t data[2];
00204   data[0] = BME280_CTRL_HUM_REGISTER;
00205   data[1] = (osrs_h&0x07);
00206   busWrite(data,2,0);
00207   // Writing CTRL_MEAS validates previous write to CTRL_HUM.
00208   data[0] = BME280_CTRL_MEAS_REGISTER;
00209   data[1] = ((osrs_t&0x07)<<5) | ((osrs_p&0x07)<<2) | (mode&0x03);
00210   busWrite(data,2,0);
00211 }
00212 
00213 
00214 void BME280::writeConfigRegister(uint8_t t_sb, uint8_t filter, uint8_t spi)
00215 {
00216   uint8_t data[2];
00217   data[0] = BME280_CONFIG_REGISTER;
00218   data[1] = ((t_sb&0x07)<<5) | ((filter&0x07)<<2) | (spi&0x01);
00219   busWrite(data,2,0);
00220 }
00221 
00222 
00223 void BME280::reset(void)
00224 {
00225   uint8_t data[2] = { BME280_RESET_REGISTER, BME280_RESET };
00226   busWrite(data,2,0);
00227 }
00228 
00229 
00230 uint8_t BME280::readId(void)
00231 {
00232   return readUint8(BME280_ID_REGISTER);
00233 }
00234 
00235 
00236 #if BME280_ALLOW_FLOAT!=0
00237 
00238 // From the driver by Bosch Sensortec
00239 
00240 //!
00241 // @brief Reads actual temperature from uncompensated temperature
00242 // @note returns the value in Degree centigrade
00243 // @note Output value of "51.23" equals 51.23 DegC.
00244 //
00245 //  @param adc_T : value of uncompensated temperature
00246 //
00247 //  @return  Return the actual temperature in floating point
00248 //
00249 temperature_t BME280::compensateTemperature(int32_t adc_T)
00250 {
00251   double v_x1_u32;
00252   double v_x2_u32;
00253   double temperature;
00254   
00255   v_x1_u32  = (((double)adc_T) / 16384.0 - ((double)_dig_T1) / 1024.0) * ((double)_dig_T2);
00256   v_x2_u32  = ((((double)adc_T) / 131072.0 - ((double)_dig_T1) / 8192.0) * (((double)adc_T) / 131072.0 - ((double)_dig_T1) / 8192.0)) * ((double)_dig_T3);
00257   _t_fine = (int32_t)(v_x1_u32 + v_x2_u32);
00258   temperature  = (v_x1_u32 + v_x2_u32) / 5120.0;
00259   return temperature;
00260 }
00261 
00262 
00263 //!
00264 // @brief Reads actual pressure from uncompensated pressure
00265 // @note Returns pressure in Pa as double.
00266 // @note Output value of "96386.2"
00267 // equals 96386.2 Pa = 963.862 hPa.
00268 //
00269 //  @param adc_P : value of uncompensated pressure
00270 //
00271 //  @return  Return the actual pressure in floating point
00272 //
00273 pressure_t BME280::compensatePressure(int32_t adc_P)
00274 {
00275   double v_x1_u32;
00276   double v_x2_u32;
00277   double pressure;
00278   
00279   v_x1_u32 = ((double)_t_fine / 2.0) - 64000.0;
00280   v_x2_u32 = v_x1_u32 * v_x1_u32 * ((double)_dig_P6) / 32768.0;
00281   v_x2_u32 = v_x2_u32 + v_x1_u32 * ((double)_dig_P5) * 2.0;
00282   v_x2_u32 = (v_x2_u32 / 4.0) + (((double)_dig_P4) * 65536.0);
00283   v_x1_u32 = (((double)_dig_P3) * v_x1_u32 * v_x1_u32 / 524288.0 + ((double)_dig_P2) * v_x1_u32) / 524288.0;
00284   v_x1_u32 = (1.0 + v_x1_u32 / 32768.0) * ((double)_dig_P1);
00285   pressure = 1048576.0 - (double)adc_P;
00286   // Avoid exception caused by division by zero.
00287   if (v_x1_u32 != 0) pressure = (pressure - (v_x2_u32 / 4096.0)) * 6250.0 / v_x1_u32;
00288   else return 0;
00289   v_x1_u32 = ((double)_dig_P9) * pressure * pressure / 2147483648.0;
00290   v_x2_u32 = pressure * ((double)_dig_P8) / 32768.0;
00291   pressure = pressure + (v_x1_u32 + v_x2_u32 + ((double)_dig_P7)) / 16.0;
00292   
00293   return pressure;
00294 }
00295 
00296 
00297 //!
00298 // @brief Reads actual humidity from uncompensated humidity
00299 // @note returns the value in relative humidity (%rH)
00300 // @note Output value of "42.12" equals 42.12 %rH
00301 //
00302 //  @param adc_H : value of uncompensated humidity
00303 //
00304 //  @return Return the actual humidity in floating point
00305 //
00306 humidity_t BME280::compensateHumidity(int32_t adc_H)
00307 {
00308   double var_h;
00309   
00310   var_h = (((double)_t_fine) - 76800.0);
00311   if (var_h != 0)
00312   {
00313     var_h = (adc_H - (((double)_dig_H4) * 64.0 + ((double)_dig_H5) / 16384.0 * var_h)) * 
00314       (((double)_dig_H2) / 65536.0 * (1.0 + ((double) _dig_H6) / 67108864.0 * 
00315       var_h * (1.0 + ((double)_dig_H3) / 67108864.0 * var_h)));
00316   }
00317   else return 0;
00318   var_h = var_h * (1.0 - ((double)_dig_H1)*var_h / 524288.0);
00319   if (var_h > 100.0) var_h = 100.0;
00320   else if (var_h < 0.0) var_h = 0.0;
00321   return var_h;
00322 }
00323 
00324 #else /* BME280_ALLOW_FLOAT */
00325 
00326 // From the datasheet.
00327 // Returns temperature in DegC, resolution is 0.01 DegC. Output value of 5123 equals 51.23 DegC.
00328 // _t_fine carries fine temperature as "global" value.
00329 temperature_t BME280::compensateTemperature(int32_t adc_T)
00330 {
00331   int32_t var1, var2, T;
00332   var1 = ((((adc_T>>3) - ((int32_t)_dig_T1<<1))) * ((int32_t)_dig_T2)) >> 11;
00333   var2 = (((((adc_T>>4) - ((int32_t)_dig_T1)) * ((adc_T>>4) - ((int32_t)_dig_T1))) >> 12) * ((int32_t)_dig_T3)) >> 14;
00334   _t_fine = var1 + var2;
00335   T = (_t_fine * 5 + 128) >> 8;
00336   return T;
00337 }
00338 
00339 
00340 // From the datasheet.
00341 // Returns pressure in Pa as unsigned 32 bit integer. Output value of 96386 equals 96386 Pa = 963.86 hPa
00342 pressure_t BME280::compensatePressure(int32_t adc_P)
00343 {
00344   int32_t var1, var2;
00345   uint32_t p;
00346   var1 = (((int32_t)_t_fine)>>1) - (int32_t)64000;
00347   var2 = (((var1>>2) * (var1>>2)) >> 11 ) * ((int32_t)_dig_P6);
00348   var2 = var2 + ((var1*((int32_t)_dig_P5))<<1);
00349   var2 = (var2>>2)+(((int32_t)_dig_P4)<<16);
00350   var1 = (((_dig_P3 * (((var1>>2) * (var1>>2)) >> 13 )) >> 3) + ((((int32_t)_dig_P2) * var1)>>1))>>18;
00351   var1 =((((32768+var1))*((int32_t)_dig_P1))>>15);
00352   if (var1 == 0)
00353   {
00354     return 0; // avoid exception caused by division by zero
00355   }
00356   p = (((uint32_t)(((int32_t)1048576)-adc_P)-(var2>>12)))*3125;
00357   if (p < 0x80000000)
00358   {
00359     p = (p << 1) / ((uint32_t)var1);
00360   }
00361   else
00362   {
00363     p = (p / (uint32_t)var1) * 2;
00364   }
00365   var1 = (((int32_t)_dig_P9) * ((int32_t)(((p>>3) * (p>>3))>>13)))>>12;
00366   var2 = (((int32_t)(p>>2)) * ((int32_t)_dig_P8))>>13;
00367   p = (uint32_t)((int32_t)p + ((var1 + var2 + _dig_P7) >> 4));
00368   return p;
00369 }
00370 
00371 
00372 // From the datasheet.
00373 // Returns humidity in %RH as unsigned 32 bit integer in Q22.10 format (22 integer and 10 fractional bits).
00374 // Output value of 47445 represents 47445/1024 = 46.333 %RH
00375 humidity_t BME280::compensateHumidity(int32_t adc_H)
00376 {
00377   int32_t v_x1_u32r;
00378   v_x1_u32r = (_t_fine - ((int32_t)76800));
00379   v_x1_u32r = (((((adc_H << 14) - (((int32_t)_dig_H4) << 20) - (((int32_t)_dig_H5) * v_x1_u32r)) +
00380       ((int32_t)16384)) >> 15) * (((((((v_x1_u32r * ((int32_t)_dig_H6)) >> 10) * (((v_x1_u32r *
00381       ((int32_t)_dig_H3)) >> 11) + ((int32_t)32768))) >> 10) + ((int32_t)2097152)) *
00382       ((int32_t)_dig_H2) + 8192) >> 14));
00383   v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((int32_t)_dig_H1)) >> 4));
00384   v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r);
00385   v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);
00386   return (uint32_t)(v_x1_u32r>>12);
00387 }
00388 
00389 #endif /* BME280_ALLOW_FLOAT */