From 2e4ec6166490b4b6cd91e24a5ae1f2629b4df1de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E7=8E=AE=20=28Jade=20Lin=29?= Date: Tue, 14 May 2024 12:23:13 +0100 Subject: [PATCH] bme280: Add BMP388 sensor support to BMxx80 (#6576) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extends the BMxx80 category with support for the BMP388 sensor, providing temperature and pressure output similar to the existing BMxx80 class of sensors. Signed-off-by: 林玮 (Jade Lin) --- docs/Config_Reference.md | 8 +- klippy/extras/bme280.py | 158 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 160 insertions(+), 6 deletions(-) diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index c6f4cfa66..e6fad4789 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -2911,9 +2911,9 @@ sensor_pin: # name in the above list. ``` -### BMP180/BMP280/BME280/BME680 temperature sensor +### BMP180/BMP280/BME280/BMP388/BME680 temperature sensor -BMP180/BMP280/BME280/BME680 two wire interface (I2C) environmental sensors. +BMP180/BMP280/BME280/BMP388/BME680 two wire interface (I2C) environmental sensors. Note that these sensors are not intended for use with extruders and heater beds, but rather for monitoring ambient temperature (C), pressure (hPa), relative humidity and in case of the BME680 gas level. @@ -2924,8 +2924,8 @@ temperature. ``` sensor_type: BME280 #i2c_address: -# Default is 118 (0x76). The BMP180 and some BME280 sensors have an address of 119 -# (0x77). +# Default is 118 (0x76). The BMP180, BMP388 and some BME280 sensors +# have an address of 119 (0x77). #i2c_mcu: #i2c_bus: #i2c_software_scl_pin: diff --git a/klippy/extras/bme280.py b/klippy/extras/bme280.py index 176712691..dbd071ee4 100644 --- a/klippy/extras/bme280.py +++ b/klippy/extras/bme280.py @@ -27,6 +27,29 @@ "CAL_2": 0xE1, } +BMP388_REGS = { + "CMD": 0x7E, + "STATUS": 0x03, + "PWR_CTRL": 0x1B, + "OSR": 0x1C, + "ORD": 0x1D, + "INT_CTRL": 0x19, + "CAL_1": 0x31, + "TEMP_MSB": 0x09, + "TEMP_LSB": 0x08, + "TEMP_XLSB": 0x07, + "PRESS_MSB": 0x06, + "PRESS_LSB": 0x05, + "PRESS_XLSB": 0x04, +} +BMP388_REG_VAL_PRESS_EN = 0x01 +BMP388_REG_VAL_TEMP_EN = 0x02 +BMP388_REG_VAL_PRESS_OS_NO = 0b000 +BMP388_REG_VAL_TEMP_OS_NO = 0b000000 +BMP388_REG_VAL_ODR_50_HZ = 0x02 +BMP388_REG_VAL_DRDY_EN = 0b100000 +BMP388_REG_VAL_NORMAL_MODE = 0x30 + BME680_REGS = { "RESET": 0xE0, "CTRL_HUM": 0x72, @@ -95,8 +118,15 @@ MEASURE_DONE = 1 << 5 RESET_CHIP_VALUE = 0xB6 -BME_CHIPS = {0x58: "BMP280", 0x60: "BME280", 0x61: "BME680", 0x55: "BMP180"} +BME_CHIPS = { + 0x58: "BMP280", + 0x60: "BME280", + 0x61: "BME680", + 0x55: "BMP180", + 0x50: "BMP388", +} BME_CHIP_ID_REG = 0xD0 +BMP3_CHIP_ID_REG = 0x00 def get_twos_complement(val, bit_size): @@ -197,6 +227,29 @@ def read_calibration_data_bmp280(calib_data_1): dig["P9"] = get_signed_short(calib_data_1[22:24]) return dig + def read_calibration_data_bmp388(calib_data_1): + dig = {} + dig["T1"] = get_unsigned_short(calib_data_1[0:2]) / 0.00390625 + dig["T2"] = get_unsigned_short(calib_data_1[2:4]) / 1073741824.0 + dig["T3"] = get_signed_byte(calib_data_1[4]) / 281474976710656.0 + + dig["P1"] = get_signed_short(calib_data_1[5:7]) - 16384 + dig["P1"] /= 1048576.0 + dig["P2"] = get_signed_short(calib_data_1[7:9]) - 16384 + dig["P2"] /= 536870912.0 + dig["P3"] = get_signed_byte(calib_data_1[9]) / 4294967296.0 + dig["P4"] = get_signed_byte(calib_data_1[10]) / 137438953472.0 + dig["P5"] = get_unsigned_short(calib_data_1[11:13]) / 0.125 + dig["P6"] = get_unsigned_short(calib_data_1[13:15]) / 64.0 + dig["P7"] = get_signed_byte(calib_data_1[15]) / 256.0 + dig["P8"] = get_signed_byte(calib_data_1[16]) / 32768.0 + dig["P9"] = get_signed_short(calib_data_1[17:19]) + dig["P9"] /= 281474976710656.0 + dig["P10"] = get_signed_byte(calib_data_1[19]) / 281474976710656.0 + dig["P11"] = get_signed_byte(calib_data_1[20]) + dig["P11"] /= 36893488147419103232.0 + return dig + def read_calibration_data_bme280(calib_data_1, calib_data_2): dig = read_calibration_data_bmp280(calib_data_1) dig["H1"] = calib_data_1[25] & 0xFF @@ -262,7 +315,7 @@ def read_calibration_data_bmp180(calib_data_1): dig["MD"] = get_signed_short_msb(calib_data_1[20:22]) return dig - chip_id = self.read_id() + chip_id = self.read_id() or self.read_bmp3_id() if chip_id not in BME_CHIPS.keys(): logging.info("bme280: Unknown Chip ID received %#x" % chip_id) else: @@ -294,6 +347,24 @@ def read_calibration_data_bmp180(calib_data_1): ) / 1000 self.sample_timer = self.reactor.register_timer(self._sample_bmp180) self.chip_registers = BMP180_REGS + elif self.chip_type == "BMP388": + self.max_sample_time = 0.5 + self.chip_registers = BMP388_REGS + self.write_register( + "PWR_CTRL", + [ + BMP388_REG_VAL_PRESS_EN + | BMP388_REG_VAL_TEMP_EN + | BMP388_REG_VAL_NORMAL_MODE + ], + ) + self.write_register( + "OSR", [BMP388_REG_VAL_PRESS_OS_NO | BMP388_REG_VAL_TEMP_OS_NO] + ) + self.write_register("ORD", [BMP388_REG_VAL_ODR_50_HZ]) + self.write_register("INT_CTRL", [BMP388_REG_VAL_DRDY_EN]) + + self.sample_timer = self.reactor.register_timer(self._sample_bmp388) else: self.max_sample_time = ( 1.25 @@ -310,6 +381,8 @@ def read_calibration_data_bmp180(calib_data_1): # Read out and calculate the trimming parameters if self.chip_type == "BMP180": cal_1 = self.read_register("CAL_1", 22) + elif self.chip_type == "BMP388": + cal_1 = self.read_register("CAL_1", 21) else: cal_1 = self.read_register("CAL_1", 26) cal_2 = self.read_register("CAL_2", 16) @@ -321,6 +394,8 @@ def read_calibration_data_bmp180(calib_data_1): self.dig = read_calibration_data_bme680(cal_1, cal_2) elif self.chip_type == "BMP180": self.dig = read_calibration_data_bmp180(cal_1) + elif self.chip_type == "BMP388": + self.dig = read_calibration_data_bmp388(cal_1) def _sample_bme280(self, eventtime): # Enter forced mode @@ -365,6 +440,79 @@ def _sample_bme280(self, eventtime): self._callback(self.mcu.estimated_print_time(measured_time), self.temp) return measured_time + REPORT_TIME + def _sample_bmp388(self, eventtime): + status = self.read_register("STATUS", 1) + if status[0] & 0b100000: + self.temp = self._sample_bmp388_temp() + if self.temp < self.min_temp or self.temp > self.max_temp: + self.printer.invoke_shutdown( + "BME280 temperature %0.1f outside range of %0.1f:%.01f" + % (self.temp, self.min_temp, self.max_temp) + ) + + if status[0] & 0b010000: + self.pressure = self._sample_bmp388_press() / 100.0 + + measured_time = self.reactor.monotonic() + self._callback(self.mcu.estimated_print_time(measured_time), self.temp) + return measured_time + REPORT_TIME + + def _sample_bmp388_temp(self): + xlsb = self.read_register("TEMP_XLSB", 1) + lsb = self.read_register("TEMP_LSB", 1) + msb = self.read_register("TEMP_MSB", 1) + adc_T = (msb[0] << 16) + (lsb[0] << 8) + (xlsb[0]) + + partial_data1 = adc_T - self.dig["T1"] + partial_data2 = self.dig["T2"] * partial_data1 + + self.t_fine = partial_data2 + self.t_fine += (partial_data1 * partial_data1) * self.dig["T3"] + + if self.t_fine < -40.0: + self.t_fine = -40.0 + + if self.t_fine > 85.0: + self.t_fine = 85.0 + + return self.t_fine + + def _sample_bmp388_press(self): + xlsb = self.read_register("PRESS_XLSB", 1) + lsb = self.read_register("PRESS_LSB", 1) + msb = self.read_register("PRESS_MSB", 1) + adc_P = (msb[0] << 16) + (lsb[0] << 8) + (xlsb[0]) + + partial_data1 = self.dig["P6"] * self.t_fine + partial_data2 = self.dig["P7"] * (self.t_fine * self.t_fine) + partial_data3 = self.dig["P8"] + partial_data3 *= self.t_fine * self.t_fine * self.t_fine + partial_out1 = self.dig["P5"] + partial_out1 += partial_data1 + partial_data2 + partial_data3 + + partial_data1 = self.dig["P2"] * self.t_fine + partial_data2 = self.dig["P3"] * (self.t_fine * self.t_fine) + partial_data3 = self.dig["P4"] + partial_data3 *= self.t_fine * self.t_fine * self.t_fine + partial_out2 = adc_P * ( + self.dig["P1"] + partial_data1 + partial_data2 + partial_data3 + ) + + partial_data1 = adc_P * adc_P + partial_data2 = self.dig["P9"] + (self.dig["P10"] * self.t_fine) + partial_data3 = partial_data1 * partial_data2 + partial_data4 = partial_data3 + adc_P * adc_P * adc_P * self.dig["P11"] + + comp_press = partial_out1 + partial_out2 + partial_data4 + + if comp_press < 30000: + comp_press = 30000 + + if comp_press > 125000: + comp_press = 125000 + + return comp_press + def _sample_bme680(self, eventtime): self.write_register("CTRL_HUM", self.os_hum & 0x07) meas = self.os_temp << 5 | self.os_pres << 2 @@ -644,6 +792,12 @@ def read_id(self): params = self.i2c.i2c_read(regs, 1) return bytearray(params["response"])[0] + def read_bmp3_id(self): + # read chip id register + regs = [BMP3_CHIP_ID_REG] + params = self.i2c.i2c_read(regs, 1) + return bytearray(params["response"])[0] + def read_register(self, reg_name, read_len): # read a single register regs = [self.chip_registers[reg_name]]