Files
ESP8266lib/ext/sens/BME280.h

308 lines
9.4 KiB
C++

#ifndef SENS_BME280
#define SENS_BME280
#include "../../Platforms.h"
#include "../../Debug.h"
// https://www.mouser.com/datasheet/2/783/BST-BME280-DS002-1509607.pdf
template <typename I2C> class BME280 {
I2C& i2c;
static constexpr const char* NAME = "BME280";
static constexpr uint8_t ADDR7_1 = 0b1110110; // 0x76
static constexpr uint8_t ADDR7_2 = 0b1110111; // 0x77
static constexpr uint8_t MODE_SLEEP = 0b00;
static constexpr uint8_t MODE_FORCED = 0b01; // manual sampling
static constexpr uint8_t MODE_NORMAL = 0b11; // periodic background sampling
uint8_t ADDR7;
static constexpr uint8_t REG_CTRL1 = 0xF2; // humidity
static constexpr uint8_t REG_CTRL2 = 0xF4; // temp, pressure, mode
static constexpr uint8_t REG_CONFIG = 0xF5;
static constexpr uint8_t REG_STATUS = 0xF3;
static constexpr uint8_t REG_PRESSURE = 0xF7;
static constexpr uint8_t REG_TEMPERATURE= 0xFA;
static constexpr uint8_t REG_HUMIDITY = 0xFD;
static constexpr uint8_t REG_DIG_T1 = 0x88;
static constexpr uint8_t REG_DIG_T2 = 0x8A;
static constexpr uint8_t REG_DIG_T3 = 0x8C;
bool started = false;
/** internal sensor calibration values */
struct Calibration {
uint16_t dig_T1 = 0;
int16_t dig_T2 = 0;
int16_t dig_T3 = 0;
uint16_t dig_P1 = 0;
int16_t dig_P2 = 0;
int16_t dig_P3 = 0;
int16_t dig_P4 = 0;
int16_t dig_P5 = 0;
int16_t dig_P6 = 0;
int16_t dig_P7 = 0;
int16_t dig_P8 = 0;
int16_t dig_P9 = 0;
uint8_t dig_H1 = 0;
int16_t dig_H2 = 0;
uint8_t dig_H3 = 0;
int16_t dig_H4 = 0;
int16_t dig_H5 = 0;
int8_t dig_H6 = 0;
} cal;
public:
struct Result {
float temp;
float humi;
float pres;
};
enum class Interval : uint8_t {
INT_500_US = 0b000,
INT_62_MS = 0b001,
INT_125_MS = 0b010,
INT_250_MS = 0b011,
INT_500_MS = 0b100,
INT_1000_MS = 0b101,
INT_10_MS = 0b110,
INT_20_MS = 0b111,
};
enum class Oversample : uint8_t {
X1 = 0b001,
X2 = 0b010,
X4 = 0b011,
X8 = 0b100,
X16 = 0b101,
};
private:
struct Config {
Oversample temp = Oversample::X2;
Oversample pres = Oversample::X16;
Oversample humi = Oversample::X2;
uint8_t mode = MODE_NORMAL;
Interval interval = Interval::INT_500_MS;
uint8_t fir = 0b000; // fir filter disabled
} cfg;
public:
BME280(I2C& i2c, uint8_t addrOffset = 0) : i2c(i2c), ADDR7(ADDR7_1 + addrOffset) {
}
bool isPresent() {
return i2c.query(ADDR7);
}
void init() {
readCalib();
}
void setSampling(Oversample temp, Oversample pres, Oversample humi) {
cfg.temp = temp;
cfg.pres = pres;
cfg.humi = humi;
}
uint8_t getStatus() {
uint8_t res[1];
i2c.readReg(ADDR7, REG_STATUS, 1, res);
return 0;
}
/** start periodic background measurement (tends to sensor-self-heating??) */
void measurePeriodic(Interval ival) {
cfg.mode = MODE_NORMAL;
cfg.interval = ival;
commitConfig();
}
/** measure only once, takes some time before NEW results are present (seems more stable in terms of temperature) */
void measureOnce() {
cfg.mode = MODE_FORCED;
commitConfig();
}
/** get the most recent readings */
Result getAll() {
Result res;
res.temp = getTemperature();
res.humi = getHumidity();
res.pres = getPressure();
return res;
}
/** get most recent pressure reading (hPa) */
float getPressure() {
uint8_t res[3];
i2c.readReg(ADDR7, REG_PRESSURE, 3, res);
//os_printf("res: %d - %d - %d \n", res[0], res[1], res[2]);
const uint32_t tmp = ((res[0] << 16) | (res[1] << 8) | (res[2] << 0)) >> 4;
const uint32_t pres = BME280_compensate_P_int64(tmp);
const float presF = pres / 256.0f / 100.0f; // convert from Q24.8 to float and from Pa to hPa
//const uint32_t p0 = pres / 256;
//const uint32_t p1 = (uint32_t) presF;
//const uint32_t p2 = (presF - p1) * 100000;
//Log::addInfo((NAME, "[pres] ADC: %d -> %d Pa | %d.%d hPa", tmp, p0, p1,p2);
return presF;
}
/** get most recent temperature reading */
float getTemperature() {
uint8_t res[3];
i2c.readReg(ADDR7, REG_TEMPERATURE, 3, res);
const uint32_t tmp = ((res[0] << 16) | (res[1] << 8) | (res[2] << 0)) >> 4;
const int32_t temp = BME280_compensate_T_int32(tmp);
const float tempF = temp / 100.0f;
return tempF;
}
/** get the most recent humidity reading */
float getHumidity() {
uint8_t res[2];
i2c.readReg(ADDR7, REG_HUMIDITY, 2, res);
//os_printf("res: %d - %d \n", res[0], res[1]);
const uint32_t tmp = (res[0] << 8) | (res[1] << 0);
const int32_t humi = bme280_compensate_H_int32(tmp);
const float humiF = humi / 1024.0f;
//const uint16_t h0 = humi / 1024;
//const uint16_t h1 = (uint16_t) humiF;
//const uint16_t h2 = (humiF - humi) * 10000;
//Log::addInfo((NAME, "[humi] ADC: %d -> %d -> %d.%d %%", tmp, h0, h1,h2);
return humiF;
}
private:
void readCalib() {
Log::addInfo(NAME, "readCalib()");
// read all 24 calibration bytes for temperature and pressure
uint8_t b1[24];
i2c.readReg(ADDR7, REG_DIG_T1, 24, b1);
cal.dig_T1 = (b1[1] << 8) | b1[0];
cal.dig_T2 = (b1[3] << 8) | b1[2];
cal.dig_T3 = (b1[5] << 8) | b1[4];
cal.dig_P1 = (b1[7] << 8) | b1[6];
cal.dig_P2 = (b1[9] << 8) | b1[8];
cal.dig_P3 = (b1[11] << 8) | b1[10];
cal.dig_P4 = (b1[13] << 8) | b1[12];
cal.dig_P5 = (b1[15] << 8) | b1[14];
cal.dig_P6 = (b1[17] << 8) | b1[16];
cal.dig_P7 = (b1[19] << 8) | b1[18];
cal.dig_P8 = (b1[21] << 8) | b1[20];
cal.dig_P9 = (b1[23] << 8) | b1[22];
// humidity
i2c.readReg(ADDR7, 0xA1, 1, &cal.dig_H1);
i2c.readReg(ADDR7, 0xE1, 7, b1);
cal.dig_H2 = (b1[1] << 8) | b1[0];
cal.dig_H3 = b1[2];
cal.dig_H4 = (b1[3] << 4) | (b1[4] & 0b00001111);
cal.dig_H5 = (b1[5] << 4) | ((b1[4] & 0b11110000) >> 4);
cal.dig_H6 = (b1[6]);
//os_printf("calib temp: %d %d %d\n", cal.dig_T1, cal.dig_T2, cal.dig_T3);
//os_printf("calib pres: %d %d %d %d %d %d %d %d %d\n", cal.dig_P1, cal.dig_P2, cal.dig_P3, cal.dig_P4, cal.dig_P5, cal.dig_P6, cal.dig_P7, cal.dig_P8, cal.dig_P9);
//os_printf("calib humi: %d %d %d %d %d %d\n", cal.dig_H1, cal.dig_H2, cal.dig_H3, cal.dig_H4, cal.dig_H5, cal.dig_H6);
Log::addInfo(NAME, "calTemp: %d %d %d", cal.dig_T1, cal.dig_T2, cal.dig_T3);
Log::addInfo(NAME, "calPres: %d %d %d %d %d %d %d %d %d", cal.dig_P1, cal.dig_P2, cal.dig_P3, cal.dig_P4, cal.dig_P5, cal.dig_P6, cal.dig_P7, cal.dig_P8, cal.dig_P9);
Log::addInfo(NAME, "calHumi: %d %d %d %d %d %d", cal.dig_H1, cal.dig_H2, cal.dig_H3, cal.dig_H4, cal.dig_H5, cal.dig_H6);
}
void commitConfig() {
const uint8_t ctrl1 = (uint8_t(cfg.humi) << 0);
const uint8_t ctrl2 = (uint8_t(cfg.temp) << 5) | (uint8_t(cfg.pres) << 2) | (uint8_t(cfg.mode) << 0);
const uint8_t conf = (uint8_t(cfg.interval) << 5) | (cfg.fir << 2);
i2c.writeReg8(ADDR7, REG_CTRL1, MODE_SLEEP);
i2c.writeReg8(ADDR7, REG_CTRL1, ctrl1);
i2c.writeReg8(ADDR7, REG_CTRL2, ctrl2);
i2c.writeReg8(ADDR7, REG_CONFIG, conf);
}
/** conversions from ADC values to real-world values. from Bosch BMP280 manual! */
using BME280_S32_t = int32_t;
using BME280_U32_t = uint32_t;
using BME280_S64_t = int64_t;
BME280_S32_t t_fine = 0;
// Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC.
// t_fine carries fine temperature as global value
BME280_S32_t BME280_compensate_T_int32(BME280_S32_t adc_T) {
BME280_S32_t var1, var2, T;
var1 = ((((adc_T>>3) - ((BME280_S32_t)cal.dig_T1<<1))) * ((BME280_S32_t)cal.dig_T2)) >> 11;
var2 = (((((adc_T>>4) - ((BME280_S32_t)cal.dig_T1)) * ((adc_T>>4) - ((BME280_S32_t)cal.dig_T1))) >> 12) * ((BME280_S32_t)cal.dig_T3)) >> 14;
t_fine = var1 + var2;
T = (t_fine * 5 + 128) >> 8;
return T;
}
// Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits).
// Output value of “24674867” represents 24674867/256 = 96386.2 Pa = 963.862 hPa
BME280_U32_t BME280_compensate_P_int64(BME280_S32_t adc_P) {
BME280_S64_t var1, var2, p;
var1 = ((BME280_S64_t)t_fine) - 128000;
var2 = var1 * var1 * (BME280_S64_t)cal.dig_P6;
var2 = var2 + ((var1*(BME280_S64_t)cal.dig_P5)<<17);
var2 = var2 + (((BME280_S64_t)cal.dig_P4)<<35);
var1 = ((var1 * var1 * (BME280_S64_t)cal.dig_P3)>>8) + ((var1 * (BME280_S64_t)cal.dig_P2)<<12);
var1 = (((((BME280_S64_t)1l)<<47)+var1))*((BME280_S64_t)cal.dig_P1)>>33;
if (var1 == 0) {return 0;} // avoid exception caused by division by zero
p = 1048576-adc_P;
p = (((p<<31)-var2)*3125)/var1;
var1 = (((BME280_S64_t)cal.dig_P9) * (p>>13) * (p>>13)) >> 25;
var2 = (((BME280_S64_t)cal.dig_P8) * p) >> 19;
p = ((p + var1 + var2) >> 8) + (((BME280_S64_t)cal.dig_P7)<<4);
return (BME280_U32_t)p;
}
// Returns humidity in %RH as unsigned 32 bit integer in Q22.10 format (22 integer and 10 fractional bits).
// Output value of “47445” represents 47445/1024 = 46.333 %RH
BME280_U32_t bme280_compensate_H_int32(BME280_S32_t adc_H) {
BME280_S32_t v_x1_u32r;
v_x1_u32r = (t_fine - ((BME280_S32_t)76800));
v_x1_u32r = (((((adc_H << 14) - (((BME280_S32_t)cal.dig_H4) << 20) - (((BME280_S32_t)cal.dig_H5) * v_x1_u32r)) +
((BME280_S32_t)16384)) >> 15) * (((((((v_x1_u32r * ((BME280_S32_t)cal.dig_H6)) >> 10) * (((v_x1_u32r *
((BME280_S32_t)cal.dig_H3)) >> 11) + ((BME280_S32_t)32768))) >> 10) + ((BME280_S32_t)2097152)) *
((BME280_S32_t)cal.dig_H2) + 8192) >> 14));
v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((BME280_S32_t)cal.dig_H1)) >>
4));
v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r);
v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);
return (BME280_U32_t)(v_x1_u32r>>12);
}
};
#endif