bme280: Add support for BMP280 and BME680 sensors (#4040)

This adds support for BMP280 and BME680 sensor ICs,
along with fixing calibration data readout for BME280.

Gas sensor readout for the BME680 is just the raw compensated value.
To get actual meaningful values, more research is needed.

Signed-off-by: Michael Kurz <michi.kurz@gmail.com>
This commit is contained in:
Michael Kurz 2021-03-15 16:50:29 +01:00 committed by GitHub
parent 86a924f5a6
commit b4437f8eee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 344 additions and 74 deletions

View File

@ -117,7 +117,7 @@ gcode:
RESTORE_GCODE_STATE NAME=M600_state RESTORE_GCODE_STATE NAME=M600_state
###################################################################### ######################################################################
# BME280 Environmental Sensor # BMP280/BME280/BME680 Environmental Sensor
###################################################################### ######################################################################
# The macro below assumes you have a BME280 sensor_type defined in one # The macro below assumes you have a BME280 sensor_type defined in one

View File

@ -264,7 +264,8 @@ The following are common printer attributes:
the config file if a `SET_RETRACTION` command alters them. the config file if a `SET_RETRACTION` command alters them.
- `printer["bme280 <sensor_name>"].temperature`, - `printer["bme280 <sensor_name>"].temperature`,
`printer["bme280 <sensor_name>"].humidity`, `printer["bme280 <sensor_name>"].humidity`,
`printer["bme280 <sensor_name>"].pressure`: The last read values `printer["bme280 <sensor_name>"].pressure`,
`printer["bme280 <sensor_name>"].gas`: The last read values
from the sensor. from the sensor.
- `printer["htu21d <sensor_name>"].temperature`, - `printer["htu21d <sensor_name>"].temperature`,
`printer["htu21d <sensor_name>"].humidity`: The last read values `printer["htu21d <sensor_name>"].humidity`: The last read values

View File

@ -2039,13 +2039,13 @@ sensor_pin:
# name in the above list. # name in the above list.
``` ```
## BME280 temperature sensor ## BMP280/BME280/BME680 temperature sensor
BME280 two wire interface (I2C) environmental sensor. Note that this BMP280/BME280/BME680 two wire interface (I2C) environmental sensors.
sensor is not intended for use with extruders and heater beds, but Note that thoose sensors aee not intended for use with extruders and
rather for monitoring ambient temperature (C), pressure (hPa), and heater beds, but rather for monitoring ambient temperature (C),
relative humidity. See pressure (hPa), relative humidity and in case of the BME680 gas level.
[sample-macros.cfg](../config/sample-macros.cfg) for a gcode_macro See [sample-macros.cfg](../config/sample-macros.cfg) for a gcode_macro
that may be used to report pressure and humidity in addition to that may be used to report pressure and humidity in addition to
temperature. temperature.

View File

@ -9,16 +9,77 @@ from . import bus
REPORT_TIME = .8 REPORT_TIME = .8
BME280_CHIP_ADDR = 0x76 BME280_CHIP_ADDR = 0x76
BME280_REGS = { BME280_REGS = {
'CHIP_ID': 0xD0, 'RESET': 0xE0, 'CTRL_HUM': 0xF2, 'RESET': 0xE0, 'CTRL_HUM': 0xF2,
'STATUS': 0xF3, 'CTRL_MEAS': 0xF4, 'CONFIG': 0xF5, 'STATUS': 0xF3, 'CTRL_MEAS': 0xF4, 'CONFIG': 0xF5,
'PRESSURE_MSB': 0xF7, 'PRESSURE_LSB': 0xF8, 'PRESSURE_XLSB': 0xF9, 'PRESSURE_MSB': 0xF7, 'PRESSURE_LSB': 0xF8, 'PRESSURE_XLSB': 0xF9,
'TEMP_MSB': 0xFA, 'TEMP_LSB': 0xFB, 'TEMP_XLSB': 0xFC, 'TEMP_MSB': 0xFA, 'TEMP_LSB': 0xFB, 'TEMP_XLSB': 0xFC,
'HUM_MSB': 0xFD, 'HUM_LSB': 0xFE, 'CAL_1': 0x88, 'CAL_2': 0xE1 'HUM_MSB': 0xFD, 'HUM_LSB': 0xFE, 'CAL_1': 0x88, 'CAL_2': 0xE1
} }
# BME default settings
BME680_REGS = {
'RESET': 0xE0, 'CTRL_HUM': 0x72, 'CTRL_GAS_1': 0x71, 'CTRL_GAS_0': 0x70,
'GAS_WAIT_0': 0x64, 'RES_HEAT_0': 0x5A, 'IDAC_HEAT_0': 0x50,
'STATUS': 0x73, 'EAS_STATUS_0': 0x1D, 'CTRL_MEAS': 0x74, 'CONFIG': 0x75,
'GAS_R_LSB': 0x2B, 'GAS_R_MSB': 0x2A,
'PRESSURE_MSB': 0x1F, 'PRESSURE_LSB': 0x20, 'PRESSURE_XLSB': 0x21,
'TEMP_MSB': 0x22, 'TEMP_LSB': 0x23, 'TEMP_XLSB': 0x24,
'HUM_MSB': 0x25, 'HUM_LSB': 0x26, 'CAL_1': 0x88, 'CAL_2': 0xE1,
'RES_HEAT_VAL': 0x00, 'RES_HEAT_RANGE': 0x02, 'RANGE_SWITCHING_ERROR': 0x04
}
BME680_GAS_CONSTANTS = {
0: (1., 8000000.),
1: (1., 4000000.),
2: (1., 2000000.),
3: (1., 1000000.),
4: (1., 499500.4995),
5: (0.99, 248262.1648),
6: (1., 125000.),
7: (0.992, 63004.03226),
8: (1., 31281.28128),
9: (1., 15625.),
10: (0.998, 7812.5),
11: (0.995, 3906.25),
12: (1., 1953.125),
13: (0.99, 976.5625),
14: (1., 488.28125),
15: (1., 244.140625)
}
STATUS_MEASURING = 1 << 3 STATUS_MEASURING = 1 << 3
STATUS_IM_UPDATE = 1 STATUS_IM_UPDATE = 1
MODE = 1 MODE = 1
RUN_GAS = 1 << 4
NB_CONV_0 = 0
EAS_NEW_DATA = 1 << 7
GAS_DONE = 1 << 6
MEASURE_DONE = 1 << 5
RESET_CHIP_VALUE = 0xB6
BME_CHIPS = {
0x58: 'BMP280', 0x60: 'BME280', 0x61: 'BME680'
}
BME_CHIP_ID_REG = 0xD0
def get_twos_complement(val, bit_size):
if val & (1 << (bit_size - 1)):
val -= (1 << bit_size)
return val
def get_unsigned_short(bits):
return bits[1] << 8 | bits[0]
def get_signed_short(bits):
val = get_unsigned_short(bits)
return get_twos_complement(val, 16)
def get_signed_byte(bits):
return get_twos_complement(bits, 8)
class BME280: class BME280:
def __init__(self, config): def __init__(self, config):
@ -28,16 +89,23 @@ class BME280:
self.i2c = bus.MCU_I2C_from_config( self.i2c = bus.MCU_I2C_from_config(
config, default_addr=BME280_CHIP_ADDR, default_speed=100000) config, default_addr=BME280_CHIP_ADDR, default_speed=100000)
self.mcu = self.i2c.get_mcu() self.mcu = self.i2c.get_mcu()
self.iir_filter = config.getint('bme280_iir_filter', 1)
self.os_temp = config.getint('bme280_oversample_temp', 2) self.os_temp = config.getint('bme280_oversample_temp', 2)
self.os_hum = config.getint('bme280_oversample_hum', 2) self.os_hum = config.getint('bme280_oversample_hum', 2)
self.os_pres = config.getint('bme280_oversample_pressure', 2) self.os_pres = config.getint('bme280_oversample_pressure', 2)
self.temp = self.pressure = self.humidity = self.t_fine = 0. self.gas_heat_temp = config.getint('bme280_gas_target_temp', 320)
self.min_temp = self.max_temp = 0. self.gas_heat_duration = config.getint('bme280_gas_heat_duration', 150)
self.max_sample_time = \ logging.info("BMxx80: Oversampling: Temp %dx Humid %dx Pressure %dx" % (
(1.25 + (2.3 * self.os_temp) + ((2.3 * self.os_pres) + pow(2, self.os_temp - 1), pow(2, self.os_hum - 1),
.575) + ((2.3 * self.os_hum) + .575)) / 1000 pow(2, self.os_pres - 1)))
self.dig = None logging.info("BMxx80: IIR: %dx" % (pow(2, self.iir_filter) - 1))
self.sample_timer = self.reactor.register_timer(self._sample_bme280)
self.temp = self.pressure = self.humidity = self.gas = self.t_fine = 0.
self.min_temp = self.max_temp = self.range_switching_error = 0.
self.max_sample_time = None
self.dig = self.sample_timer = None
self.chip_type = 'BMP280'
self.chip_registers = BME280_REGS
self.printer.add_object("bme280 " + self.name, self) self.printer.add_object("bme280 " + self.name, self)
if self.printer.get_start_args().get('debugoutput') is not None: if self.printer.get_start_args().get('debugoutput') is not None:
return return
@ -45,7 +113,7 @@ class BME280:
self.handle_connect) self.handle_connect)
def handle_connect(self): def handle_connect(self):
self._init_bme280() self._init_bmxx80()
self.reactor.update_timer(self.sample_timer, self.reactor.NOW) self.reactor.update_timer(self.sample_timer, self.reactor.NOW)
def setup_minmax(self, min_temp, max_temp): def setup_minmax(self, min_temp, max_temp):
@ -58,25 +126,79 @@ class BME280:
def get_report_time_delta(self): def get_report_time_delta(self):
return REPORT_TIME return REPORT_TIME
def _init_bme280(self): def _init_bmxx80(self):
def get_twos_complement(val, bit_size): def read_calibration_data_bmp280(calib_data_1):
if val & (1 << (bit_size - 1)): dig = {}
val -= (1 << bit_size) dig['T1'] = get_unsigned_short(calib_data_1[0:2])
return val dig['T2'] = get_signed_short(calib_data_1[2:4])
dig['T3'] = get_signed_short(calib_data_1[4:6])
def get_unsigned_short(bits): dig['P1'] = get_unsigned_short(calib_data_1[6:8])
return bits[1] << 8 | bits[0] dig['P2'] = get_signed_short(calib_data_1[8:10])
dig['P3'] = get_signed_short(calib_data_1[10:12])
dig['P4'] = get_signed_short(calib_data_1[12:14])
dig['P5'] = get_signed_short(calib_data_1[14:16])
dig['P6'] = get_signed_short(calib_data_1[16:18])
dig['P7'] = get_signed_short(calib_data_1[18:20])
dig['P8'] = get_signed_short(calib_data_1[20:22])
dig['P9'] = get_signed_short(calib_data_1[22:24])
return dig
def get_signed_short(bits): def read_calibration_data_bme280(calib_data_1, calib_data_2):
val = get_unsigned_short(bits) dig = read_calibration_data_bmp280(calib_data_1)
return get_twos_complement(val, 16) dig['H1'] = calib_data_1[25] & 0xFF
dig['H2'] = get_signed_short(calib_data_2[0:2])
dig['H3'] = calib_data_2[2] & 0xFF
dig['H4'] = get_twos_complement(
(calib_data_2[3] << 4) | (calib_data_2[4] & 0x0F), 12)
dig['H5'] = get_twos_complement(
(calib_data_2[5] << 4) | ((calib_data_2[4] & 0xF0) >> 4), 12)
dig['H6'] = get_twos_complement(calib_data_2[6], 8)
return dig
# Check the chip ID, should be 0x60 def read_calibration_data_bme680(calib_data_1, calib_data_2):
chip_id = self.read_register('CHIP_ID', 1)[0] dig = {}
if chip_id != 0x60: dig['T1'] = get_unsigned_short(calib_data_2[8:10])
logging.info( dig['T2'] = get_signed_short(calib_data_1[2:4])
"bme280: Chip ID mismatch, expected 0x60, received %#x" dig['T3'] = get_signed_byte(calib_data_1[4])
% (chip_id))
dig['P1'] = get_unsigned_short(calib_data_1[6:8])
dig['P2'] = get_signed_short(calib_data_1[8:10])
dig['P3'] = calib_data_1[10]
dig['P4'] = get_signed_short(calib_data_1[12:14])
dig['P5'] = get_signed_short(calib_data_1[14:16])
dig['P6'] = get_signed_byte(calib_data_1[17])
dig['P7'] = get_signed_byte(calib_data_1[16])
dig['P8'] = get_signed_short(calib_data_1[20:22])
dig['P9'] = get_signed_short(calib_data_1[22:24])
dig['P10'] = calib_data_1[24]
dig['H1'] = get_twos_complement(
(calib_data_2[2] << 4) | (calib_data_2[1] & 0x0F), 12)
dig['H2'] = get_twos_complement(
(calib_data_2[0] << 4) | ((calib_data_2[1] & 0xF0) >> 4), 12)
dig['H3'] = get_signed_byte(calib_data_2[3])
dig['H4'] = get_signed_byte(calib_data_2[4])
dig['H5'] = get_signed_byte(calib_data_2[5])
dig['H6'] = calib_data_2[6]
dig['H7'] = get_signed_byte(calib_data_2[7])
dig['G1'] = get_signed_byte(calib_data_2[12])
dig['G2'] = get_signed_short(calib_data_2[10:12])
dig['G3'] = get_signed_byte(calib_data_2[13])
return dig
chip_id = self.read_id()
if chip_id not in BME_CHIPS.keys():
logging.info("bme280: Unknown Chip ID received %#x" % chip_id)
else:
self.chip_type = BME_CHIPS[chip_id]
logging.info("bme280: Found Chip %s at %#x" % (
self.chip_type, self.i2c.i2c_address))
# Reset chip
self.write_register('RESET', [RESET_CHIP_VALUE])
self.reactor.pause(self.reactor.monotonic() + .5)
# Make sure non-volatile memory has been copied to registers # Make sure non-volatile memory has been copied to registers
status = self.read_register('STATUS', 1)[0] status = self.read_register('STATUS', 1)[0]
@ -84,34 +206,33 @@ class BME280:
self.reactor.pause(self.reactor.monotonic() + .01) self.reactor.pause(self.reactor.monotonic() + .01)
status = self.read_register('STATUS', 1)[0] status = self.read_register('STATUS', 1)[0]
c1 = self.read_register('CAL_1', 26) if self.chip_type == 'BME680':
c2 = self.read_register('CAL_2', 7) self.max_sample_time = 0.5
self.sample_timer = self.reactor.register_timer(self._sample_bme680)
self.chip_registers = BME680_REGS
else:
self.max_sample_time = \
(1.25 + (2.3 * self.os_temp) + ((2.3 * self.os_pres) + .575)
+ ((2.3 * self.os_hum) + .575)) / 1000
self.sample_timer = self.reactor.register_timer(self._sample_bme280)
self.chip_registers = BME280_REGS
if self.chip_type in ('BME680', 'BME280'):
self.write_register('CONFIG', (self.iir_filter & 0x07) << 2)
# Read out and calculate the trimming parameters # Read out and calculate the trimming parameters
dig = {} cal_1 = self.read_register('CAL_1', 26)
unsigned_keys = ['T1', 'P1'] cal_2 = self.read_register('CAL_2', 16)
idx = 0 if self.chip_type == 'BME280':
for cnt, prefix in [(3, 'T'), (9, 'P')]: self.dig = read_calibration_data_bme280(cal_1, cal_2)
for i in range(cnt): elif self.chip_type == 'BMP280':
key = prefix + str(i + 1) self.dig = read_calibration_data_bmp280(cal_1)
if key in unsigned_keys: elif self.chip_type == 'BME680':
dig[key] = get_unsigned_short(c1[idx:idx+2]) self.dig = read_calibration_data_bme680(cal_1, cal_2)
else:
dig[key] = get_signed_short(c1[idx:idx+2])
idx += 2
dig['H1'] = c1[25] & 0xFF
dig['H2'] = get_signed_short(c2[0:2])
dig['H3'] = c2[2] & 0xFF
dig['H4'] = get_twos_complement(
((c2[3] << 4) & 0xFF0) | (c2[4] & 0x0F), 12)
dig['H5'] = get_twos_complement(
(c2[4] & 0x0F) | ((c2[5] << 4) & 0xFF0), 12)
dig['H6'] = get_twos_complement(c2[6], 8)
self.dig = dig
def _sample_bme280(self, eventtime): def _sample_bme280(self, eventtime):
# Enter forced mode # Enter forced mode
if self.chip_type == 'BME280':
self.write_register('CTRL_HUM', self.os_hum) self.write_register('CTRL_HUM', self.os_hum)
meas = self.os_temp << 5 | self.os_pres << 2 | MODE meas = self.os_temp << 5 | self.os_pres << 2 | MODE
self.write_register('CTRL_MEAS', meas) self.write_register('CTRL_MEAS', meas)
@ -124,19 +245,24 @@ class BME280:
self.reactor.monotonic() + self.max_sample_time) self.reactor.monotonic() + self.max_sample_time)
status = self.read_register('STATUS', 1)[0] status = self.read_register('STATUS', 1)[0]
if self.chip_type == 'BME280':
data = self.read_register('PRESSURE_MSB', 8) data = self.read_register('PRESSURE_MSB', 8)
elif self.chip_type == 'BMP280':
data = self.read_register('PRESSURE_MSB', 6)
else:
return self.reactor.NEVER
except Exception: except Exception:
logging.exception("BME280: Error reading data") logging.exception("BME280: Error reading data")
self.temp = self.pressure = self.humidity = .0 self.temp = self.pressure = self.humidity = .0
return self.reactor.NEVER return self.reactor.NEVER
pressure_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4) temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4)
humid_raw = (data[6] << 8) | data[7]
self.temp = self._compensate_temp(temp_raw) self.temp = self._compensate_temp(temp_raw)
self.pressure = self._compensate_pressure(pressure_raw) / 100. pressure_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
self.humidity = self._compensate_humidity(humid_raw) self.pressure = self._compensate_pressure_bme280(pressure_raw) / 100.
if self.chip_type == 'BME280':
humid_raw = (data[6] << 8) | data[7]
self.humidity = self._compensate_humidity_bme280(humid_raw)
if self.temp < self.min_temp or self.temp > self.max_temp: if self.temp < self.min_temp or self.temp > self.max_temp:
self.printer.invoke_shutdown( self.printer.invoke_shutdown(
"BME280 temperature %0.1f outside range of %0.1f:%.01f" "BME280 temperature %0.1f outside range of %0.1f:%.01f"
@ -145,6 +271,69 @@ class BME280:
self._callback(self.mcu.estimated_print_time(measured_time), self.temp) self._callback(self.mcu.estimated_print_time(measured_time), self.temp)
return measured_time + REPORT_TIME return measured_time + REPORT_TIME
def _sample_bme680(self, eventtime):
self.write_register('CTRL_HUM', self.os_hum & 0x07)
meas = self.os_temp << 5 | self.os_pres << 2
self.write_register('CTRL_MEAS', [meas])
gas_wait_0 = self._calculate_gas_heater_duration(self.gas_heat_duration)
self.write_register('GAS_WAIT_0', [gas_wait_0])
res_heat_0 = self._calculate_gas_heater_resistance(self.gas_heat_temp)
self.write_register('RES_HEAT_0', [res_heat_0])
gas_config = RUN_GAS | NB_CONV_0
self.write_register('CTRL_GAS_1', [gas_config])
def data_ready(stat):
new_data = (stat & EAS_NEW_DATA)
gas_done = not (stat & GAS_DONE)
meas_done = not (stat & MEASURE_DONE)
return new_data and gas_done and meas_done
# Enter forced mode
meas = meas | MODE
self.write_register('CTRL_MEAS', meas)
try:
# wait until results are ready
status = self.read_register('EAS_STATUS_0', 1)[0]
while not data_ready(status):
self.reactor.pause(
self.reactor.monotonic() + self.max_sample_time)
status = self.read_register('EAS_STATUS_0', 1)[0]
data = self.read_register('PRESSURE_MSB', 8)
gas_data = self.read_register('GAS_R_MSB', 2)
except Exception:
logging.exception("BME680: Error reading data")
self.temp = self.pressure = self.humidity = self.gas = .0
return self.reactor.NEVER
temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4)
if temp_raw != 0x80000:
self.temp = self._compensate_temp(temp_raw)
pressure_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
if pressure_raw != 0x80000:
self.pressure = self._compensate_pressure_bme680(
pressure_raw) / 100.
humid_raw = (data[6] << 8) | data[7]
self.humidity = self._compensate_humidity_bme680(humid_raw)
gas_valid = ((gas_data[1] & 0x20) == 0x20)
if gas_valid:
gas_heater_stable = ((gas_data[1] & 0x10) == 0x10)
if not gas_heater_stable:
logging.warning("BME680: Gas heater didn't reach target")
gas_raw = (gas_data[0] << 2) | ((gas_data[1] & 0xC0) >> 6)
gas_range = (gas_data[1] & 0x0F)
self.gas = self._compensate_gas(gas_raw, gas_range)
if self.temp < self.min_temp or self.temp > self.max_temp:
self.printer.invoke_shutdown(
"BME680 temperature %0.1f outside range of %0.1f:%.01f"
% (self.temp, self.min_temp, self.max_temp))
measured_time = self.reactor.monotonic()
self._callback(self.mcu.estimated_print_time(measured_time), self.temp)
return measured_time + REPORT_TIME * 4
def _compensate_temp(self, raw_temp): def _compensate_temp(self, raw_temp):
dig = self.dig dig = self.dig
var1 = ((raw_temp / 16384. - (dig['T1'] / 1024.)) * dig['T2']) var1 = ((raw_temp / 16384. - (dig['T1'] / 1024.)) * dig['T2'])
@ -154,7 +343,7 @@ class BME280:
self.t_fine = var1 + var2 self.t_fine = var1 + var2
return self.t_fine / 5120.0 return self.t_fine / 5120.0
def _compensate_pressure(self, raw_pressure): def _compensate_pressure_bme280(self, raw_pressure):
dig = self.dig dig = self.dig
t_fine = self.t_fine t_fine = self.t_fine
var1 = t_fine / 2. - 64000. var1 = t_fine / 2. - 64000.
@ -172,37 +361,117 @@ class BME280:
var2 = pressure * dig['P8'] / 32768. var2 = pressure * dig['P8'] / 32768.
return pressure + (var1 + var2 + dig['P7']) / 16. return pressure + (var1 + var2 + dig['P7']) / 16.
def _compensate_humidity(self, raw_humidity): def _compensate_pressure_bme680(self, raw_pressure):
dig = self.dig
t_fine = self.t_fine
var1 = t_fine / 2. - 64000.
var2 = var1 * var1 * dig['P6'] / 131072.
var2 = var2 + var1 * dig['P5'] * 2.
var2 = var2 / 4. + (dig['P4'] * 65536.)
var1 = (dig['P3'] * var1 * var1 / 16384. + dig['P2'] * var1) / 524288.
var1 = (1. + var1 / 32768.) * dig['P1']
if var1 == 0:
return 0.
else:
pressure = 1048576.0 - raw_pressure
pressure = ((pressure - var2 / 4096.) * 6250.) / var1
var1 = dig['P9'] * pressure * pressure / 2147483648.
var2 = pressure * dig['P8'] / 32768.
var3 = (pressure / 256.) * (pressure / 256.) * (pressure / 256.) * (
dig['P10'] / 131072.)
return pressure + (var1 + var2 + var3 + (dig['P7'] * 128.)) / 16.
def _compensate_humidity_bme280(self, raw_humidity):
dig = self.dig dig = self.dig
t_fine = self.t_fine t_fine = self.t_fine
humidity = t_fine - 76800. humidity = t_fine - 76800.
h1 = ( h1 = (
raw_humidity - (dig['H4'] * 64. + dig['H5'] / 16384. * humidity)) raw_humidity - (
dig['H4'] * 64. + dig['H5'] / 16384. * humidity))
h2 = (dig['H2'] / 65536. * (1. + dig['H6'] / 67108864. * humidity * h2 = (dig['H2'] / 65536. * (1. + dig['H6'] / 67108864. * humidity *
(1. + dig['H3'] / 67108864. * humidity))) (1. + dig['H3'] / 67108864. * humidity)))
humidity = h1 * h2 humidity = h1 * h2
humidity = humidity * (1. - dig['H1'] * humidity / 524288.) humidity = humidity * (1. - dig['H1'] * humidity / 524288.)
return min(100., max(0., humidity)) return min(100., max(0., humidity))
def _compensate_humidity_bme680(self, raw_humidity):
dig = self.dig
temp_comp = self.temp
var1 = raw_humidity - (
(dig['H1'] * 16.) + ((dig['H3'] / 2.) * temp_comp))
var2 = var1 * ((dig['H2'] / 262144.) *
(1. + ((dig['H4'] / 16384.) * temp_comp) +
((dig['H5'] / 1048576.) * temp_comp * temp_comp)))
var3 = dig['H6'] / 16384.
var4 = dig['H7'] / 2097152.
humidity = var2 + ((var3 + (var4 * temp_comp)) * var2 * var2)
return min(100., max(0., humidity))
def _compensate_gas(self, gas_raw, gas_range):
gas_switching_error = self.read_register('RANGE_SWITCHING_ERROR', 1)[0]
var1 = (1340. + 5. * gas_switching_error) * \
BME680_GAS_CONSTANTS[gas_range][0]
gas = var1 * BME680_GAS_CONSTANTS[gas_range][1] / (
gas_raw - 512. + var1)
return gas
def _calculate_gas_heater_resistance(self, target_temp):
amb_temp = self.temp
heater_data = self.read_register('RES_HEAT_VAL', 3)
res_heat_val = get_signed_byte(heater_data[0])
res_heat_range = (heater_data[2] & 0x30) >> 4
dig = self.dig
var1 = (dig['G1'] / 16.) + 49.
var2 = ((dig['G2'] / 32768.) * 0.0005) + 0.00235
var3 = dig['G3'] / 1024.
var4 = var1 * (1. + (var2 * target_temp))
var5 = var4 + (var3 * amb_temp)
res_heat = (3.4 * ((var5 * (4. / (4. + res_heat_range))
* (1. / (1. + (res_heat_val * 0.002)))) - 25))
return int(res_heat)
def _calculate_gas_heater_duration(self, duration_ms):
if duration_ms >= 4032:
duration_reg = 0xff
else:
factor = 0
while duration_ms > 0x3F:
duration_ms /= 4
factor += 1
duration_reg = duration_ms + (factor * 64)
return duration_reg
def read_id(self):
# read chip id register
regs = [BME_CHIP_ID_REG]
params = self.i2c.i2c_read(regs, 1)
return bytearray(params['response'])[0]
def read_register(self, reg_name, read_len): def read_register(self, reg_name, read_len):
# read a single register # read a single register
regs = [BME280_REGS[reg_name]] regs = [self.chip_registers[reg_name]]
params = self.i2c.i2c_read(regs, read_len) params = self.i2c.i2c_read(regs, read_len)
return bytearray(params['response']) return bytearray(params['response'])
def write_register(self, reg_name, data): def write_register(self, reg_name, data):
if type(data) is not list: if type(data) is not list:
data = [data] data = [data]
reg = BME280_REGS[reg_name] reg = self.chip_registers[reg_name]
data.insert(0, reg) data.insert(0, reg)
self.i2c.i2c_write(data) self.i2c.i2c_write(data)
def get_status(self, eventtime): def get_status(self, eventtime):
return { data = {
'temperature': self.temp, 'temperature': self.temp,
'humidity': self.humidity,
'pressure': self.pressure 'pressure': self.pressure
} }
if self.chip_type in ('BME280', 'BME680'):
data['humidity'] = self.humidity
if self.chip_type == 'BME680':
data['gas'] = self.gas
return data
def load_config(config): def load_config(config):