summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2026-03-17 11:49:42 +0100
committerYuval Adam <_@yuv.al>2026-03-17 11:49:42 +0100
commit3a3d6ed1adc009cc6efee8e0b416137a8d17bb89 (patch)
treeab509587b5368f76dd43031159d46a778691d2a9
parent2e79de0ad5024f6c0e53f1c10ce795c05cfb5416 (diff)
Fix 5 bugs in BME280.py verified against Bosch BME280_SensorAPI and Adafruit references
1. get_power_mode() missing parentheses in set_oversamp_pressure and set_oversamp_temperature — compared method object instead of calling it, so sleep-mode check never worked correctly. 2. set_oversamp_humidity else branch wrote ctrl_hum_reg into CTRL_MEAS register instead of ctrl_meas_reg (per datasheet §5.4.3, humidity settings require a subsequent write to ctrl_meas with the correct value). 3. dig_H6 stored as unsigned byte — should be int8_t per Bosch bme280_defs.h and Adafruit_BME280.h. 4. dig_H4/H5 sign extension wrong — used to_s16() on 12-bit signed values. Now sign-extends from MSB byte first (to_s8) then combines with LSB nibble, matching Bosch parse_humidity_calib_data() and Adafruit readCoefficients(). 5. Humidity compensation formula was missing var5 multiplication factor vs Bosch compensate_humidity() (BME280_DOUBLE_ENABLE path). Rewritten with Bosch variable names for easy cross-referencing.
-rw-r--r--BME280.py51
1 files changed, 40 insertions, 11 deletions
diff --git a/BME280.py b/BME280.py
index af9afac..8628eb2 100644
--- a/BME280.py
+++ b/BME280.py
@@ -2,6 +2,7 @@ import time
import smbus
to_s16 = lambda x: (x + 2**15) % 2**16 - 2**15
+to_s8 = lambda x: (x + 2**7) % 2**8 - 2**7
to_u16 = lambda x: x % 2**16
BME280_I2C_ADDR = 0x76
@@ -119,9 +120,19 @@ class BME280:
params = self.read(cali_regs['BME280_HUMIDITY_CALIB_DIG_H2_LSB_REG'], 7)
self.param['dig_H2'] = to_s16((params[1] << 8) | params[0])
self.param['dig_H3'] = params[2]
- self.param['dig_H4'] = to_s16((params[3] << 4) | (params[4] & 0x0f))
- self.param['dig_H5'] = to_s16((params[5] << 4) | (params[4] >> 4))
- self.param['dig_H6'] = params[6]
+ # FIX: dig_H4/H5 are 12-bit signed values. Must sign-extend from the
+ # MSB byte first, then combine with the LSB nibble — matching the Bosch
+ # BME280_SensorAPI parse_humidity_calib_data() which does:
+ # dig_h4_msb = (int16_t)(int8_t)reg_data[3] * 16;
+ # dig_h4 = dig_h4_msb | (reg_data[4] & 0x0F);
+ # The old code used to_s16() which only sign-extends at the 16-bit
+ # boundary, giving wrong results when the 12-bit value is negative.
+ self.param['dig_H4'] = (to_s8(params[3]) << 4) | (params[4] & 0x0f)
+ self.param['dig_H5'] = (to_s8(params[5]) << 4) | (params[4] >> 4)
+ # FIX: dig_H6 is int8_t per Bosch API (bme280_defs.h) and Adafruit.
+ # Was stored unsigned, which gives wrong humidity for sensors where
+ # this calibration coefficient is negative.
+ self.param['dig_H6'] = to_s8(params[6])
def get_power_mode(self):
return self.read(regs['BME280_CTRL_MEAS_REG'], 1)[0] & 0x03
@@ -160,7 +171,12 @@ class BME280:
self.write(regs['BME280_CTRL_MEAS_REG'], [self.ctrl_meas_reg])
else:
self.write(regs['BME280_CTRL_HUMIDITY_REG'], [v_data])
- self.write(regs['BME280_CTRL_MEAS_REG'], [self.ctrl_hum_reg])
+ # FIX: Was writing ctrl_hum_reg into CTRL_MEAS register (wrong
+ # value in wrong register). Per datasheet section 5.4.3, humidity
+ # settings only take effect after a write to ctrl_meas — but we
+ # must write the actual ctrl_meas value, not the humidity one.
+ # Matches Adafruit setSampling() and Bosch set_osr_humidity_settings().
+ self.write(regs['BME280_CTRL_MEAS_REG'], [self.ctrl_meas_reg])
self.oversamp_humidity = sampling
self.ctrl_meas_reg = self.read(regs['BME280_CTRL_MEAS_REG'], 1)[0]
@@ -170,7 +186,11 @@ class BME280:
def set_oversamp_pressure(self, sampling):
v_data = (self.ctrl_meas_reg & ~0x1c) | ((sampling << 2) & 0x1c)
- if self.get_power_mode != power_mode['BME280_SLEEP_MODE']:
+ # FIX: Was missing () on get_power_mode — compared the method object
+ # (always truthy) to an int, so the sleep-mode check never worked.
+ # Matches set_power_mode() and set_oversamp_humidity() which both
+ # correctly call self.get_power_mode().
+ if self.get_power_mode() != power_mode['BME280_SLEEP_MODE']:
self.soft_rst()
time.sleep(0.003)
self.write(regs['BME280_CONFIG_REG'], [self.config_reg])
@@ -187,7 +207,8 @@ class BME280:
def set_oversamp_temperature(self, sampling):
v_data = (self.ctrl_meas_reg & ~0xe0) | ((sampling << 5) & 0xe0)
- if self.get_power_mode != power_mode['BME280_SLEEP_MODE']:
+ # FIX: Same missing () bug as set_oversamp_pressure — see above.
+ if self.get_power_mode() != power_mode['BME280_SLEEP_MODE']:
self.soft_rst()
time.sleep(0.003)
self.write(regs['BME280_CONFIG_REG'], [self.config_reg])
@@ -217,11 +238,19 @@ class BME280:
return (x1 >> 12) / 1024.0
"""
- h = float(self.t_fine) - 76800.0
- h = (raw_val - (float(self.param['dig_H4']) * 64.0 + float(self.param['dig_H5']) / 16384.0 * h)) * (
- float(self.param['dig_H2']) / 65536.0 * (1.0 + float(self.param['dig_H6']) / 67108864.0 * h * (
- 1.0 + float(self.param['dig_H3']) / 67108864.0 * h)))
- h = h * (1.0 - float(self.param['dig_H1']) * h / 524288.0)
+ # FIX: Humidity compensation rewritten to match Bosch BME280_SensorAPI
+ # compensate_humidity() (BME280_DOUBLE_ENABLE path in bme280.c).
+ # The old code was missing the var5 multiplication factor, producing
+ # incorrect humidity. The formula below uses the same variable names
+ # as the Bosch reference for easy cross-referencing.
+ var1 = float(self.t_fine) - 76800.0
+ var2 = float(self.param['dig_H4']) * 64.0 + (float(self.param['dig_H5']) / 16384.0) * var1
+ var3 = raw_val - var2
+ var4 = float(self.param['dig_H2']) / 65536.0
+ var5 = 1.0 + (float(self.param['dig_H3']) / 67108864.0) * var1
+ var6 = 1.0 + (float(self.param['dig_H6']) / 67108864.0) * var1 * var5
+ var6 = var3 * var4 * (var5 * var6)
+ h = var6 * (1.0 - float(self.param['dig_H1']) * var6 / 524288.0)
if h > 100:
h = 100
elif h < 0: