Skip to content

Commit

Permalink
Add support for BMP180 sensor
Browse files Browse the repository at this point in the history
Extends the BMxx80 category with support for the older BMP180 sensor, providing temperature and humidity output.
  • Loading branch information
vaxxi authored Oct 15, 2023
1 parent b1f597c commit 8b4fca4
Showing 1 changed file with 124 additions and 7 deletions.
131 changes: 124 additions & 7 deletions klippy/extras/bme280.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@
#
# Copyright (C) 2020 Eric Callahan <[email protected]>
#
# BMP180 sensor support by VAXXi <[email protected]>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging
import time
from . import bus

REPORT_TIME = .8
BME280_CHIP_ADDR = 0x76
BMP180_CHIP_ADDR = 0x77

BME280_REGS = {
'RESET': 0xE0, 'CTRL_HUM': 0xF2,
'STATUS': 0xF3, 'CTRL_MEAS': 0xF4, 'CONFIG': 0xF5,
Expand Down Expand Up @@ -46,6 +51,16 @@
15: (1., 244.140625)
}

BMP180_REGS = {
'RESET': 0xE0,
'CAL_1': 0xAA,
'CTRL_MEAS': 0xF4,
'REG_MSB': 0xF6,
'REG_LSB': 0xF7,
'CRV_TEMP': 0x2E,
'CRV_PRES': 0x34
}

STATUS_MEASURING = 1 << 3
STATUS_IM_UPDATE = 1
MODE = 1
Expand All @@ -57,7 +72,7 @@
RESET_CHIP_VALUE = 0xB6

BME_CHIPS = {
0x58: 'BMP280', 0x60: 'BME280', 0x61: 'BME680'
0x58: 'BMP280', 0x60: 'BME280', 0x61: 'BME680', 0x55: 'BMP180'
}
BME_CHIP_ID_REG = 0xD0

Expand All @@ -81,6 +96,14 @@ def get_signed_byte(bits):
return get_twos_complement(bits, 8)


def get_unsigned_short_msb(bits):
return bits[0] << 8 | bits[1]


def get_signed_short_msb(bits):
val = get_unsigned_short_msb(bits)
return get_twos_complement(val, 16)

class BME280:
def __init__(self, config):
self.printer = config.get_printer()
Expand Down Expand Up @@ -188,6 +211,23 @@ def read_calibration_data_bme680(calib_data_1, calib_data_2):
dig['G3'] = get_signed_byte(calib_data_2[13])
return dig

def read_calibration_data_bmp180(calib_data_1):
dig = {}
dig['AC1'] = get_signed_short_msb(calib_data_1[0:2])
dig['AC2'] = get_signed_short_msb(calib_data_1[2:4])
dig['AC3'] = get_signed_short_msb(calib_data_1[4:6])
dig['AC4'] = get_unsigned_short_msb(calib_data_1[6:8])
dig['AC5'] = get_unsigned_short_msb(calib_data_1[8:10])
dig['AC6'] = get_unsigned_short_msb(calib_data_1[10:12])

dig['B1'] = get_signed_short_msb(calib_data_1[12:14])
dig['B2'] = get_signed_short_msb(calib_data_1[14:16])

dig['MB'] = get_signed_short_msb(calib_data_1[16:18])
dig['MC'] = get_signed_short_msb(calib_data_1[18:20])
dig['MD'] = get_signed_short_msb(calib_data_1[20:22])
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)
Expand All @@ -200,16 +240,21 @@ def read_calibration_data_bme680(calib_data_1, calib_data_2):
self.write_register('RESET', [RESET_CHIP_VALUE])
self.reactor.pause(self.reactor.monotonic() + .5)

# Make sure non-volatile memory has been copied to registers
status = self.read_register('STATUS', 1)[0]
while status & STATUS_IM_UPDATE:
self.reactor.pause(self.reactor.monotonic() + .01)
# Make sure non-volatile memory has been copied to registers, except BMP180 which has no status
if self.chip_type != 'BMP180':
status = self.read_register('STATUS', 1)[0]
while status & STATUS_IM_UPDATE:
self.reactor.pause(self.reactor.monotonic() + .01)
status = self.read_register('STATUS', 1)[0]

