diff options
| author | Yuval Adam <_@yuv.al> | 2026-03-17 11:49:42 +0100 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2026-03-17 11:49:42 +0100 |
| commit | 3a3d6ed1adc009cc6efee8e0b416137a8d17bb89 (patch) | |
| tree | ab509587b5368f76dd43031159d46a778691d2a9 | |
| parent | 2e79de0ad5024f6c0e53f1c10ce795c05cfb5416 (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.py | 51 |
1 files changed, 40 insertions, 11 deletions
@@ -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: |
