From 9de3f850742cde73a7f8b04fdb925ebab7de4c15 Mon Sep 17 00:00:00 2001 From: Rogerio Goncalves Date: Wed, 15 May 2024 10:34:24 +0100 Subject: [PATCH] bme280: Add BMP388 sensor support to BMxx80 (#6576) (#242) 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) Co-authored-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 c6f4cfa6684a..e6fad4789d3b 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 176712691675..dbd071ee4dce 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]]