if self.chip_type == 'BME680':
self.max_sample_time = 0.5
self.sample_timer = self.reactor.register_timer(self._sample_bme680)
self.chip_registers = BME680_REGS
elif self.chip_type == 'BMP180':
self.max_sample_time = (1.25 + ((2.3 * self.os_pres) + .575)) / 1000
self.sample_timer = self.reactor.register_timer(self._sample_bmp180)
self.chip_registers = BMP180_REGS
else:
self.max_sample_time = \
(1.25 + (2.3 * self.os_temp) + ((2.3 * self.os_pres) + .575)
Expand All @@ -221,14 +266,19 @@ def read_calibration_data_bme680(calib_data_1, calib_data_2):
self.write_register('CONFIG', (self.iir_filter & 0x07) << 2)

# Read out and calculate the trimming parameters
cal_1 = self.read_register('CAL_1', 26)
cal_2 = self.read_register('CAL_2', 16)
if self.chip_type == 'BMP180':
cal_1 = self.read_register('CAL_1', 22)
else:
cal_1 = self.read_register('CAL_1', 26)
cal_2 = self.read_register('CAL_2', 16)
if self.chip_type == 'BME280':
self.dig = read_calibration_data_bme280(cal_1, cal_2)
elif self.chip_type == 'BMP280':
self.dig = read_calibration_data_bmp280(cal_1)
elif self.chip_type == 'BME680':
self.dig = read_calibration_data_bme680(cal_1, cal_2)
elif self.chip_type == 'BMP180':
self.dig = read_calibration_data_bmp180(cal_1)

def _sample_bme280(self, eventtime):
# Enter forced mode
Expand Down Expand Up @@ -334,6 +384,42 @@ def data_ready(stat):
self._callback(self.mcu.estimated_print_time(measured_time), self.temp)
return measured_time + REPORT_TIME

def _sample_bmp180(self, eventtime):
meas = self.chip_registers['CRV_TEMP']
self.write_register('CTRL_MEAS', meas)

try:
time.sleep(0.005)
data = self.read_register('REG_MSB', 2)
UT = (data[0] << 8) | data[1]
except Exception:
logging.exception("BMP180: Error reading temperature")
self.temp = self.pressure = .0
return self.reactor.NEVER

meas = self.chip_registers['CRV_PRES'] | (self.os_pres << 6)
self.write_register('CTRL_MEAS', meas)

try:
time.sleep(0.005)
data = self.read_register('REG_MSB', 3)
UP = ( (data[0] << 16) | (data[1] << 8) | data[2] ) >> (8 - self.os_pres)
except Exception:
logging.exception("BMP180: Error reading pressure")
self.temp = self.pressure = .0
return self.reactor.NEVER

self.temp = self._compensate_temp_bmp180(UT)
self.pressure = self._compensate_pressure_bmp180(UP) / 100.
if self.temp < self.min_temp or self.temp > self.max_temp:
self.printer.invoke_shutdown(
"BMP180 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


def _compensate_temp(self, raw_temp):
dig = self.dig
var1 = ((raw_temp / 16384. - (dig['T1'] / 1024.)) * dig['T2'])
Expand Down Expand Up @@ -443,6 +529,37 @@ def _calculate_gas_heater_duration(self, duration_ms):

return duration_reg

def _compensate_temp_bmp180(self, raw_temp):
dig = self.dig
X1 = (raw_temp - dig['AC6']) * dig['AC5'] / 32768.
X2 = dig['MC'] * 2048 / (X1 + dig['MD'])
B5 = X1 + X2
self.t_fine = B5
return (B5 + 8)/16./10.

def _compensate_pressure_bmp180(self, raw_pressure):
dig = self.dig
B5 = self.t_fine
B6 = B5 - 4000
X1 = (dig['B2'] * (B6 * B6 / 4096)) / 2048
X2 = dig['AC2'] * B6 / 2048
X3 = X1 + X2
B3 = ((int(dig['AC1'] * 4 + X3) << self.os_pres) + 2) / 4
X1 = dig['AC3'] * B6 / 8192
X2 = (dig['B1'] * (B6 * B6 / 4096)) / 65536
X3 = ((X1 + X2) + 2) / 4
B4 = dig['AC4'] * (X3 + 32768) / 32768
B7 = (raw_pressure - B3) * (50000 >> self.os_pres)
if (B7 < 0x80000000):
p = (B7 * 2) / B4
else:
p = (B7 / B4) * 2
X1 = (p / 256) * (p / 256)
X1 = (X1 * 3038) / 65536
X2 = (-7357 * p) / 65536
p = p + (X1 + X2 + 3791) / 16.
return p

def read_id(self):
# read chip id register
regs = [BME_CHIP_ID_REG]
Expand Down

0 comments on commit 8b4fca4

Please sign in to comment.