bme280.tc¶
BME280 Temperature, Humidity & Pressure Sensor Driver
// BME280 Temperature, Humidity & Pressure Sensor Driver
// I2C addresses: 0x76 (SDO=GND) or 0x77 (SDO=VCC)
// Scans both I2C buses, claims via Tasmota I2C system
// Reads every second, displays on web UI + JSON teleperiod
// Includes chart for 24h history
#define BME_ADDR1 0x76
#define BME_ADDR2 0x77
#define BME_ID 0x60
// Measurement results
float bme_temp = 0.0;
float bme_humi = 0.0;
float bme_pres = 0.0;
int bme_ok = 0;
int bme_addr = 0;
int bme_bus = 0;
char bme_lbl[32];
// I2C data buffer
char bme_buf[26];
// Calibration data (from registers 0x88..0xA1 and 0xE1..0xE7)
// Temperature
int dig_T1;
int dig_T2;
int dig_T3;
// Pressure
int dig_P1;
int dig_P2;
int dig_P3;
int dig_P4;
int dig_P5;
int dig_P6;
int dig_P7;
int dig_P8;
int dig_P9;
// Humidity
int dig_H1;
int dig_H2;
int dig_H3;
int dig_H4;
int dig_H5;
int dig_H6;
// t_fine is shared between temp and pressure/humidity compensation
int t_fine;
// Chart history (24h at 1 sample/min = 1440 points, use 360 = 6h)
#define CHART_LEN 360
float hist_temp[CHART_LEN];
float hist_humi[CHART_LEN];
float hist_pres[CHART_LEN];
int hist_pos;
int hist_tick;
// Sign-extend a 16-bit value to signed int
int sign16(int val) {
if (val >= 32768) {
return val - 65536;
}
return val;
}
// Sign-extend an 8-bit value to signed int
int sign8(int val) {
if (val >= 128) {
return val - 256;
}
return val;
}
// Read calibration data from sensor
int bme_read_calib() {
// Read 26 bytes from 0x88..0xA1 (temp + pressure calibration)
if (!i2cRead(bme_addr, 0x88, bme_buf, 26, bme_bus)) return 0;
dig_T1 = bme_buf[0] | (bme_buf[1] << 8);
dig_T2 = sign16(bme_buf[2] | (bme_buf[3] << 8));
dig_T3 = sign16(bme_buf[4] | (bme_buf[5] << 8));
dig_P1 = bme_buf[6] | (bme_buf[7] << 8);
dig_P2 = sign16(bme_buf[8] | (bme_buf[9] << 8));
dig_P3 = sign16(bme_buf[10] | (bme_buf[11] << 8));
dig_P4 = sign16(bme_buf[12] | (bme_buf[13] << 8));
dig_P5 = sign16(bme_buf[14] | (bme_buf[15] << 8));
dig_P6 = sign16(bme_buf[16] | (bme_buf[17] << 8));
dig_P7 = sign16(bme_buf[18] | (bme_buf[19] << 8));
dig_P8 = sign16(bme_buf[20] | (bme_buf[21] << 8));
dig_P9 = sign16(bme_buf[22] | (bme_buf[23] << 8));
// H1 at 0xA1
dig_H1 = i2cRead8(bme_addr, 0xA1, bme_bus);
// Read 7 bytes from 0xE1..0xE7 (humidity calibration)
if (!i2cRead(bme_addr, 0xE1, bme_buf, 7, bme_bus)) return 0;
dig_H2 = sign16(bme_buf[0] | (bme_buf[1] << 8));
dig_H3 = bme_buf[2];
dig_H4 = sign16((bme_buf[3] << 4) | (bme_buf[4] & 0x0F));
dig_H5 = sign16((bme_buf[5] << 4) | ((bme_buf[4] >> 4) & 0x0F));
dig_H6 = sign8(bme_buf[6]);
return 1;
}
// Configure sensor: oversampling x1 for all, normal mode, 1000ms standby
int bme_configure() {
// ctrl_hum (0xF2): humidity oversampling x1
if (!i2cWrite8(bme_addr, 0xF2, 0x01, bme_bus)) return 0;
// config (0xF5): standby 1000ms, filter off
if (!i2cWrite8(bme_addr, 0xF5, 0xA0, bme_bus)) return 0;
// ctrl_meas (0xF4): temp os x1, press os x1, normal mode
// must be written AFTER ctrl_hum to take effect
if (!i2cWrite8(bme_addr, 0xF4, 0x27, bme_bus)) return 0;
return 1;
}
// Scan both buses and addresses
int bme_scan() {
int bus = 0;
while (bus < 2) {
int addr = BME_ADDR1;
while (addr <= BME_ADDR2) {
if (i2cSetDevice(addr, bus)) {
// Check chip ID register (0xD0) — BME280 = 0x60
int id = i2cRead8(addr, 0xD0, bus);
if (id == BME_ID) {
bme_addr = addr;
bme_bus = bus;
i2cSetActiveFound(bme_addr, "BME280", bme_bus);
return 1;
}
}
addr++;
}
bus++;
}
return 0;
}
// Compensate temperature — returns °C * 100 as int, sets t_fine
// Using integer arithmetic from BME280 datasheet
int bme_comp_temp(int adc_T) {
int var1 = ((((adc_T >> 3) - (dig_T1 << 1))) * dig_T2) >> 11;
int var2 = (((((adc_T >> 4) - dig_T1) * ((adc_T >> 4) - dig_T1)) >> 12) * dig_T3) >> 14;
t_fine = var1 + var2;
int T = (t_fine * 5 + 128) >> 8;
return T;
}
// Compensate pressure — returns Pa as float
// Using float arithmetic to avoid 32-bit overflow issues
float bme_comp_pres(int adc_P) {
float var1 = (float)t_fine / 2.0 - 64000.0;
float var2 = var1 * var1 * (float)dig_P6 / 32768.0;
var2 = var2 + var1 * (float)dig_P5 * 2.0;
var2 = var2 / 4.0 + (float)dig_P4 * 65536.0;
var1 = ((float)dig_P3 * var1 * var1 / 524288.0 + (float)dig_P2 * var1) / 524288.0;
var1 = (1.0 + var1 / 32768.0) * (float)dig_P1;
if (var1 == 0.0) return 0.0;
float p = 1048576.0 - (float)adc_P;
p = (p - var2 / 4096.0) * 6250.0 / var1;
var1 = (float)dig_P9 * p * p / 2147483648.0;
var2 = p * (float)dig_P8 / 32768.0;
p = p + (var1 + var2 + (float)dig_P7) / 16.0;
return p;
}
// Compensate humidity — returns %RH as float
float bme_comp_humi(int adc_H) {
float h = (float)t_fine - 76800.0;
if (h == 0.0) return 0.0;
h = ((float)adc_H - ((float)dig_H4 * 64.0 + (float)dig_H5 / 16384.0 * h)) *
((float)dig_H2 / 65536.0 * (1.0 + (float)dig_H6 / 67108864.0 * h *
(1.0 + (float)dig_H3 / 67108864.0 * h)));
h = h * (1.0 - (float)dig_H1 * h / 524288.0);
if (h > 100.0) h = 100.0;
if (h < 0.0) h = 0.0;
return h;
}
void EverySecond() {
if (!bme_addr) {
if (!bme_scan()) {
bme_ok = 0;
return;
}
if (!bme_read_calib()) {
bme_ok = 0;
bme_addr = 0;
return;
}
if (!bme_configure()) {
bme_ok = 0;
bme_addr = 0;
return;
}
}
// Read 8 bytes from 0xF7: press[3], temp[3], humi[2]
if (!i2cRead(bme_addr, 0xF7, bme_buf, 8, bme_bus)) {
bme_ok = 0;
bme_addr = 0;
return;
}
int adc_P = (bme_buf[0] << 12) | (bme_buf[1] << 4) | (bme_buf[2] >> 4);
int adc_T = (bme_buf[3] << 12) | (bme_buf[4] << 4) | (bme_buf[5] >> 4);
int adc_H = (bme_buf[6] << 8) | bme_buf[7];
// Temperature (sets t_fine used by pressure/humidity)
int T100 = bme_comp_temp(adc_T);
bme_temp = (float)T100 / 100.0;
// Pressure in hPa
bme_pres = bme_comp_pres(adc_P) / 100.0;
// Humidity %RH
bme_humi = bme_comp_humi(adc_H);
bme_ok = 1;
// Update chart history every minute
hist_tick++;
if (hist_tick >= 60) {
hist_tick = 0;
hist_temp[hist_pos % CHART_LEN] = bme_temp;
hist_humi[hist_pos % CHART_LEN] = bme_humi;
hist_pres[hist_pos % CHART_LEN] = bme_pres;
hist_pos++;
}
}
// Tasmota main page — sensor values + charts
void WebPage() {
int n = hist_pos;
if (n > CHART_LEN) n = CHART_LEN;
if (n > 0) {
WebChart('l', "Temperature", "\u00b0C", 0xe74c3c, hist_pos, CHART_LEN, hist_temp, 1, 0, 0, 0);
WebChart('l', "Humidity", "%RH", 0x3498db, hist_pos, CHART_LEN, hist_humi, 1, 0, 0, 0);
WebChart('l', "Pressure", "hPa", 0x27ae60, hist_pos, CHART_LEN, hist_pres, 1, 0, 0, 0);
}
}
// Tasmota web UI — sensor rows
void bme_web_label(int idx) {
char vt[32];
LGetString(idx, bme_lbl);
strcpy(vt, "{s}BME280 ");
strcat(vt, bme_lbl);
strcat(vt, "{m}");
webSend(vt);
}
void WebCall() {
char buf[32];
if (bme_ok) {
bme_web_label(0);
sprintf(buf, "%.1f °C{e}", bme_temp);
webSend(buf);
bme_web_label(1);
sprintf(buf, "%.1f %{e}", bme_humi);
webSend(buf);
bme_web_label(2);
sprintf(buf, "%.1f hPa{e}", bme_pres);
webSend(buf);
} else {
webSend("{s}BME280{m}not found{e}");
}
}
// JSON teleperiod — appears in MQTT sensor payload
void JsonCall() {
if (!bme_ok) return;
char buf[96];
sprintf(buf, ",\"BME280\":{\"Temperature\":%.1f", bme_temp);
responseAppend(buf);
sprintf(buf, ",\"Humidity\":%.1f", bme_humi);
responseAppend(buf);
sprintf(buf, ",\"Pressure\":%.1f}", bme_pres);
responseAppend(buf);
}
void OnExit() {
if (bme_addr) {
I2cResetActive(bme_addr, bme_bus);
bme_addr = 0;
}
}
int main() {
char buf[64];
bme_ok = 0;
bme_addr = 0;
hist_pos = 0;
hist_tick = 0;
if (bme_scan()) {
sprintf(buf, "BME280 found at 0x%x on bus %d", bme_addr, bme_bus);
addLog(buf);
if (bme_read_calib() && bme_configure()) {
addLog("BME280 calibration loaded, sensor active");
} else {
addLog("BME280 calibration/config failed");
bme_addr = 0;
}
} else {
addLog("BME280 not found on any bus");
}
return 0;
}