From f36abc1028a0e8adb9b46993da6650224d1765b8 Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Fri, 13 Dec 2024 10:55:06 +0100 Subject: [PATCH 01/26] verion bump --- docs/source/conf.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index d40cdda..c1a2e63 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -13,7 +13,7 @@ project = 'sts1-sensors' copyright = '2024, Simon Köfinger, Florian Rohrer' author = 'Simon Köfinger, Florian Rohrer' -release = 'v0.3.4' +release = 'v0.3.5' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/pyproject.toml b/pyproject.toml index 4a14c08..6d6f6db 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sts1_sensors" -version = "0.3.4" +version = "0.3.5" description = "A sensor library for the CubeSat STS1 (TU Wien Space Team)." readme = "README.md" requires-python = ">=3.13" From 62846d29cde7360235aafd6b1c293d09f380e619 Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Fri, 13 Dec 2024 11:06:28 +0100 Subject: [PATCH 02/26] Fix tests --- CONTRIBUTING.md | 1 + pyproject.toml | 3 +++ src/sts1_sensors/__init__.py | 1 + src/sts1_sensors/sensors/__init__.py | 6 ++---- uv.lock | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) create mode 100644 src/sts1_sensors/__init__.py diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f389a84..43217cb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,6 +3,7 @@ ## Making a new release +* (On Raspberry Pi) Run `pytest`. * Go to pyproject.toml and increase the version number * Go to docs/source/conf.py and increase the version number * Merge into main diff --git a/pyproject.toml b/pyproject.toml index 6d6f6db..2edc4a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,3 +27,6 @@ dev-dependencies = [ "sphinx-autobuild>=2024.10.3", "sphinx>=8.1.3", ] + +[tool.hatch.build.targets.wheel] +packages = ["src/sts1_sensors"] diff --git a/src/sts1_sensors/__init__.py b/src/sts1_sensors/__init__.py new file mode 100644 index 0000000..5e36334 --- /dev/null +++ b/src/sts1_sensors/__init__.py @@ -0,0 +1 @@ +from .sensors import * diff --git a/src/sts1_sensors/sensors/__init__.py b/src/sts1_sensors/sensors/__init__.py index 516d0d4..0e63a82 100644 --- a/src/sts1_sensors/sensors/__init__.py +++ b/src/sts1_sensors/sensors/__init__.py @@ -1,4 +1,2 @@ -from .BME688 import BME688 -from .BMM150 import BMM150 -from .GUVA_C32 import GUVA_C32 -from .L3GD20H import L3GD20H +from .ADXL345 import * +from .TMP112 import * diff --git a/uv.lock b/uv.lock index a59b7e0..e9675e7 100644 --- a/uv.lock +++ b/uv.lock @@ -515,7 +515,7 @@ wheels = [ [[package]] name = "sts1-sensors" -version = "0.3.4" +version = "0.3.5" source = { editable = "." } dependencies = [ { name = "smbus2" }, From 20999110e66ac5cc6371ecb2111d0afa39092e4e Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Fri, 13 Dec 2024 11:49:11 +0100 Subject: [PATCH 03/26] refactorings --- src/sts1_sensors/sensors/ADXL345.py | 2 - src/sts1_sensors/sensors/BMM150.py | 265 +++++++++------------------- 2 files changed, 82 insertions(+), 185 deletions(-) diff --git a/src/sts1_sensors/sensors/ADXL345.py b/src/sts1_sensors/sensors/ADXL345.py index 496a96e..60a20a5 100644 --- a/src/sts1_sensors/sensors/ADXL345.py +++ b/src/sts1_sensors/sensors/ADXL345.py @@ -19,8 +19,6 @@ def __init__(self, range=2, datarate=50, x_offset=0, y_offset=0, z_offset=0, add :param int z_offset: z-axis offset, defaults to 0. :param hexadecimal address: Physical address of the sensor on the board (see `i2cdetect` command). Allowed values: `[0x1D, 0x3A, 0x3B, 0x53]`. If None, the environment variable `STS1_SENSOR_ADDRESS_AVXL345` will be used. If environment variable is not found, 0x53 will be used. :param SMBus bus: A SMBus object. If None, this class will generate its own, defaults to None. - - :meta public: """ super().__init__(bus) diff --git a/src/sts1_sensors/sensors/BMM150.py b/src/sts1_sensors/sensors/BMM150.py index 87a691b..6225003 100644 --- a/src/sts1_sensors/sensors/BMM150.py +++ b/src/sts1_sensors/sensors/BMM150.py @@ -1,187 +1,86 @@ -import logging +import os import time -class BMM150: - poss_addr = [0x10, 0x11, 0x12, 0x13] - poss_datarate = [10,2,6,8,15,20,25,30] - poss_datarate_bin= [0b000,0b001,0b010,0b011,0b100,0b101,0b110,0b111] - - config_set = 0 - addr = 0 - datarate = 0 - +from sts1_sensors.sensors.AbstractSensor import AbstractSensor + +class BMM150(AbstractSensor): + """Geomagnetic sensor. + """ + _possible_addresses = [0x10, 0x11, 0x12, 0x13] + _possible_datarates = [1, 2, 6, 8, 15, 20, 25, 30] - check = 0 + def __init__(self, datarate=8, address=None, bus=None): + super().__init__(bus) + self.address = address or int(os.environ.get("STS1_SENSOR_ADDRESS_BMM150", "0x10"), 16) + self.datarate = datarate + self.xyz_addresses = {"x": 0x42, "y": 0x44, "z": 0x46} + + self.bus.write_byte_data(self.addr, 0x4B, 1) + time.sleep(1) + self.bus.write_byte_data(self.addr, 0x4C, self.poss_datarate.index(self.datarate) << 3) + self.bus.write_byte_data(self.addr, 0x51, 0b1111) + self.bus.write_byte_data(self.addr, 0x52, 0b1111) + + @property + def address(self): + return self._address + + @address.setter + def address(self, address): + if address not in self._possible_addresses: + s = f"The address {hex(address)} does not exist." + s += f" Choose one of {self._possible_addresses}." + raise ValueError(s) + self._address = address + + @property + def datarate(self): + return self._datarate + + @datarate.setter + def datarate(self, datarate): + if datarate not in self._possible_datarates: + s = f"The datarate {hex(datarate)} does not exist." + s += f" Choose one of {self._possible_datarates}." + raise ValueError(s) + self._datarate = datarate + + + def _get_raw(self, var): + lsb, msb = self.bus.read_i2c_block_data(self.address, self.xyz_addresses[var], 2) + k = (msb << 8) | lsb + k = k >> 3 + + if var in ("x", "y"): + shift = 12 + elif var == "z": + shift = 15 + + if (k >> shift) == 1: + k = (1 << shift) - (k & 0b111111111111111) + k = k * (-1) + return k - setDatarate = False - setAddr = False - setupD = False - Error = False - consoleLog = True - def __init__(self, bus): - self.addr = 0x10 - self.bus = bus - def deact_consoleLog(self): - self.consoleLog = False - def set_address(self, address): - try: - self.poss_addr.index(address) - self.addr = address - self.setAddr = True - except ValueError: - s = "BMM150: The address (" + str(hex(address)) + ") you entered for the sensor BMM150 does not exist!" - if self.consoleLog: - logging.error(s) - s = "BMM150: Try one of the following:" - for value in self.poss_addr: - s = s + str(hex(value)) + " " - if self.consoleLog: - logging.info(s) - logging.error("BMM150: not initialized!!!") - def set_datarate(self, rate): - try: - self.poss_datarate.index(rate) - self.datarate = rate - self.setDatarate = True - except ValueError: - s = "BMM150: The datarate (" + str(rate) + ") you entered for the sensor BMM150 does not exist!" - if self.consoleLog: - logging.error(s) - s = "BMM150: Try one of the following:" - for value in self.poss_datarate: - s = s + str(value) + " " - if self.consoleLog: - logging.info(s) - logging.error("BMM150: datarate not set!!!") - def setup(self): - if self.setAddr and self.setDatarate: - #all settings correct - try: - self.bus.write_byte_data(self.addr, 0x4B, 0b00000001) - time.sleep(1) - self.bus.write_byte_data(self.addr, 0x4C, 0b00000000 & (self.poss_datarate_bin[self.poss_datarate.index(self.datarate)] << 3)) - self.bus.write_byte_data(self.addr, 0x51, 0b00001111) - self.bus.write_byte_data(self.addr, 0x52, 0b00001111) - except OSError as e: - self.Error = True - if e.errno == 121: - logging.error("BMM150: Remote I/O Error: The device is not responding on the bus. Therefore it will be ignored") - else: - logging.error(f"BMM150: An error occurred: {e}") - return None - - self.setupD = True - if self.consoleLog: - logging.info("BMM150: Setup finished, sensor ready.") - else: - if self.consoleLog: - logging.error("BMM150: Setup failed! Settings incorrect") - def getXRaw(self): - if self.setupD: - x_LSB = self.bus.read_byte_data(self.addr, 0x42) - x_MSB= self.bus.read_byte_data(self.addr, 0x43) - x_XSB = (x_MSB<<8) + x_LSB - x_XSB = x_XSB >>3 - - if (x_XSB>>(13-1)) == 0b1: - X_XSB = (1<<12) - (x_XSB & 0b0111111111111) - x_XSB = x_XSB * (-1) - - - return x_XSB - else: - if self.consoleLog: - if self.Error: - logging.error("BMM150: not available") - else: - logging.error("BMM150: Setup not finished") - def getYRaw(self): - if self.setupD: - y_LSB = self.bus.read_byte_data(self.addr, 0x44) - y_MSB= self.bus.read_byte_data(self.addr, 0x45) - y_XSB = (y_MSB<<8) + y_LSB - y_XSB = y_XSB >>3 - - if (y_XSB>>(13-1)) == 0b1: - y_XSB = (1<<12) - (y_XSB & 0b0111111111111) - y_XSB = y_XSB * (-1) - - return y_XSB - else: - if self.consoleLog: - if self.Error: - logging.error("BMM150: not available") - else: - logging.error("BMM150: Setup not finished") - def getZRaw(self): - if self.setupD: - z_LSB = self.bus.read_byte_data(self.addr, 0x42) - z_MSB= self.bus.read_byte_data(self.addr, 0x43) - z_XSB = (z_MSB<<8) + z_LSB - z_XSB = z_XSB >>3 - - if (z_XSB>>(16-1)) == 0b1: - z_XSB = (1<<15) - (z_XSB & 0b0111111111111111) - z_XSB = z_XSB * (-1) - - return z_XSB - else: - if self.consoleLog: - if self.Error: - logging.error("BMM150: not available") - else: - logging.error("BMM150: Setup not finished") - def getXuT(self): - if self.setupD: - x_LSB = self.bus.read_byte_data(self.addr, 0x42) - x_MSB= self.bus.read_byte_data(self.addr, 0x43) - x_XSB = (x_MSB<<8) + x_LSB - x_XSB = x_XSB >>3 - - if (x_XSB>>(13-1)) == 0b1: - #negative temp - x_XSB = x_XSB - (1<<(13)) - - return x_XSB / 3.15076 - else: - if self.consoleLog: - if self.Error: - logging.error("BMM150: not available") - else: - logging.error("BMM150: Setup not finished") - def getYuT(self): - if self.setupD: - y_LSB = self.bus.read_byte_data(self.addr, 0x44) - y_MSB= self.bus.read_byte_data(self.addr, 0x45) - y_XSB = (y_MSB<<8) + y_LSB - y_XSB = y_XSB >>3 - - if (y_XSB>>(13-1)) == 0b1: - #negative temp - y_XSB = y_XSB - (1<<(13)) - - return y_XSB / 3.15076 - else: - if self.consoleLog: - if self.Error: - logging.error("BMM150: not available") - else: - logging.error("BMM150: Setup not finished") - def getZuT(self): - if self.setupD: - z_LSB = self.bus.read_byte_data(self.addr, 0x46) - z_MSB= self.bus.read_byte_data(self.addr, 0x47) - z_XSB = (z_MSB<<8) + z_LSB - z_XSB = z_XSB >>1 - - if (z_XSB>>(15-1)) == 0b1: - #negative temp - z_XSB = z_XSB - (1<<(15)) - - return z_XSB / 6.5536 - else: - if self.consoleLog: - if self.Error: - logging.error("BMM150: not available") - else: - logging.error("BMM150: Setup not finished") + def get_raw(self): + return self._get_raw("x"), self._get_raw("y"), self._get_raw("z") + + def _get_ut(self, var): + lsb, msb = self.bus.read_i2c_block_data(self.address, self.xyz_addresses[var], 2) + k = (msb << 8) | lsb + k = k >> 3 + + if var in ("x", "y"): + shift = 12 + elif var == "z": + shift = 14 + + if (k >> shift) == 1: + k = k - (1 << (shift+1)) + + if var in ("x", "y"): + return k / 3.15076 + elif var == "z": + return k / 6.5536 + + def get_ut(self): + return self._get_ut("x"), self._get_ut("y"), self._get_ut("z") From d5f6be799b8d22b3f75310c4121383cdbb68851d Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Fri, 13 Dec 2024 15:40:17 +0100 Subject: [PATCH 04/26] sensor refactors --- examples/ADXL345_example.py | 3 +- src/sts1_sensors/sensors/GUVA_C32.py | 236 ++++++++------------------ src/sts1_sensors/sensors/L3GD20H.py | 242 +++++++-------------------- src/sts1_sensors/utils.py | 5 + 4 files changed, 142 insertions(+), 344 deletions(-) create mode 100644 src/sts1_sensors/utils.py diff --git a/examples/ADXL345_example.py b/examples/ADXL345_example.py index e1452df..f142e84 100644 --- a/examples/ADXL345_example.py +++ b/examples/ADXL345_example.py @@ -4,8 +4,7 @@ log = structlog.get_logger() -accel = ADXL345(range=2, datarate=50, - x_offset=-0.04570, y_offset=-0.00697, z_offset=0.04614) +accel = ADXL345(range=2, datarate=50) while True: x, y, z = accel.get_g() diff --git a/src/sts1_sensors/sensors/GUVA_C32.py b/src/sts1_sensors/sensors/GUVA_C32.py index 4c133f5..4c2b903 100644 --- a/src/sts1_sensors/sensors/GUVA_C32.py +++ b/src/sts1_sensors/sensors/GUVA_C32.py @@ -1,169 +1,81 @@ +import os from smbus2 import i2c_msg -import logging -logging.basicConfig(level=logging.INFO) +from sts1_sensors.sensors.AbstractSensor import AbstractSensor -class GUVA_C32: - poss_addr = [0x39] - poss_res = [100, 200, 400, 800] - poss_res_bin = [0b011, 0b010, 0b001, 0b000] - poss_range = [1, 2, 4, 8, 16, 32, 64, 128] - poss_range_bin = [0b000, 0b001, 0b010, 0b011, 0b100, 0b101, 0b110, 0b111] - - - addr = 0 - res = 0 - range_ = 0 - - +class GUVA_C32(AbstractSensor): + """Ultraviolet light sensor.""" + + _possible_addresses = [0x39] + _possible_resolutions = [800, 400, 200, 100] + _possible_ranges = [1, 2, 4, 8, 16, 32, 64, 128] - setAddr = False - setRes = False - setRange = False - setupD = False - Error = False - consoleLog = True - def __init__(self, bus): - self.addr = 0x37 - self.bus = bus - def deact_consoleLog(self): - self.consoleLog = False - def set_address(self, address): - try: - self.poss_addr.index(address) - self.addr = address - self.setAddr = True - except ValueError: - s = "GUVA_C32: The address (" + str(hex(address)) + ") you entered for the sensor GUVA_C32 does not exist!" - if self.consoleLog: - #print(s) - logging.error(s) - s = "GUVA_C32: Try one of the following:" - for value in self.poss_addr: - s = s + str(hex(value)) + " " - if self.consoleLog: - logging.info(s) - #print(s) - #print("GUVA_C32 not initialized!!!") - logging.error("GUVA_C32: not initialized!!!") - def set_resolution(self, res): - try: - self.poss_res.index(res) - self.res = res - self.setRes = True - except ValueError: - s = "GUVA_C32: The resolution (" + str(hex(address)) + ") you entered for the sensor GUVA_C32 does not exist!" - if self.consoleLog: - logging.error(s) - #print(s) - s = "GUVA_C32: Try one of the following:" - for value in self.poss_res: - s = s + str(hex(value)) + " " - if self.consoleLog: - #print(s) - logging.info(s) - #print("GUVA_C32 not initialized!!!") - logging.error("GUVA_C32: not initialized!!!") - def set_range(self, range_): - try: - self.poss_range.index(range_) - self.range_ = range_ - self.setRange = True - except ValueError: - s = "GUVA_C32: The range (" + str(hex(address)) + ") you entered for the sensor GUVA_C32 does not exist!" - if self.consoleLog: - #print(s) - logging.error(s) - s = "GUVA_C32: Try one of the following:" - for value in self.poss_range: - s = s + str(hex(value)) + " " - if self.consoleLog: - #print(s) - logging.info(s) - #print("GUVA_C32 not initialized!!!") - logging.error("GUVA_C32: not initialized!!!") - def setup(self): - if self.setAddr and self.setRes and self.setRange: - try: - #all settings correct - #set power mode normal 0b00 and operation mode UVAoperation 0b01 -> 0b00010000 0x01 - msg = i2c_msg.write(self.addr, [0x01, 0b00010000]) - self.bus.i2c_rdwr(msg) - #set resolution 0x04 - msg = i2c_msg.write(self.addr, [0x04, 0b00000011]) - #msg = i2c_msg.write(self.addr, [0x04, 0b00000000 + self.poss_res_bin[self.poss_res.index(self.res)]]) - self.bus.i2c_rdwr(msg) - #set range 0x05 - msg = i2c_msg.write(self.addr, [0x05, 0b00000000]) - #msg = i2c_msg.write(self.addr, [0x05, 0b00000000 + self.poss_range_bin[self.poss_range.index(self.range_)]]) - self.bus.i2c_rdwr(msg) - except OSError as e: - self.Error = True - if e.errno == 121: - #print("Remote I/O Error: The device is not responding on the bus. Therefore it will be ignored") - logging.error("GUVA_C32: Remote I/O Error: The device is not responding on the bus. Therefore it will be ignored") - else: - #print(f"An error occurred: {e}") - logging.error(f"GUVA_C32: An error occurred: {e}") - return None - - - + def __init__(self, range=1, resolution=800, address=None, bus=None): + super().__init__(bus) + self.address = address or int(os.environ.get("STS1_SENSOR_ADDRESS_GUVA_C32", "0x39"), 16) + self.range = range + self.resolution = resolution + + #set power mode normal 0b00 and operation mode UVAoperation 0b01 -> 0b00010000 0x01 + msg = i2c_msg.write(self.addr, [0x01, 0b00010000]) + self.bus.i2c_rdwr(msg) + #set resolution 0x04 + msg = i2c_msg.write(self.addr, [0x04, 0b00000011]) + #msg = i2c_msg.write(self.addr, [0x04, 0b00000000 + self.poss_res_bin[self.poss_res.index(self.res)]]) + self.bus.i2c_rdwr(msg) + #set range 0x05 + msg = i2c_msg.write(self.addr, [0x05, 0b00000000]) + #msg = i2c_msg.write(self.addr, [0x05, 0b00000000 + self.poss_range_bin[self.poss_range.index(self.range_)]]) + self.bus.i2c_rdwr(msg) + + + @property + def address(self): + return self._address + + @address.setter + def address(self, address): + if address not in self._possible_addresses: + s = f"The address {hex(address)} does not exist." + s += f" Choose one of {self._possible_addresses}." + raise ValueError(s) + self._address = address + + @property + def range(self): + return self._range + + @range.setter + def range(self, range): + if range not in self._possible_ranges: + s = f"The range {hex(range)} does not exist." + s += f" Choose one of {self._possible_ranges}." + raise ValueError(s) + self._range = range + + @property + def resolution(self): + return self._resolution + + @resolution.setter + def resolution(self, resolution): + if resolution not in self._possible_resolutions: + s = f"The resolution {hex(resolution)} does not exist." + s += f" Choose one of {self._possible_resolutions}." + raise ValueError(s) + self._resolution = resolution - self.setupD = True - if self.consoleLog: - #print("Setup finished, GUVA_C32 ready.") - logging.info("GUVA_C32: Setup finished, sensor ready.") - return True - else: - if self.consoleLog: - #print("Setup failed! Settings incorrect") - logging.error("GUVA_C32: Setup failed! Settings incorrect") def getRawUVA(self): - if self.setupD: - - msg_w = i2c_msg.write(self.addr, [0x15]) - msg_r = i2c_msg.read(self.addr, 2) - self.bus.i2c_rdwr(msg_w) - self.bus.i2c_rdwr(msg_r) - - byte = [] - for value in msg_r: - byte.append(value) - #print(bin(value)) - - raw = (byte[0] + (byte[1] << 8)) - - return raw - else: - if self.consoleLog: - if self.Error: - #print("GUVA_C32 not available") - logging.error("GUVA_C32: not available") - else: - #print("Setup not finished") - logging.error("GUVA_C32: Setup not finished") + msg_w = i2c_msg.write(self.addr, [0x15]) + msg_r = i2c_msg.read(self.addr, 2) + self.bus.i2c_rdwr(msg_w) + self.bus.i2c_rdwr(msg_r) + + byte = [] + for value in msg_r: + byte.append(value) + + return (byte[0] + (byte[1] << 8)) + def getUVA(self): - if self.setupD: - - msg_w = i2c_msg.write(self.addr, [0x15]) - msg_r = i2c_msg.read(self.addr, 2) - self.bus.i2c_rdwr(msg_w) - self.bus.i2c_rdwr(msg_r) - - byte = [] - for value in msg_r: - byte.append(value) - #print(bin(value)) - - raw = (byte[0] + (byte[1] << 8)) - - return int(raw / (65536 / 16)) - else: - if self.consoleLog: - if self.Error: - #print("GUVA_C32 not available") - logging.error("GUVA_C32: not available") - else: - #print("Setup not finished") - logging.error("GUVA_C32: Setup not finished") \ No newline at end of file + return round(self.getRawUVA() / 4096) diff --git a/src/sts1_sensors/sensors/L3GD20H.py b/src/sts1_sensors/sensors/L3GD20H.py index 55ecdec..596d12c 100644 --- a/src/sts1_sensors/sensors/L3GD20H.py +++ b/src/sts1_sensors/sensors/L3GD20H.py @@ -1,184 +1,66 @@ -import logging +import os -def twos_comp(val, bits): - if val & (1 << (bits - 1)) != 0: - val = val - (1 << bits) - return val +from sts1_sensors.sensors.AbstractSensor import AbstractSensor +from sts1_sensors.utils import twos_comp -class L3GD20H: - poss_addr = [0x6A, 0x6B] - poss_datarate = [12.5,25,50,100,200,400,800] - poss_datarate_bin = [0b00, 0b01, 0b10, 0b00, 0b01, 0b10, 0b11] - poss_LOW_ODR_bin = [0b1, 0b1, 0b1, 0b0, 0b0, 0b0, 0b0] - poss_range = [245,500,2000] - poss_range_bin = [0b00, 0b01, 0b10] - dps_per_digit = [0.00875, 0.01750, 0.07000] +class L3GD20H(AbstractSensor): + """Three-axis gyroscope""" + _possible_addresses = [0x6A, 0x6B] + _possible_datarates = [12.5, 25, 50, 100, 200, 400, 800] + _possible_datarates_bin = [(0, 1), (1, 1), (2, 1), (0, 0), (1, 0), (2, 0), (3, 0)] + _possible_ranges = [245, 500, 2000] - config_set = 0 - addr = 0 - datarate = 0 - range = 0 - - - check = 0 - - setDatarate = False - setAddr = False - setupD = False - Error = False - consoleLog = True - setRange = False - def __init__(self, bus): - self.addr = 0x6A - self.bus = bus - def deact_consoleLog(self): - self.consoleLog = False - def set_address(self, address): - try: - self.poss_addr.index(address) - self.addr = address - self.setAddr = True - except ValueError: - s = "L3GD20H: The address (" + str(hex(address)) + ") you entered for the sensor L3GD20H does not exist!" - if self.consoleLog: - logging.error(s) - s = "L3GD20H: Try one of the following:" - for value in self.poss_addr: - s = s + str(hex(value)) + " " - if self.consoleLog: - logging.info(s) - logging.error("L3GD20H: not initialized!!!") - def set_datarate(self, rate): - try: - self.poss_datarate.index(rate) - self.datarate = rate - self.setDatarate = True - except ValueError: - s = "L3GD20H: The datarate (" + str(rate) + ") you entered for the sensor L3GD20H does not exist!" - if self.consoleLog: - logging.error(s) - s = "L3GD20H: Try one of the following:" - for value in self.poss_datarate: - s = s + str(value) + " " - if self.consoleLog: - logging.info(s) - logging.error("L3GD20H: datarate not set!!!") - def set_range(self, range): - try: - self.poss_range.index(range) - self.range = range - #self.resolution = self.range_resolution[self.poss_range.index(range)] - self.setRange = True - except ValueError: - s = "L3GD20H: The range (" + str(range) + ") you entered for the sensor L3GD20H does not exist!" - if self.consoleLog: - logging.error(s) - s = "L3GD20H: Try one of the following:" - for value in self.poss_range: - s = s + str(value) + " " - if self.consoleLog: - logging.info(s) - logging.error("L3GD20H range not set!!!") - def setup(self): - if self.setAddr and self.setDatarate and self.setRange: - #all settings correct - try: - #write CTRL1 (datarate, bandwith, powermode and enable for all axis - self.bus.write_byte_data(self.addr, 0x20, 0b00001111 | (self.poss_datarate_bin[self.poss_datarate.index(self.datarate)] << 6)) - #write CTRL4 (Block Data update, Big/little endian, Full Scale Selection, - self.bus.write_byte_data(self.addr, 0x23, 0b00000000 | (self.poss_range_bin[self.poss_range.index(self.range)] << 4)) - #write LOW_ODR - self.bus.write_byte_data(self.addr, 0x39, 0b00000000 | (self.poss_LOW_ODR_bin[self.poss_datarate.index(self.datarate)])) - except OSError as e: - self.Error = True - if e.errno == 121: - logging.error("L3GD20H: Remote I/O Error: The device is not responding on the bus. Therefore it will be ignored") - else: - logging.error(f"L3GD20H: An error occurred: {e}") - return None - - self.setupD = True - if self.consoleLog: - logging.info("L3GD20H: Setup finished, L3GD20H ready.") - else: - if self.consoleLog: - logging.error("L3GD20H: Setup failed! Settings incorrect") - def getXraw(self): - if self.setupD: - XL_val = self.bus.read_byte_data(0x6a, 0x28) - XH_val = self.bus.read_byte_data(0x6a, 0x29) - X_val = (XH_val<<8) + XL_val - x = twos_comp(X_val, 16) - return x - else: - if self.consoleLog: - if self.Error: - logging.error("L3GD20H: not available") - else: - logging.error("L3GD20H: Setup not finished") - def getYraw(self): - if self.setupD: - YL_val = self.bus.read_byte_data(0x6A, 0x2A) - YH_val = self.bus.read_byte_data(0x6a, 0x2B) - Y_val = (YH_val<<8) + YL_val - y = twos_comp(Y_val, 16) - return y - else: - if self.consoleLog: - if self.Error: - logging.error("L3GD20H: not available") - else: - logging.error("L3GD20H: Setup not finished") - def getZraw(self): - if self.setupD: - ZL_val = self.bus.read_byte_data(0x6a, 0x2C) - ZH_val = self.bus.read_byte_data(0x6a, 0x2D) - Z_val = (ZH_val<<8) + ZL_val - z = twos_comp(Z_val, 16) - return z - else: - if self.consoleLog: - if self.Error: - logging.error("L3GD20H: not available") - else: - logging.error("L3GD20H: Setup not finished") - def getXdps(self): - if self.setupD: - XL_val = self.bus.read_byte_data(0x6a, 0x28) - XH_val = self.bus.read_byte_data(0x6a, 0x29) - X_val = (XH_val<<8) + XL_val - x = twos_comp(X_val, 16) - return x * self.dps_per_digit[self.poss_range.index(self.range)] - else: - if self.consoleLog: - if self.Error: - logging.error("L3GD20H: not available") - else: - logging.error("L3GD20H: Setup not finished") - def getYdps(self): - if self.setupD: - YL_val = self.bus.read_byte_data(0x6a, 0x2A) - YH_val = self.bus.read_byte_data(0x6a, 0x2B) - Y_val = (YH_val<<8) + YL_val - y = twos_comp(Y_val, 16) - return y * self.dps_per_digit[self.poss_range.index(self.range)] - else: - if self.consoleLog: - if self.Error: - logging.error("L3GD20H: not available") - else: - logging.error("L3GD20H: Setup not finished") - def getZdps(self): - if self.setupD: - ZL_val = self.bus.read_byte_data(0x6a, 0x2C) - ZH_val = self.bus.read_byte_data(0x6a, 0x2D) - Z_val = (ZH_val<<8) + ZL_val - z = twos_comp(Z_val, 16) - return z * self.dps_per_digit[self.poss_range.index(self.range)] - else: - if self.consoleLog: - if self.Error: - logging.error("L3GD20H: not available") - else: - logging.error("L3GD20H: Setup not finished") + def __init__(self, range=245, datarate=12.5, address=None, bus=None): + super().__init__(bus) + self.address = address or int(os.environ.get("STS1_SENSOR_ADDRESS_L3GD20H", "0x6A"), 16) + self.range = range + self.datarate = datarate + self.xyz_addresses = {"x": 0x28, "y": 0x2A, "z": 0x2C} + self.dps_per_digit = [0.00875, 0.01750, 0.07000] + + # write CTRL1 datarate, bandwith, powermode and enable for all axis + # write CTRL4 Block Data update, Big/little endian, Full Scale Selection, + # write LOW_ODR + a, b = self._possible_datarates_bin[self._possible_datarates.index(self.datarate)] + self.bus.write_byte_data(self.address, 0x20, 0b1111 | (a << 6)) + self.bus.write_byte_data(self.address, 0x23, (self._possible_ranges.index(self.range) << 4)) + self.bus.write_byte_data(self.address, 0x39, b) + + @property + def datarate(self): + return self._datarate + + @datarate.setter + def datarate(self, datarate): + if datarate not in self._possible_datarates: + s = f"The datarate {hex(datarate)} does not exist." + s += f" Choose one of {self._possible_datarates}." + raise ValueError(s) + self._datarate = datarate + + @property + def range(self): + return self._range + + @range.setter + def range(self, range): + if range not in self._possible_ranges: + s = f"The range {hex(range)} does not exist." + s += f" Choose one of {self._possible_ranges}." + raise ValueError(s) + self._range = range + + def _get_raw(self, var): + lsb, msb = self.bus.read_i2c_block_data(self.address, self.xyz_addresses[var], 2) + return twos_comp((msb << 8) + lsb, 16) + + def get_position_raw(self): + return self._get_raw("x"), self._get_raw("y"), self._get_raw("z") + + def _get_dps(self, var): + k = self._get_raw(var) + return k * self.dps_per_digit[self.poss_range.index(self.range)] + + def get_position(self): + return self._get_dps("x"), self._get_dps("y"), self._get_dps("z") diff --git a/src/sts1_sensors/utils.py b/src/sts1_sensors/utils.py new file mode 100644 index 0000000..92cc629 --- /dev/null +++ b/src/sts1_sensors/utils.py @@ -0,0 +1,5 @@ + +def twos_comp(val, bits): + if val & (1 << (bits - 1)) != 0: + val = val - (1 << bits) + return val From 83885ea484b7bb0d2ad10d70b45a7c998c7c5d60 Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Fri, 13 Dec 2024 16:05:09 +0100 Subject: [PATCH 05/26] bugfix --- examples/L3GD20HExample.py | 13 ------------- examples/L3GD20H_example.py | 12 ++++++++++++ examples/calibration_example.py | 5 +++-- src/sts1_sensors/sensors/L3GD20H.py | 4 ++-- src/sts1_sensors/sensors/__init__.py | 1 + 5 files changed, 18 insertions(+), 17 deletions(-) delete mode 100644 examples/L3GD20HExample.py create mode 100644 examples/L3GD20H_example.py diff --git a/examples/L3GD20HExample.py b/examples/L3GD20HExample.py deleted file mode 100644 index de82d4b..0000000 --- a/examples/L3GD20HExample.py +++ /dev/null @@ -1,13 +0,0 @@ -import sts1_sensors as STS1 -from smbus2 import SMBus -import time - -with SMBus(1) as bus: - gyro = STS1.L3GD20H(bus) - gyro.set_address(0x6A) - gyro.set_datarate(50) - gyro.set_range(2000) - gyro.setup() - while True: - print("X: %.2fdps, Y: %.2fdps Z: %.2fdps" % (gyro.getXdps(), gyro.getYdps(), gyro.getZdps())) - time.sleep(.1) diff --git a/examples/L3GD20H_example.py b/examples/L3GD20H_example.py new file mode 100644 index 0000000..dfca814 --- /dev/null +++ b/examples/L3GD20H_example.py @@ -0,0 +1,12 @@ +import time +import structlog +from sts1_sensors import L3GD20H + +log = structlog.get_logger() + +gyro = L3GD20H(range=245, datarate=12.5) + +while True: + x, y, z = gyro.get_position() + log.info(f"X: {x:.2f}dpfs, Y: {y:.2f}dpfs, Z: {z:.2f}dpfs") + time.sleep(.25) diff --git a/examples/calibration_example.py b/examples/calibration_example.py index 427c12a..c82555f 100644 --- a/examples/calibration_example.py +++ b/examples/calibration_example.py @@ -2,12 +2,13 @@ import time from sts1_sensors import ADXL345 -accel = ADXL345(address=0x53, range=2, datarate=50) +accel = ADXL345(range=2, datarate=50) measurements = [] +# takes 40 secs for _ in range(200): measurements.append(accel.get_g()) - time.sleep(.1) + time.sleep(.2) x_vals, y_vals, z_vals = zip(*measurements) diff --git a/src/sts1_sensors/sensors/L3GD20H.py b/src/sts1_sensors/sensors/L3GD20H.py index 596d12c..b07e1ed 100644 --- a/src/sts1_sensors/sensors/L3GD20H.py +++ b/src/sts1_sensors/sensors/L3GD20H.py @@ -7,9 +7,9 @@ class L3GD20H(AbstractSensor): """Three-axis gyroscope""" _possible_addresses = [0x6A, 0x6B] + _possible_ranges = [245, 500, 2000] _possible_datarates = [12.5, 25, 50, 100, 200, 400, 800] _possible_datarates_bin = [(0, 1), (1, 1), (2, 1), (0, 0), (1, 0), (2, 0), (3, 0)] - _possible_ranges = [245, 500, 2000] def __init__(self, range=245, datarate=12.5, address=None, bus=None): super().__init__(bus) @@ -60,7 +60,7 @@ def get_position_raw(self): def _get_dps(self, var): k = self._get_raw(var) - return k * self.dps_per_digit[self.poss_range.index(self.range)] + return k * self.dps_per_digit[self._possible_ranges.index(self.range)] def get_position(self): return self._get_dps("x"), self._get_dps("y"), self._get_dps("z") diff --git a/src/sts1_sensors/sensors/__init__.py b/src/sts1_sensors/sensors/__init__.py index 0e63a82..e526113 100644 --- a/src/sts1_sensors/sensors/__init__.py +++ b/src/sts1_sensors/sensors/__init__.py @@ -1,2 +1,3 @@ from .ADXL345 import * +from .L3GD20H import * from .TMP112 import * From aa71450a07ae17878a9cfa610bb43d718df0e3c5 Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Fri, 13 Dec 2024 16:10:43 +0100 Subject: [PATCH 06/26] Added tests --- justfile | 2 +- src/sts1_sensors/sensors/L3GD20H.py | 12 ++++++++ tests/test_L3GD20H.py | 45 +++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 tests/test_L3GD20H.py diff --git a/justfile b/justfile index d5d0c55..23dc0e5 100644 --- a/justfile +++ b/justfile @@ -7,4 +7,4 @@ clean_docs: test: # Works only on Rasperry Pi - pytest + uv run pytest diff --git a/src/sts1_sensors/sensors/L3GD20H.py b/src/sts1_sensors/sensors/L3GD20H.py index b07e1ed..0c4ba47 100644 --- a/src/sts1_sensors/sensors/L3GD20H.py +++ b/src/sts1_sensors/sensors/L3GD20H.py @@ -27,6 +27,18 @@ def __init__(self, range=245, datarate=12.5, address=None, bus=None): self.bus.write_byte_data(self.address, 0x23, (self._possible_ranges.index(self.range) << 4)) self.bus.write_byte_data(self.address, 0x39, b) + @property + def address(self): + return self._address + + @address.setter + def address(self, address): + if address not in self._possible_addresses: + s = f"The address {hex(address)} does not exist." + s += f" Choose one of {self._possible_addresses}." + raise ValueError(s) + self._address = address + @property def datarate(self): return self._datarate diff --git a/tests/test_L3GD20H.py b/tests/test_L3GD20H.py new file mode 100644 index 0000000..58d3916 --- /dev/null +++ b/tests/test_L3GD20H.py @@ -0,0 +1,45 @@ +import os + +import pytest + +from sts1_sensors import L3GD20H + +def test_class_creation1(): + L3GD20H() + +def test_class_creation2(): + L3GD20H(range=245, datarate=12.5) + +def test_class_creation3(): + L3GD20H(range=2000) + +def test_class_creation4(): + L3GD20H(datarate=800) + +def test_class_creation5(): + from smbus2 import SMBus + with SMBus(1) as bus: + L3GD20H(bus=bus) + +def test_class_creation6(): + os.environ["STS1_SENSOR_ADDRESS_L3GD20H"] = "0x6A" + L3GD20H() + del os.environ["STS1_SENSOR_ADDRESS_L3GD20H"] + +def test_get_postition1(): + t = L3GD20H() + t.get_position_raw() + +def test_get_postition2(): + t = L3GD20H() + t.get_position() + +def test_set_wrong_address1(): + with pytest.raises(ValueError): + L3GD20H(address=0x99) + +def test_set_wrong_address2(): + with pytest.raises(ValueError): + os.environ["STS1_SENSOR_ADDRESS_L3GD20H"] = "0x98" + L3GD20H() + del os.environ["STS1_SENSOR_ADDRESS_L3GD20H"] From dbd8cfb85d38c4a1ea8250ecff44fa11b4e376ec Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Fri, 13 Dec 2024 16:15:20 +0100 Subject: [PATCH 07/26] refactorings --- examples/{ADXL345_example.py => ADXL345.py} | 0 examples/{L3GD20H_example.py => L3GD20H.py} | 0 examples/{TMP112_example.py => TMP112.py} | 0 src/sts1_sensors/sensors/BME688.py | 9 +++------ 4 files changed, 3 insertions(+), 6 deletions(-) rename examples/{ADXL345_example.py => ADXL345.py} (100%) rename examples/{L3GD20H_example.py => L3GD20H.py} (100%) rename examples/{TMP112_example.py => TMP112.py} (100%) diff --git a/examples/ADXL345_example.py b/examples/ADXL345.py similarity index 100% rename from examples/ADXL345_example.py rename to examples/ADXL345.py diff --git a/examples/L3GD20H_example.py b/examples/L3GD20H.py similarity index 100% rename from examples/L3GD20H_example.py rename to examples/L3GD20H.py diff --git a/examples/TMP112_example.py b/examples/TMP112.py similarity index 100% rename from examples/TMP112_example.py rename to examples/TMP112.py diff --git a/src/sts1_sensors/sensors/BME688.py b/src/sts1_sensors/sensors/BME688.py index b69f453..2771b68 100644 --- a/src/sts1_sensors/sensors/BME688.py +++ b/src/sts1_sensors/sensors/BME688.py @@ -1,10 +1,7 @@ -def twos_comp(val, bits): - if val & (1 << (bits - 1)) != 0: - val = val - (1 << bits) - return val - -import time import logging +import time + +from sts1_sensors.utils import twos_comp class bmeData: temperature = 0 From 6a9f22033649128014b9987a0617a07941835ebc Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Fri, 13 Dec 2024 16:26:23 +0100 Subject: [PATCH 08/26] address refactorings --- src/sts1_sensors/sensors/ADXL345.py | 15 +-------------- src/sts1_sensors/sensors/AbstractSensor.py | 16 +++++++++++++++- src/sts1_sensors/sensors/BMM150.py | 16 ++-------------- src/sts1_sensors/sensors/GUVA_C32.py | 21 ++++----------------- src/sts1_sensors/sensors/L3GD20H.py | 20 ++++---------------- src/sts1_sensors/sensors/TMP112.py | 15 +-------------- 6 files changed, 27 insertions(+), 76 deletions(-) diff --git a/src/sts1_sensors/sensors/ADXL345.py b/src/sts1_sensors/sensors/ADXL345.py index 60a20a5..850d82a 100644 --- a/src/sts1_sensors/sensors/ADXL345.py +++ b/src/sts1_sensors/sensors/ADXL345.py @@ -5,7 +5,6 @@ class ADXL345(AbstractSensor): """Digital accelerometer. """ - _possible_addresses = [0x1D, 0x3A, 0x3B, 0x53] _possible_datarates = [0.10, 0.20, 0.39, 0.78, 1.56, 3.13, 6.25, 12.5, 25, 50, 100, 200, 400, 800, 1600, 3200] _possible_ranges = [2, 4, 8, 16] @@ -20,7 +19,7 @@ def __init__(self, range=2, datarate=50, x_offset=0, y_offset=0, z_offset=0, add :param hexadecimal address: Physical address of the sensor on the board (see `i2cdetect` command). Allowed values: `[0x1D, 0x3A, 0x3B, 0x53]`. If None, the environment variable `STS1_SENSOR_ADDRESS_AVXL345` will be used. If environment variable is not found, 0x53 will be used. :param SMBus bus: A SMBus object. If None, this class will generate its own, defaults to None. """ - super().__init__(bus) + super().__init__(possible_addresses=[0x1D, 0x3A, 0x3B, 0x53], bus=bus) self.address = address or int(os.environ.get("STS1_SENSOR_ADDRESS_AVXL345", "0x53"), 16) self.datarate = datarate @@ -32,18 +31,6 @@ def __init__(self, range=2, datarate=50, x_offset=0, y_offset=0, z_offset=0, add self.bus.write_byte_data(self.address, 0x2D, 0b1000) self.bus.write_byte_data(self.address, 0x31, 0b1011 & self._possible_ranges.index(self.range)) - @property - def address(self): - return self._address - - @address.setter - def address(self, address): - if address not in self._possible_addresses: - s = f"The address {hex(address)} does not exist." - s += f" Choose one of {self._possible_addresses}." - raise ValueError(s) - self._address = address - @property def datarate(self): return self._datarate diff --git a/src/sts1_sensors/sensors/AbstractSensor.py b/src/sts1_sensors/sensors/AbstractSensor.py index be5908f..62957e2 100644 --- a/src/sts1_sensors/sensors/AbstractSensor.py +++ b/src/sts1_sensors/sensors/AbstractSensor.py @@ -3,7 +3,9 @@ from smbus2 import SMBus class AbstractSensor: - def __init__(self, bus=None): + def __init__(self, possible_addresses, bus=None): + self.possible_addresses = possible_addresses + if bus is None: self.manage_bus = True self.bus = SMBus(int(os.environ.get("STS1_SENSORS_I2C_BUS_ADDRESS", 1))) @@ -14,3 +16,15 @@ def __init__(self, bus=None): def __del__(self): if self.manage_bus: self.bus.close() + + @property + def address(self): + return self._address + + @address.setter + def address(self, address): + if address not in self.possible_addresses: + s = f"The address {hex(address)} does not exist." + s += f" Choose one of {self.possible_addresses}." + raise ValueError(s) + self._address = address diff --git a/src/sts1_sensors/sensors/BMM150.py b/src/sts1_sensors/sensors/BMM150.py index 6225003..dbcc047 100644 --- a/src/sts1_sensors/sensors/BMM150.py +++ b/src/sts1_sensors/sensors/BMM150.py @@ -6,11 +6,11 @@ class BMM150(AbstractSensor): """Geomagnetic sensor. """ - _possible_addresses = [0x10, 0x11, 0x12, 0x13] _possible_datarates = [1, 2, 6, 8, 15, 20, 25, 30] def __init__(self, datarate=8, address=None, bus=None): - super().__init__(bus) + super().__init__(possible_addresses=[0x10, 0x11, 0x12, 0x13], bus=bus) + self.address = address or int(os.environ.get("STS1_SENSOR_ADDRESS_BMM150", "0x10"), 16) self.datarate = datarate self.xyz_addresses = {"x": 0x42, "y": 0x44, "z": 0x46} @@ -21,18 +21,6 @@ def __init__(self, datarate=8, address=None, bus=None): self.bus.write_byte_data(self.addr, 0x51, 0b1111) self.bus.write_byte_data(self.addr, 0x52, 0b1111) - @property - def address(self): - return self._address - - @address.setter - def address(self, address): - if address not in self._possible_addresses: - s = f"The address {hex(address)} does not exist." - s += f" Choose one of {self._possible_addresses}." - raise ValueError(s) - self._address = address - @property def datarate(self): return self._datarate diff --git a/src/sts1_sensors/sensors/GUVA_C32.py b/src/sts1_sensors/sensors/GUVA_C32.py index 4c2b903..f8e2d11 100644 --- a/src/sts1_sensors/sensors/GUVA_C32.py +++ b/src/sts1_sensors/sensors/GUVA_C32.py @@ -4,14 +4,14 @@ from sts1_sensors.sensors.AbstractSensor import AbstractSensor class GUVA_C32(AbstractSensor): - """Ultraviolet light sensor.""" - - _possible_addresses = [0x39] + """Ultraviolet light sensor. + """ _possible_resolutions = [800, 400, 200, 100] _possible_ranges = [1, 2, 4, 8, 16, 32, 64, 128] def __init__(self, range=1, resolution=800, address=None, bus=None): - super().__init__(bus) + super().__init__(possible_addresses=[0x39], bus=bus) + self.address = address or int(os.environ.get("STS1_SENSOR_ADDRESS_GUVA_C32", "0x39"), 16) self.range = range self.resolution = resolution @@ -28,19 +28,6 @@ def __init__(self, range=1, resolution=800, address=None, bus=None): #msg = i2c_msg.write(self.addr, [0x05, 0b00000000 + self.poss_range_bin[self.poss_range.index(self.range_)]]) self.bus.i2c_rdwr(msg) - - @property - def address(self): - return self._address - - @address.setter - def address(self, address): - if address not in self._possible_addresses: - s = f"The address {hex(address)} does not exist." - s += f" Choose one of {self._possible_addresses}." - raise ValueError(s) - self._address = address - @property def range(self): return self._range diff --git a/src/sts1_sensors/sensors/L3GD20H.py b/src/sts1_sensors/sensors/L3GD20H.py index 0c4ba47..e7b2294 100644 --- a/src/sts1_sensors/sensors/L3GD20H.py +++ b/src/sts1_sensors/sensors/L3GD20H.py @@ -4,15 +4,15 @@ from sts1_sensors.utils import twos_comp class L3GD20H(AbstractSensor): - """Three-axis gyroscope""" - - _possible_addresses = [0x6A, 0x6B] + """Three-axis gyroscope + """ _possible_ranges = [245, 500, 2000] _possible_datarates = [12.5, 25, 50, 100, 200, 400, 800] _possible_datarates_bin = [(0, 1), (1, 1), (2, 1), (0, 0), (1, 0), (2, 0), (3, 0)] def __init__(self, range=245, datarate=12.5, address=None, bus=None): - super().__init__(bus) + super().__init__(possible_addresses=[0x6A, 0x6B], bus=bus) + self.address = address or int(os.environ.get("STS1_SENSOR_ADDRESS_L3GD20H", "0x6A"), 16) self.range = range self.datarate = datarate @@ -27,18 +27,6 @@ def __init__(self, range=245, datarate=12.5, address=None, bus=None): self.bus.write_byte_data(self.address, 0x23, (self._possible_ranges.index(self.range) << 4)) self.bus.write_byte_data(self.address, 0x39, b) - @property - def address(self): - return self._address - - @address.setter - def address(self, address): - if address not in self._possible_addresses: - s = f"The address {hex(address)} does not exist." - s += f" Choose one of {self._possible_addresses}." - raise ValueError(s) - self._address = address - @property def datarate(self): return self._datarate diff --git a/src/sts1_sensors/sensors/TMP112.py b/src/sts1_sensors/sensors/TMP112.py index a345489..57b6eec 100644 --- a/src/sts1_sensors/sensors/TMP112.py +++ b/src/sts1_sensors/sensors/TMP112.py @@ -7,7 +7,6 @@ class TMP112(AbstractSensor): """High-accuracy temperature sensor. """ - _possible_addresses = [0x48, 0x49, 0x4A, 0x4B] _possible_conversion_rates = [0.25, 1, 4, 8] def __init__(self, conversion_rate=1, extended_temp_range=True, address=None, bus=None): @@ -18,7 +17,7 @@ def __init__(self, conversion_rate=1, extended_temp_range=True, address=None, bu :param hexadecimal address: Physical address of the sensor on the board (see `i2cdetect` command). Allowed values: `[0x48, 0x49, 0x4A, 0x4B]`. If None, the environment variable `STS1_SENSOR_ADDRESS_TMP112` will be used. If environment variable is not found, 0x48 will be used. :param SMBus bus: A SMBus object. If None, this class will generate its own, defaults to None. """ - super().__init__(bus) + super().__init__(possible_addresses=[0x48, 0x49, 0x4A, 0x4B], bus=bus) self.address = address or int(os.environ.get("STS1_SENSOR_ADDRESS_TMP112", "0x48"), 16) self.conversion_rate = conversion_rate @@ -28,18 +27,6 @@ def __init__(self, conversion_rate=1, extended_temp_range=True, address=None, bu m = int(self.extended_temp_range) self.bus.i2c_rdwr(i2c_msg.write(self.address, [0b1,0b1100000,0b100000 + (c << 6) + (m << 4)])) - @property - def address(self): - return self._address - - @address.setter - def address(self, address): - if address not in self._possible_addresses: - s = f"The address {hex(address)} does not exist." - s += f" Choose one of {self._possible_addresses}." - raise ValueError(s) - self._address = address - @property def conversion_rate(self): return self._conversion_rate From 84bd3f2a22f6a14c4d66370092a7dd45cbc643cf Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Fri, 13 Dec 2024 16:29:09 +0100 Subject: [PATCH 09/26] readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 60d4684..2638518 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ pip install sts1-sensors ## For Developers * Install [just](https://github.com/casey/just?tab=readme-ov-file#pre-built-binaries): `curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to ~/bin` +* Add it to `~/.bashrc`: `export PATH="$PATH:$HOME/bin"` * Install the [package manager uv](https://docs.astral.sh/uv/getting-started/installation/): `curl -LsSf https://astral.sh/uv/install.sh | sh` * Add its path to your `~/.bashrc` such that the command `uv` is available: `export PATH=$HOME/.local/bin:$PATH` * Clone this repo: `git clone https://github.com/SpaceTeam/STS1_sensor_libraries` From 49651085b58654a170027cbdd0a262c22f798363 Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Fri, 13 Dec 2024 17:43:00 +0100 Subject: [PATCH 10/26] bug fix --- src/sts1_sensors/sensors/ADXL345.py | 4 +- src/sts1_sensors/sensors/BME688.py | 696 +++++++++++---------------- src/sts1_sensors/sensors/GUVA_C32.py | 4 +- src/sts1_sensors/sensors/L3GD20H.py | 4 +- 4 files changed, 290 insertions(+), 418 deletions(-) diff --git a/src/sts1_sensors/sensors/ADXL345.py b/src/sts1_sensors/sensors/ADXL345.py index 850d82a..61e3208 100644 --- a/src/sts1_sensors/sensors/ADXL345.py +++ b/src/sts1_sensors/sensors/ADXL345.py @@ -38,7 +38,7 @@ def datarate(self): @datarate.setter def datarate(self, datarate): if datarate not in self._possible_datarates: - s = f"The datarate {hex(datarate)} does not exist." + s = f"The datarate {datarate} does not exist." s += f" Choose one of {self._possible_datarates}." raise ValueError(s) self._datarate = datarate @@ -50,7 +50,7 @@ def range(self): @range.setter def range(self, range): if range not in self._possible_ranges: - s = f"The range {hex(range)} does not exist." + s = f"The range {range} does not exist." s += f" Choose one of {self._possible_ranges}." raise ValueError(s) self._range = range diff --git a/src/sts1_sensors/sensors/BME688.py b/src/sts1_sensors/sensors/BME688.py index 2771b68..93d86f8 100644 --- a/src/sts1_sensors/sensors/BME688.py +++ b/src/sts1_sensors/sensors/BME688.py @@ -1,456 +1,328 @@ -import logging +import os import time +from sts1_sensors.sensors.AbstractSensor import AbstractSensor from sts1_sensors.utils import twos_comp -class bmeData: - temperature = 0 - humidity = 0 - pressure = 0 - gasResistance = 0 - AQI = 0 - def __init__(self): - self.temperature = 0 - self.humidity = 0 - self.pressure = 0 - self.gasResistance = 0 - def setTemp(self, temp): - self.temperature = temp - def setHum(self, hum): - self.humidity = hum - def setPress(self, press): - self.pressure = press - def setGasRes(self, gasRes): - self.gasResistance = gasRes - def setAQI(self, AQI): - self.AQI = AQI +class BME688(AbstractSensor): + """Pressure, humidity, temperature and gas sensor. + """ + # over-sampling rates + _possible_temperature_osrs = [1, 2, 4, 8, 16] + _possible_humidity_osrs = [1, 2, 4, 8, 16] + _possible_pressure_osrs = [1, 2, 4, 8, 16] + _possible_iirs = [0, 1, 3, 7, 15, 31, 63, 127] + def __init__(self, temperature_osr=1, humidity_osr=1, pressure_osr=1, iir=0, + use_gas=True, gas_temperature=200, gas_time=1000, + address=None, bus=None): + super().__init__(possible_addresses=[0x76, 0x77], bus=bus) + self.address = address or int(os.environ.get("STS1_SENSOR_ADDRESS_BME688", "0x76"), 16) + self.temperature_osr = temperature_osr + self.humidity_osr = humidity_osr + self.pressure_osr = pressure_osr + self.iir = iir + self.use_gas = use_gas + self.gas_temperature = gas_temperature + self.gas_time = gas_time -class BME688: - poss_addr = [0x76, 0x77] - poss_tempOSR = [1, 2, 4, 8, 16] - poss_tempOSR_bin = [0b001, 0b010, 0b011, 0b100, 0b101] #0x74 7/6/5 - poss_humOSR = [1, 2, 4, 8, 16] - poss_humOSR_bin = [0b001, 0b010, 0b011, 0b100, 0b101] #0x72 2/1/0 - poss_pressOSR = [1, 2, 4, 8, 16] - poss_pressOSR_bin = [0b001, 0b010, 0b011, 0b100, 0b101] #0x74 4/3/2 - poss_IIR = [0, 1, 3, 7, 15, 31, 63, 127] - poss_IIR_bin = [0b000, 0b001, 0b010, 0b011, 0b100, 0b101, 0b110, 0b111] - - #poss_conversionrate = [0.25, 1, 4, 8] - #poss_conversionrate_bin = [0b00, 0b01, 0b10, 0b11] - + self.ambient_temp = 25 + self.par_t1 = 0 + self.par_t2 = 0 + self.par_t3 = 0 + self.par_h1 = 0 + self.par_h2 = 0 + self.par_h3 = 0 + self.par_h4 = 0 + self.par_h5 = 0 + self.par_h6 = 0 + self.par_h7 = 0 + self.par_p1 = 0 + self.par_p2 = 0 + self.par_p3 = 0 + self.par_p4 = 0 + self.par_p5 = 0 + self.par_p6 = 0 + self.par_p7 = 0 + self.par_p8 = 0 + self.par_p9 = 0 + self.par_p10 = 0 + self.par_g1 = 0 + self.par_g2 = 0 + self.par_g3 = 0 - config_set = 0 - addr = 0 - - #mode = 0b1 #1 ... extended mode (13-Bit -55°C - 150°C) 0 ... normal mode (12-Bit -55°C - 128°C) - #conversionrate = 0 - tempOSR = 0 - humOSR = 0 - pressOSR = 0 - tempRES = 0 - gasTemp = 0 - gasTime = 0 - IIR = 0 - #setConversionrate = False - #setAddr = False - setTempOSR = 0 - setHumOSR = 0 - setPressOSR = 0 - setGasTemp = 0 - setGas = 0 - setGasTime = 0 - setIIR = 0 - setupD = False - Error = False - consoleLog = True - - ambientTemp = 25 - - par_t1 = 0 - par_t2 = 0 - par_t3 = 0 - par_h1 = 0 - par_h2 = 0 - par_h3 = 0 - par_h4 = 0 - par_h5 = 0 - par_h6 = 0 - par_h7 = 0 - par_p1 = 0 - par_p2 = 0 - par_p3 = 0 - par_p4 = 0 - par_p5 = 0 - par_p6 = 0 - par_p7 = 0 - par_p8 = 0 - par_p9 = 0 - par_p10 = 0 - par_g1 = 0 - par_g2 = 0 - par_g3 = 0 - - def __init__(self, bus): - self.addr = 0x76 - self.bus = bus - self.output = bmeData() - def deact_consoleLog(self): - self.consoleLog = False - def set_address(self, address): - try: - self.poss_addr.index(address) - self.addr = address - self.setAddr = True - except ValueError: - s = "BME688: The address (" + str(hex(address)) + ") you entered for the sensor BME688 does not exist!" - if self.consoleLog: - logging.error(s) - s = "BME688: Try one of the following:" - for value in self.poss_addr: - s = s + str(hex(value)) + " " - if self.consoleLog: - logging.info(s) - logging.error("BME688: not initialized!!!") - def set_tempOSR(self, OSR): #temperateure Oversamplingrate - try: - self.poss_tempOSR.index(OSR) - self.tempOSR = OSR - self.setTempOSR = True - except ValueError: - s = "BME688: The temperature oversamplingrate (" + str(OSR) + ") you entered for the sensor BME688 does not exist!" - if self.consoleLog: - logging.error(s) - s = "BME688: Try one of the following:" - for value in self.poss_tempOSR: - s = s + str(value) + " " - if self.consoleLog: - logging.info(s) - logging.error("BME688: temperature oversamplingrate not set!!!") - def set_humOSR(self, OSR): #humidity Oversamplingrate - try: - self.poss_humOSR.index(OSR) - self.humOSR = OSR - self.setHumOSR = True - except ValueError: - s = "BME688: The humidity oversamplingrate (" + str(OSR) + ") you entered for the sensor BME688 does not exist!" - if self.consoleLog: - logging.error(s) - s = "BME688: Try one of the following:" - for value in self.poss_humOSR: - s = s + str(value) + " " - if self.consoleLog: - logging.info(s) - logging.error("BME688: humidity oversamplingrate not set!!!") - def set_pressOSR(self, OSR): #pressure Oversamplingrate - try: - self.poss_pressOSR.index(OSR) - self.pressOSR = OSR - self.setPressOSR = True - except ValueError: - s = "BME688: The pressure oversamplingrate (" + str(OSR) + ") you entered for the sensor BME688 does not exist!" - if self.consoleLog: - logging.error(s) - s = "BME688: Try one of the following:" - for value in self.poss_pressOSR: - s = s + str(value) + " " - if self.consoleLog: - logging.info(s) - logging.error("BME688: pressure oversamplingrate not set!!!") - def set_IIR(self, IIR): - try: - self.poss_IIR.index(IIR) - self.IIR = IIR - self.setIIR = True - except ValueError: - s = "BME688: The IIR value (" + str(IIR) + ") you entered for the sensor BME688 does not exist!" - if self.consoleLog: - logging.error(s) - s = "BME688: Try one of the following:" - for value in self.poss_IIR: - s = s + str(value) + " " - if self.consoleLog: - logging.info(s) - logging.error("BME688: temperature oversamplingrate not set!!!") - def set_gasTemp(self, temp): - if temp >= 200 and temp <= 400: - self.gasTemp = temp - self.setGasTemp = True - else: - s = "BME688: The temperature value (" + str(temp) + ") you entered for the sensor BME688 is outside of the possible range!" - logging.error(s) - s = "BME688: Try a value between 200-400°C" - logging.info(s) - logging.error("BME688: temperature oversamplingrate not set!!!") - def set_gasTime(self, time): - if time > 0 and time < 4096: - self.gasTime = time - self.setGasTime = True - else: - s = "BME688: The time (" + str(temp) + ") you entered for the sensor BME688 is outside of the possible range!" - logging.error(s) - s = "BME688: Try a value between 1-4096ms" - logging.info(s) - logging.error("BME688: temperature oversamplingrate not set!!!") - def setup(self): - if self.setAddr and self.setTempOSR and self.setIIR and self.setHumOSR and self.setPressOSR: - try: - if self.setGasTemp and self.setGasTime: - #Enable GasTemp - #print("setGas") - - self.bus.write_byte_data(self.addr, 0x72, 0b00000000 + (self.poss_humOSR_bin[self.poss_humOSR.index(self.humOSR)] << 0)) #set humidity oversamplingrate - self.bus.write_byte_data(self.addr, 0x74, 0b00000000 + (self.poss_tempOSR_bin[self.poss_tempOSR.index(self.tempOSR)] << 5) + (self.poss_pressOSR_bin[self.poss_pressOSR.index(self.pressOSR)] << 2)) #set temperature oversamplingrate - self.bus.write_byte_data(self.addr, 0x75, 0b00000000 + (self.poss_IIR_bin[self.poss_IIR.index(self.IIR)] << 2)) - - self.bus.write_byte_data(self.addr, 0x71, 0b00100000) #enable gas conversion and activate heater step 0 - if self.gasTime / 1 <= 64: - gasTimeSet = 0b00000000 + int(self.gasTime) - elif self.gasTime / 4 <= 64: - gasTimeSet = 0b01000000 + int(self.gasTime/4) - elif self.gasTime / 16 <= 64: - gasTimeSet = 0b10000000 + int(self.gasTime/16) - elif self.gasTime / 64 <= 64: - gasTimeSet = 0b11000000 + int(self.gasTime/64) - #print("gasTimeSet: {0:08b}".format(gasTimeSet)) - self.bus.write_byte_data(self.addr, 0x64, gasTimeSet) - self.setGas = 1 - else: - #only Temperature, Humidity and Pressure - - self.bus.write_byte_data(self.addr, 0x72, 0b00000000 + (self.poss_humOSR_bin[self.poss_humOSR.index(self.humOSR)] << 0)) #set humidity oversamplingrate - self.bus.write_byte_data(self.addr, 0x74, 0b00000000 + (self.poss_tempOSR_bin[self.poss_tempOSR.index(self.tempOSR)] << 5) + (self.poss_pressOSR_bin[self.poss_pressOSR.index(self.pressOSR)] << 2)) #set temperature oversamplingrate - self.bus.write_byte_data(self.addr, 0x75, 0b00000000 + (self.poss_IIR_bin[self.poss_IIR.index(self.IIR)] << 2)) - self.bus.write_byte_data(self.addr, 0x71, 0b00000000) - self.bus.write_byte_data(self.addr, 0x64, 0b00000000) - self.bus.write_byte_data(self.addr, 0x5A, 0b00000000) - self.bus.write_byte_data(self.addr, 0x70, 0b00001000) - except OSError as e: - self.Error = True - if e.errno == 121: - #print("Remote I/O Error: The device is not responding on the bus. Therefore it will be ignored") - logging.error("BME688: Remote I/O Error: The device is not responding on the bus. Therefore it will be ignored") - else: - #print(f"An error occurred: {e}") - logging.error(f"BME688: An error occurred: {e}") - return None - - - #all settings correct - - - self.setupD = True - - #get comp values - par_t1_LSB = self.bus.read_byte_data(self.addr, 0xE9) - par_t1_MSB = self.bus.read_byte_data(self.addr, 0xEA) - self.par_t1 = twos_comp((par_t1_MSB<<8) + par_t1_LSB, 16) - - par_t2_LSB = self.bus.read_byte_data(self.addr, 0x8A) - par_t2_MSB = self.bus.read_byte_data(self.addr, 0x8B) - self.par_t2 = twos_comp((par_t2_MSB<<8) + par_t2_LSB, 16) - - self.par_t3 = twos_comp(self.bus.read_byte_data(self.addr, 0x8C),8) - - par_h1_LSB = self.bus.read_byte_data(self.addr, 0xE2) - par_h1_MSB = self.bus.read_byte_data(self.addr, 0xE3) - self.par_h1 = twos_comp((par_h1_MSB<<4) + (par_h1_LSB & 0b00001111) ,12) - - par_h2_LSB = self.bus.read_byte_data(self.addr, 0xE2) - par_h2_MSB = self.bus.read_byte_data(self.addr, 0xE1) - self.par_h2 = twos_comp((par_h2_MSB<<4) + ((par_h2_LSB & 0b11110000) >> 4), 12) - - self.par_h3 = twos_comp(self.bus.read_byte_data(self.addr, 0xE4),8) - - self.par_h4 = twos_comp(self.bus.read_byte_data(self.addr, 0xE5),8) - - self.par_h5 = twos_comp(self.bus.read_byte_data(self.addr, 0xE6),8) - - self.par_h6 = twos_comp(self.bus.read_byte_data(self.addr, 0xE7),8) - - self.par_h7 = twos_comp(self.bus.read_byte_data(self.addr, 0xE8),8) - - par_p1_LSB = self.bus.read_byte_data(self.addr, 0x8E) - par_p1_MSB = self.bus.read_byte_data(self.addr, 0x8F) - self.par_p1 = (par_p1_MSB<<8) + par_p1_LSB - - par_p2_LSB = self.bus.read_byte_data(self.addr, 0x90) - par_p2_MSB = self.bus.read_byte_data(self.addr, 0x91) - self.par_p2 = twos_comp((par_p2_MSB<<8) + par_p2_LSB,16) - - self.par_p3 = twos_comp(self.bus.read_byte_data(self.addr, 0x92),8) - - par_p4_LSB = self.bus.read_byte_data(self.addr, 0x94) - par_p4_MSB = self.bus.read_byte_data(self.addr, 0x95) - self.par_p4 = twos_comp((par_p4_MSB<<8) + par_p4_LSB,16) - - par_p5_LSB = self.bus.read_byte_data(self.addr, 0x96) - par_p5_MSB = self.bus.read_byte_data(self.addr, 0x97) - self.par_p5 = twos_comp((par_p5_MSB<<8) + par_p5_LSB,16) - - self.par_p6 = twos_comp(self.bus.read_byte_data(self.addr, 0x99),8) - - self.par_p7 = twos_comp(self.bus.read_byte_data(self.addr, 0x98),8) - - par_p8_LSB = self.bus.read_byte_data(self.addr, 0x9C) - par_p8_MSB = self.bus.read_byte_data(self.addr, 0x9D) - self.par_p8 = twos_comp((par_p8_MSB<<8) + par_p8_LSB,16) - - par_p9_LSB = self.bus.read_byte_data(self.addr, 0x9E) - par_p9_MSB = self.bus.read_byte_data(self.addr, 0x9F) - self.par_p9 = twos_comp((par_p9_MSB<<8) + par_p9_LSB,16) - - self.par_p10 = twos_comp(self.bus.read_byte_data(self.addr, 0xA0),8) - - self.par_g1 = twos_comp(self.bus.read_byte_data(self.addr, 0xED),8) - - par_g2_LSB = self.bus.read_byte_data(self.addr, 0xEB) - par_g2_MSB = self.bus.read_byte_data(self.addr, 0xEC) - self.par_g2 = twos_comp((par_g2_MSB<<8) + par_g2_LSB,16) - - self.par_g3 = twos_comp(self.bus.read_byte_data(self.addr, 0xEE),8) - - self.res_heat_range = (self.bus.read_byte_data(self.addr, 0x02) & 0b00110000) >> 4 - - self.res_heat_val = self.bus.read_byte_data(self.addr, 0x00) - - - if self.setGas == True: - varg1 = (self.par_g1 / 16.0) + 49.0 - varg2 = ((self.par_g2 / 32768.0) * 0.0005) + 0.00235 - varg3 = self.par_g3 / 1024.0 - varg4 = varg1 * (1.0 + (varg2 * self.gasTemp)) - varg5 = varg4 + (varg3 * self.ambientTemp) - tempset = (3.4 * ((varg5 * (4.0 / (4.0 + self.res_heat_range)) * (1.0 / (1.0 + (self.res_heat_val * 0.002)))) - 25)) - #print("tempset: {0:08b}".format(int(tempset))) - self.bus.write_byte_data(self.addr, 0x5A, int(tempset)) + self._finish_setup() + + @property + def temperature_osr(self): + return self._temperature_osr + + @temperature_osr.setter + def temperature_osr(self, temperature_osr): + if temperature_osr not in self._possible_temperature_osrs: + s = f"The temperature_osr {temperature_osr} does not exist." + s += f" Choose one of {self._possible_temperature_osrs}." + raise ValueError(s) + self._temperature_osr = temperature_osr + + @property + def humidity_osr(self): + return self._humidity_osr + + @humidity_osr.setter + def humidity_osr(self, humidity_osr): + if humidity_osr not in self._possible_humidity_osrs: + s = f"The humidity_osr {humidity_osr} does not exist." + s += f" Choose one of {self._possible_humidity_osrs}." + raise ValueError(s) + self._humidity_osr = humidity_osr + + @property + def pressure_osr(self): + return self._pressure_osr + + @pressure_osr.setter + def pressure_osr(self, pressure_osr): + if pressure_osr not in self._possible_pressure_osrs: + s = f"The pressure_osr {pressure_osr} does not exist." + s += f" Choose one of {self._possible_pressure_osrs}." + raise ValueError(s) + self._pressure_osr = pressure_osr + + @property + def iir(self): + return self._iir + + @iir.setter + def iir(self, iir): + if iir not in self._possible_iirs: + s = f"The iir {iir} does not exist." + s += f" Choose one of {self._possible_iirs}." + raise ValueError(s) + self._iir = iir + + @property + def gas_temperature(self): + return self._gas_temperature + + @gas_temperature.setter + def gas_temperature(self, gas_temperature): + if gas_temperature < 200 or gas_temperature > 400: + raise ValueError(f"gas_temperature has to be between 200° and 400° Celcius (both inclusive) but was {gas_temperature}.") + self._gas_temperature = gas_temperature + + @property + def gas_time(self): + return self._gas_time + + @gas_time.setter + def gas_time(self, gas_time): + if gas_time <= 0 or gas_time >= 4096: + raise ValueError(f"gas_time has to be between 0 and 4095 ms (both inclusive) but was {gas_time}.") + self._gas_time = gas_time + + def _finish_setup(self): + self.bus.write_byte_data(self.address, 0x72, (self._possible_humidity_osrs.index(self.humOSR))) + self.bus.write_byte_data(self.address, 0x74, (self._possible_temperature_osrs.index(self.tempOSR) << 5) + (self._possible_pressure_osrs.index(self.pressOSR) << 2)) + self.bus.write_byte_data(self.address, 0x75, (self._possible_iirs.index(self.IIR) << 2)) + + if self.use_gas: + # enable gas conversion and activate heater step 0 + self.bus.write_byte_data(self.address, 0x71, 0b00100000) + if self.gas_time <= 64: + t = self.gas_time + elif self.gas_time / 4 <= 64: + t = 0b01000000 + self.gas_time // 4 + elif self.gas_time / 16 <= 64: + t = 0b10000000 + self.gas_time // 16 + elif self.gas_time / 64 <= 64: + t = 0b11000000 + self.gas_time // 64 + + self.bus.write_byte_data(self.address, 0x64, t) - #s = "t1:{0}\nt2:{1}\nt3:{2}\np1:{3}\np2:{4}\np3:{5}\np4:{6}\np5:{7}\np6:{8}\np7:{9}\np8:{10}\np9:{11}\np10:{12}\nh1:{13}\nh2:{14}\nh3:{15}\nh4:{16}\nh5:{17}\nh6:{18}\nh7:{19}\ng1:{20}\ng2:{21}\ng3:{22}\n" - #print(s.format(self.par_t1, self.par_t2, self.par_t3, self.par_p1, self.par_p2, self.par_p3, self.par_p4, self.par_p5, self.par_p6, self.par_p7, self.par_p8, self.par_p9, self.par_p10, self.par_h1, self.par_h2, self.par_h3, self.par_h4, self.par_h5, self.par_h6, self.par_h7, self.par_g1, self.par_g2, self.par_g3)) - if self.consoleLog: - #check things and eveluate the resolution of the temperature sensor - if self.IIR != 0: - self.tempRES = 20 #20bit - else: - self.tempRES = 16 + (self.poss_tempOSR_bin[self.poss_tempOSR.index(self.tempOSR)] - 1) - logging.info("BME688: Setup finished, sensor ready.") else: - if self.consoleLog: - logging.error("BME688: Setup failed! Settings incorrect") + # only Temperature, Humidity and Pressure + self.bus.write_byte_data(self.address, 0x71, 0) + self.bus.write_byte_data(self.address, 0x64, 0) + self.bus.write_byte_data(self.address, 0x5A, 0) + self.bus.write_byte_data(self.address, 0x70, 0b1000) + + #get comp values + par_t1_LSB = self.bus.read_byte_data(self.address, 0xE9) + par_t1_MSB = self.bus.read_byte_data(self.address, 0xEA) + self.par_t1 = twos_comp((par_t1_MSB<<8) + par_t1_LSB, 16) + + par_t2_LSB = self.bus.read_byte_data(self.address, 0x8A) + par_t2_MSB = self.bus.read_byte_data(self.address, 0x8B) + self.par_t2 = twos_comp((par_t2_MSB<<8) + par_t2_LSB, 16) + + self.par_t3 = twos_comp(self.bus.read_byte_data(self.address, 0x8C),8) + + par_h1_LSB = self.bus.read_byte_data(self.address, 0xE2) + par_h1_MSB = self.bus.read_byte_data(self.address, 0xE3) + self.par_h1 = twos_comp((par_h1_MSB<<4) + (par_h1_LSB & 0b00001111), 12) + + par_h2_LSB = self.bus.read_byte_data(self.address, 0xE2) + par_h2_MSB = self.bus.read_byte_data(self.address, 0xE1) + self.par_h2 = twos_comp((par_h2_MSB<<4) + ((par_h2_LSB & 0b11110000) >> 4), 12) + + self.par_h3 = twos_comp(self.bus.read_byte_data(self.address, 0xE4),8) + self.par_h4 = twos_comp(self.bus.read_byte_data(self.address, 0xE5),8) + self.par_h5 = twos_comp(self.bus.read_byte_data(self.address, 0xE6),8) + self.par_h6 = twos_comp(self.bus.read_byte_data(self.address, 0xE7),8) + self.par_h7 = twos_comp(self.bus.read_byte_data(self.address, 0xE8),8) + + par_p1_LSB = self.bus.read_byte_data(self.address, 0x8E) + par_p1_MSB = self.bus.read_byte_data(self.address, 0x8F) + self.par_p1 = (par_p1_MSB<<8) + par_p1_LSB + + par_p2_LSB = self.bus.read_byte_data(self.address, 0x90) + par_p2_MSB = self.bus.read_byte_data(self.address, 0x91) + self.par_p2 = twos_comp((par_p2_MSB<<8) + par_p2_LSB,16) + + self.par_p3 = twos_comp(self.bus.read_byte_data(self.address, 0x92),8) + + par_p4_LSB = self.bus.read_byte_data(self.address, 0x94) + par_p4_MSB = self.bus.read_byte_data(self.address, 0x95) + self.par_p4 = twos_comp((par_p4_MSB<<8) + par_p4_LSB,16) + + par_p5_LSB = self.bus.read_byte_data(self.address, 0x96) + par_p5_MSB = self.bus.read_byte_data(self.address, 0x97) + self.par_p5 = twos_comp((par_p5_MSB<<8) + par_p5_LSB,16) + + self.par_p6 = twos_comp(self.bus.read_byte_data(self.address, 0x99),8) + + self.par_p7 = twos_comp(self.bus.read_byte_data(self.address, 0x98),8) + + par_p8_LSB = self.bus.read_byte_data(self.address, 0x9C) + par_p8_MSB = self.bus.read_byte_data(self.address, 0x9D) + self.par_p8 = twos_comp((par_p8_MSB<<8) + par_p8_LSB,16) + + par_p9_LSB = self.bus.read_byte_data(self.address, 0x9E) + par_p9_MSB = self.bus.read_byte_data(self.address, 0x9F) + self.par_p9 = twos_comp((par_p9_MSB<<8) + par_p9_LSB,16) + + self.par_p10 = twos_comp(self.bus.read_byte_data(self.address, 0xA0),8) + + self.par_g1 = twos_comp(self.bus.read_byte_data(self.address, 0xED),8) + + par_g2_LSB = self.bus.read_byte_data(self.address, 0xEB) + par_g2_MSB = self.bus.read_byte_data(self.address, 0xEC) + self.par_g2 = twos_comp((par_g2_MSB<<8) + par_g2_LSB,16) + + self.par_g3 = twos_comp(self.bus.read_byte_data(self.address, 0xEE),8) + + self.res_heat_range = (self.bus.read_byte_data(self.address, 0x02) & 0b00110000) >> 4 + + self.res_heat_val = self.bus.read_byte_data(self.address, 0x00) + + if self.use_gas: + varg1 = (self.par_g1 / 16) + 49 + varg2 = ((self.par_g2 / 32768) * 0.0005) + 0.00235 + varg3 = self.par_g3 / 1024 + varg4 = varg1 * (1 + (varg2 * self.gasTemp)) + varg5 = varg4 + (varg3 * self.ambient_temp) + tempset = (3.4 * ((varg5 * (4 / (4 + self.res_heat_range)) * (1 / (1 + (self.res_heat_val * 0.002)))) - 25)) + + self.bus.write_byte_data(self.address, 0x5A, int(tempset)) + def getVal(self): if self.setupD: - #initiate force mode -> single measurement - self.bus.write_byte_data(self.addr, 0x70, 0b00000000) - self.bus.write_byte_data(self.addr, 0x74, 0b00000001 + (self.poss_tempOSR_bin[self.poss_tempOSR.index(self.tempOSR)] << 5) + (self.poss_pressOSR_bin[self.poss_pressOSR.index(self.pressOSR)] << 2)) - - #if Gas measurement mode is active wait until gas measurement is finished - gasReady = False - if self.setGas == True: - for i in range(10): - if self.bus.read_byte_data(self.addr, 0x1D) != 0b10000000: + # initiate force mode -> single measurement + self.bus.write_byte_data(self.address, 0x70, 0) + self.bus.write_byte_data(self.address, 0x74, 1 + (self._possible_temperature_osrs.index(self.tempOSR) << 5) + (self._possible_pressure_osrs.index(self.pressOSR) << 2)) + + # if gas measurement mode is active wait until gas measurement is finished + gas_ready = False + if self.use_gas: + for _ in range(10): + if self.bus.read_byte_data(self.address, 0x1D) != 0b10000000: time.sleep(0.05) continue - gasReady = True - """ - while self.bus.read_byte_data(self.addr, 0x1D) != 0b10000000: - #time.sleep(0.5) - i = 0 - #do something - """ - #get temperature adc value - temp_adc_MSB = self.bus.read_byte_data(self.addr, 0x22) - temp_adc_LSB= self.bus.read_byte_data(self.addr, 0x23) - temp_adc_XSB = self.bus.read_byte_data(self.addr, 0x24) + gas_ready = True + + # get temperature adc value + temp_adc_MSB = self.bus.read_byte_data(self.address, 0x22) + temp_adc_LSB = self.bus.read_byte_data(self.address, 0x23) + temp_adc_XSB = self.bus.read_byte_data(self.address, 0x24) temp_adc = ((temp_adc_XSB & 0b11110000) >> 4) + (temp_adc_LSB << 4) + (temp_adc_MSB << 12) - #compensate temperature adc value - vart1 = ((temp_adc / 16384.0) - (self.par_t1 / 1024.0)) * self.par_t2 - vart2 = (((temp_adc / 131072.0) - (self.par_t1 / 8192.0)) * ((temp_adc / 131072.0) - (self.par_t1 / 8192.0))) * self.par_t3 * 16.0 + # compensate temperature adc value + vart1 = ((temp_adc / 16384) - (self.par_t1 / 1024)) * self.par_t2 + vart2 = (((temp_adc / 131072) - (self.par_t1 / 8192)) * ((temp_adc / 131072) - (self.par_t1 / 8192))) * self.par_t3 * 16 t_fine = vart1 + vart2 - temp_comp = t_fine / 5120.0 + temp_comp = t_fine / 5120 - self.ambientTemp = temp_comp + self.ambient_temp = temp_comp #get humidity adc value - hum_adc_MSB = self.bus.read_byte_data(self.addr, 0x25) - hum_adc_LSB= self.bus.read_byte_data(self.addr, 0x26) + hum_adc_MSB = self.bus.read_byte_data(self.address, 0x25) + hum_adc_LSB = self.bus.read_byte_data(self.address, 0x26) hum_adc = (hum_adc_LSB << 0) + (hum_adc_MSB << 8) #compensate humidity adc value - varh1 = hum_adc - ((self.par_h1 * 16.0) + ((self.par_h3 / 2.0) * temp_comp)) - varh2 = varh1 * ((self.par_h2 / 262144.0) * (1.0 + ((self.par_h4 / 16384.0) * temp_comp) + ((self.par_h5 / 1048576.0) * temp_comp * temp_comp))) - varh3 = self.par_h6 / 16384.0 - varh4 = self.par_h7 / 2097152.0 - hum_comp = varh2 + ((varh3 + (varh4 * temp_comp)) * varh2 *varh2) + varh1 = hum_adc - ((self.par_h1 * 16) + ((self.par_h3 / 2) * temp_comp)) + varh2 = varh1 * ((self.par_h2 / 262144) * (1 + ((self.par_h4 / 16384) * temp_comp) + ((self.par_h5 / 1048576) * temp_comp * temp_comp))) + varh3 = self.par_h6 / 16384 + varh4 = self.par_h7 / 2097152 + hum_comp = varh2 + ((varh3 + (varh4 * temp_comp)) * varh2 * varh2) #get pressure adc value - press_adc_MSB = self.bus.read_byte_data(self.addr, 0x1F) - press_adc_LSB= self.bus.read_byte_data(self.addr, 0x20) - press_adc_XSB = self.bus.read_byte_data(self.addr, 0x21) + press_adc_MSB = self.bus.read_byte_data(self.address, 0x1F) + press_adc_LSB = self.bus.read_byte_data(self.address, 0x20) + press_adc_XSB = self.bus.read_byte_data(self.address, 0x21) press_adc = ((press_adc_XSB & 0b11110000) >> 4) + (press_adc_LSB << 4) + (press_adc_MSB << 12) #compensate pressure adc value - varp1 = (t_fine / 2.0) - 64000.0 - varp2 = varp1 * varp1 * (self.par_p6 / 131072.0) - varp2 = varp2 + (varp1 * (self.par_p5 * 2.0)) - varp2 = (varp2 / 4.0) + (self.par_p4 * 65536.0) - varp1 = (((self.par_p3 * varp1 * varp1) / 16384.0) + (self.par_p2 * varp1)) / 524288.0 - varp1 = (1.0 + (varp1 / 32768.0)) * self.par_p1 - press_comp = 1048576.0 - press_adc - press_comp = ((press_comp - (varp2 / 4096.0)) * 6250.0) / varp1 - varp1 = (self.par_p9 * press_comp * press_comp) / 2147483648.0 - varp2 = press_comp * (self.par_p8 / 32768.0) - varp3 = (press_comp / 256.0) * (press_comp / 256.0) * (press_comp / 256.0) * (self.par_p10 / 131072.0) - press_comp = press_comp + (varp1 + varp2 + varp3 + (self.par_p7 * 128.0)) / 16.0 + varp1 = (t_fine / 2) - 64000 + varp2 = varp1 * varp1 * (self.par_p6 / 131072) + varp2 = varp2 + (varp1 * (self.par_p5 * 2)) + varp2 = (varp2 / 4) + (self.par_p4 * 65536) + varp1 = (((self.par_p3 * varp1 * varp1) / 16384) + (self.par_p2 * varp1)) / 524288 + varp1 = (1 + (varp1 / 32768)) * self.par_p1 + press_comp = 1048576 - press_adc + press_comp = ((press_comp - (varp2 / 4096)) * 6250) / varp1 + varp1 = (self.par_p9 * press_comp * press_comp) / 2147483648 + varp2 = press_comp * (self.par_p8 / 32768) + varp3 = (press_comp / 256) * (press_comp / 256) * (press_comp / 256) * (self.par_p10 / 131072) + press_comp = press_comp + (varp1 + varp2 + varp3 + (self.par_p7 * 128)) / 16 #read gasresistence if enabled gas_res = 0 - if self.setGas == True and gasReady == True: - gas_adc_MSB = self.bus.read_byte_data(self.addr, 0x2C) - gas_adc_LSB= self.bus.read_byte_data(self.addr, 0x2D) - gas_adc = ((gas_adc_LSB & 0b11000000) >> 6) + (press_adc_MSB << 2) + if self.use_gas and gas_ready: + gas_adc_MSB = self.bus.read_byte_data(self.address, 0x2C) + gas_adc_LSB = self.bus.read_byte_data(self.address, 0x2D) + gas_adc = ((gas_adc_LSB & 0b11000000) >> 6) + (gas_adc_MSB << 2) - gas_range_XSB = self.bus.read_byte_data(self.addr, 0x2D) + gas_range_XSB = self.bus.read_byte_data(self.address, 0x2D) gas_range = gas_range_XSB & 0b00001111 - varg1 = 262144 >> gas_range varg2 = gas_adc - 512 varg2 *= 3 varg2 = 4096 + varg2 - gas_res = (10000.0 * varg1) / varg2 - gas_res *= 100.0 + gas_res = (10000 * varg1) / varg2 + gas_res *= 100 - #write values into object - self.output.setHum(hum_comp) - self.output.setTemp(temp_comp) - self.output.setPress(press_comp) - self.output.setGasRes(gas_res) #recalculate and set temperature - - if self.setGas == True: - varg1 = (self.par_g1 / 16.0) + 49.0 - varg2 = ((self.par_g2 / 32768.0) * 0.0005) + 0.00235 - varg3 = self.par_g3 / 1024.0 - varg4 = varg1 * (1.0 + (varg2 * self.gasTemp)) - varg5 = varg4 + (varg3 * self.ambientTemp) - tempset = (3.4 * ((varg5 * (4.0 / (4.0 + self.res_heat_range)) * (1.0 / (1.0 + (self.res_heat_val * 0.002)))) - 25)) - self.bus.write_byte_data(self.addr, 0x5A, int(tempset)) + if self.use_gas: + varg1 = (self.par_g1 / 16) + 49 + varg2 = ((self.par_g2 / 32768) * 0.0005) + 0.00235 + varg3 = self.par_g3 / 1024 + varg4 = varg1 * (1 + (varg2 * self.gasTemp)) + varg5 = varg4 + (varg3 * self.ambient_temp) + tempset = (3.4 * ((varg5 * (4 / (4 + self.res_heat_range)) * (1 / (1 + (self.res_heat_val * 0.002)))) - 25)) + self.bus.write_byte_data(self.address, 0x5A, int(tempset)) #turn off heater - self.bus.write_byte_data(self.addr, 0x70, 0b00001000) - - #return data object - return self.output - else: - if self.consoleLog: - if self.Error: - #print("GUVA_C32 not available") - logging.error("BME688: not available") - else: - #print("Setup not finished") - logging.error("BME688: Setup not finished") \ No newline at end of file + self.bus.write_byte_data(self.address, 0x70, 0b1000) + + return { + "humidity": hum_comp, + "temperature": temp_comp, + "pressure": press_comp, + "gas_resistance": gas_res, + } diff --git a/src/sts1_sensors/sensors/GUVA_C32.py b/src/sts1_sensors/sensors/GUVA_C32.py index f8e2d11..b45096c 100644 --- a/src/sts1_sensors/sensors/GUVA_C32.py +++ b/src/sts1_sensors/sensors/GUVA_C32.py @@ -35,7 +35,7 @@ def range(self): @range.setter def range(self, range): if range not in self._possible_ranges: - s = f"The range {hex(range)} does not exist." + s = f"The range {range} does not exist." s += f" Choose one of {self._possible_ranges}." raise ValueError(s) self._range = range @@ -47,7 +47,7 @@ def resolution(self): @resolution.setter def resolution(self, resolution): if resolution not in self._possible_resolutions: - s = f"The resolution {hex(resolution)} does not exist." + s = f"The resolution {resolution} does not exist." s += f" Choose one of {self._possible_resolutions}." raise ValueError(s) self._resolution = resolution diff --git a/src/sts1_sensors/sensors/L3GD20H.py b/src/sts1_sensors/sensors/L3GD20H.py index e7b2294..4f9be47 100644 --- a/src/sts1_sensors/sensors/L3GD20H.py +++ b/src/sts1_sensors/sensors/L3GD20H.py @@ -34,7 +34,7 @@ def datarate(self): @datarate.setter def datarate(self, datarate): if datarate not in self._possible_datarates: - s = f"The datarate {hex(datarate)} does not exist." + s = f"The datarate {datarate} does not exist." s += f" Choose one of {self._possible_datarates}." raise ValueError(s) self._datarate = datarate @@ -46,7 +46,7 @@ def range(self): @range.setter def range(self, range): if range not in self._possible_ranges: - s = f"The range {hex(range)} does not exist." + s = f"The range {range} does not exist." s += f" Choose one of {self._possible_ranges}." raise ValueError(s) self._range = range From 2df83877cc3f1317737a84ea3c635893831a25a4 Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Fri, 13 Dec 2024 17:43:08 +0100 Subject: [PATCH 11/26] bmm150 refactor --- src/sts1_sensors/sensors/BMM150.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sts1_sensors/sensors/BMM150.py b/src/sts1_sensors/sensors/BMM150.py index dbcc047..a860b0d 100644 --- a/src/sts1_sensors/sensors/BMM150.py +++ b/src/sts1_sensors/sensors/BMM150.py @@ -28,7 +28,7 @@ def datarate(self): @datarate.setter def datarate(self, datarate): if datarate not in self._possible_datarates: - s = f"The datarate {hex(datarate)} does not exist." + s = f"The datarate {datarate} does not exist." s += f" Choose one of {self._possible_datarates}." raise ValueError(s) self._datarate = datarate From c2510ad7a2d8dd026f995be040be8a86b70aa39e Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Fri, 13 Dec 2024 17:48:08 +0100 Subject: [PATCH 12/26] bme688 refactor --- src/sts1_sensors/sensors/BME688.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/sts1_sensors/sensors/BME688.py b/src/sts1_sensors/sensors/BME688.py index 93d86f8..ddd26f4 100644 --- a/src/sts1_sensors/sensors/BME688.py +++ b/src/sts1_sensors/sensors/BME688.py @@ -123,9 +123,15 @@ def gas_time(self, gas_time): self._gas_time = gas_time def _finish_setup(self): - self.bus.write_byte_data(self.address, 0x72, (self._possible_humidity_osrs.index(self.humOSR))) - self.bus.write_byte_data(self.address, 0x74, (self._possible_temperature_osrs.index(self.tempOSR) << 5) + (self._possible_pressure_osrs.index(self.pressOSR) << 2)) - self.bus.write_byte_data(self.address, 0x75, (self._possible_iirs.index(self.IIR) << 2)) + h = self._possible_humidity_osrs.index(self.humidity_osr) + self.bus.write_byte_data(self.address, 0x72, h) + + t = self._possible_temperature_osrs.index(self.temperature_osr) << 5 + p = self._possible_pressure_osrs.index(self.pressure_osr) << 2 + self.bus.write_byte_data(self.address, 0x74, t + p) + + iir = self._possible_iirs.index(self.iir) << 2 + self.bus.write_byte_data(self.address, 0x75, iir) if self.use_gas: # enable gas conversion and activate heater step 0 @@ -231,7 +237,10 @@ def getVal(self): if self.setupD: # initiate force mode -> single measurement self.bus.write_byte_data(self.address, 0x70, 0) - self.bus.write_byte_data(self.address, 0x74, 1 + (self._possible_temperature_osrs.index(self.tempOSR) << 5) + (self._possible_pressure_osrs.index(self.pressOSR) << 2)) + + t = self._possible_temperature_osrs.index(self.temperature_osr) << 5 + p = self._possible_pressure_osrs.index(self.pressure_osr) << 2 + self.bus.write_byte_data(self.address, 0x74, 1 + t + p) # if gas measurement mode is active wait until gas measurement is finished gas_ready = False From 6152c7d4e5ac114873816433d03abd7ee302e478 Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Fri, 13 Dec 2024 17:55:15 +0100 Subject: [PATCH 13/26] refactor --- src/sts1_sensors/sensors/BME688.py | 195 ++++++++++++++--------------- 1 file changed, 97 insertions(+), 98 deletions(-) diff --git a/src/sts1_sensors/sensors/BME688.py b/src/sts1_sensors/sensors/BME688.py index ddd26f4..356d149 100644 --- a/src/sts1_sensors/sensors/BME688.py +++ b/src/sts1_sensors/sensors/BME688.py @@ -233,105 +233,104 @@ def _finish_setup(self): self.bus.write_byte_data(self.address, 0x5A, int(tempset)) - def getVal(self): - if self.setupD: - # initiate force mode -> single measurement - self.bus.write_byte_data(self.address, 0x70, 0) + def get_values(self): + # initiate force mode -> single measurement + self.bus.write_byte_data(self.address, 0x70, 0) - t = self._possible_temperature_osrs.index(self.temperature_osr) << 5 - p = self._possible_pressure_osrs.index(self.pressure_osr) << 2 - self.bus.write_byte_data(self.address, 0x74, 1 + t + p) - - # if gas measurement mode is active wait until gas measurement is finished - gas_ready = False - if self.use_gas: - for _ in range(10): - if self.bus.read_byte_data(self.address, 0x1D) != 0b10000000: - time.sleep(0.05) - continue - gas_ready = True - - # get temperature adc value - temp_adc_MSB = self.bus.read_byte_data(self.address, 0x22) - temp_adc_LSB = self.bus.read_byte_data(self.address, 0x23) - temp_adc_XSB = self.bus.read_byte_data(self.address, 0x24) - temp_adc = ((temp_adc_XSB & 0b11110000) >> 4) + (temp_adc_LSB << 4) + (temp_adc_MSB << 12) - - # compensate temperature adc value - vart1 = ((temp_adc / 16384) - (self.par_t1 / 1024)) * self.par_t2 - vart2 = (((temp_adc / 131072) - (self.par_t1 / 8192)) * ((temp_adc / 131072) - (self.par_t1 / 8192))) * self.par_t3 * 16 - t_fine = vart1 + vart2 - temp_comp = t_fine / 5120 - - self.ambient_temp = temp_comp - - - #get humidity adc value - hum_adc_MSB = self.bus.read_byte_data(self.address, 0x25) - hum_adc_LSB = self.bus.read_byte_data(self.address, 0x26) - hum_adc = (hum_adc_LSB << 0) + (hum_adc_MSB << 8) - - #compensate humidity adc value - varh1 = hum_adc - ((self.par_h1 * 16) + ((self.par_h3 / 2) * temp_comp)) - varh2 = varh1 * ((self.par_h2 / 262144) * (1 + ((self.par_h4 / 16384) * temp_comp) + ((self.par_h5 / 1048576) * temp_comp * temp_comp))) - varh3 = self.par_h6 / 16384 - varh4 = self.par_h7 / 2097152 - hum_comp = varh2 + ((varh3 + (varh4 * temp_comp)) * varh2 * varh2) - - #get pressure adc value - press_adc_MSB = self.bus.read_byte_data(self.address, 0x1F) - press_adc_LSB = self.bus.read_byte_data(self.address, 0x20) - press_adc_XSB = self.bus.read_byte_data(self.address, 0x21) - press_adc = ((press_adc_XSB & 0b11110000) >> 4) + (press_adc_LSB << 4) + (press_adc_MSB << 12) - - #compensate pressure adc value - varp1 = (t_fine / 2) - 64000 - varp2 = varp1 * varp1 * (self.par_p6 / 131072) - varp2 = varp2 + (varp1 * (self.par_p5 * 2)) - varp2 = (varp2 / 4) + (self.par_p4 * 65536) - varp1 = (((self.par_p3 * varp1 * varp1) / 16384) + (self.par_p2 * varp1)) / 524288 - varp1 = (1 + (varp1 / 32768)) * self.par_p1 - press_comp = 1048576 - press_adc - press_comp = ((press_comp - (varp2 / 4096)) * 6250) / varp1 - varp1 = (self.par_p9 * press_comp * press_comp) / 2147483648 - varp2 = press_comp * (self.par_p8 / 32768) - varp3 = (press_comp / 256) * (press_comp / 256) * (press_comp / 256) * (self.par_p10 / 131072) - press_comp = press_comp + (varp1 + varp2 + varp3 + (self.par_p7 * 128)) / 16 - - #read gasresistence if enabled - gas_res = 0 - if self.use_gas and gas_ready: - gas_adc_MSB = self.bus.read_byte_data(self.address, 0x2C) - gas_adc_LSB = self.bus.read_byte_data(self.address, 0x2D) - gas_adc = ((gas_adc_LSB & 0b11000000) >> 6) + (gas_adc_MSB << 2) - - gas_range_XSB = self.bus.read_byte_data(self.address, 0x2D) - gas_range = gas_range_XSB & 0b00001111 - - varg1 = 262144 >> gas_range - varg2 = gas_adc - 512 - varg2 *= 3 - varg2 = 4096 + varg2 - gas_res = (10000 * varg1) / varg2 - gas_res *= 100 + t = self._possible_temperature_osrs.index(self.temperature_osr) << 5 + p = self._possible_pressure_osrs.index(self.pressure_osr) << 2 + self.bus.write_byte_data(self.address, 0x74, 1 + t + p) + + # if gas measurement mode is active wait until gas measurement is finished + gas_ready = False + if self.use_gas: + for _ in range(10): + if self.bus.read_byte_data(self.address, 0x1D) != 0b10000000: + time.sleep(0.05) + continue + gas_ready = True + + # get temperature adc value + temp_adc_MSB = self.bus.read_byte_data(self.address, 0x22) + temp_adc_LSB = self.bus.read_byte_data(self.address, 0x23) + temp_adc_XSB = self.bus.read_byte_data(self.address, 0x24) + temp_adc = ((temp_adc_XSB & 0b11110000) >> 4) + (temp_adc_LSB << 4) + (temp_adc_MSB << 12) + + # compensate temperature adc value + vart1 = ((temp_adc / 16384) - (self.par_t1 / 1024)) * self.par_t2 + vart2 = (((temp_adc / 131072) - (self.par_t1 / 8192)) * ((temp_adc / 131072) - (self.par_t1 / 8192))) * self.par_t3 * 16 + t_fine = vart1 + vart2 + temp_comp = t_fine / 5120 + + self.ambient_temp = temp_comp + + + #get humidity adc value + hum_adc_MSB = self.bus.read_byte_data(self.address, 0x25) + hum_adc_LSB = self.bus.read_byte_data(self.address, 0x26) + hum_adc = (hum_adc_LSB << 0) + (hum_adc_MSB << 8) + + #compensate humidity adc value + varh1 = hum_adc - ((self.par_h1 * 16) + ((self.par_h3 / 2) * temp_comp)) + varh2 = varh1 * ((self.par_h2 / 262144) * (1 + ((self.par_h4 / 16384) * temp_comp) + ((self.par_h5 / 1048576) * temp_comp * temp_comp))) + varh3 = self.par_h6 / 16384 + varh4 = self.par_h7 / 2097152 + hum_comp = varh2 + ((varh3 + (varh4 * temp_comp)) * varh2 * varh2) + + #get pressure adc value + press_adc_MSB = self.bus.read_byte_data(self.address, 0x1F) + press_adc_LSB = self.bus.read_byte_data(self.address, 0x20) + press_adc_XSB = self.bus.read_byte_data(self.address, 0x21) + press_adc = ((press_adc_XSB & 0b11110000) >> 4) + (press_adc_LSB << 4) + (press_adc_MSB << 12) + + #compensate pressure adc value + varp1 = (t_fine / 2) - 64000 + varp2 = varp1 * varp1 * (self.par_p6 / 131072) + varp2 = varp2 + (varp1 * (self.par_p5 * 2)) + varp2 = (varp2 / 4) + (self.par_p4 * 65536) + varp1 = (((self.par_p3 * varp1 * varp1) / 16384) + (self.par_p2 * varp1)) / 524288 + varp1 = (1 + (varp1 / 32768)) * self.par_p1 + press_comp = 1048576 - press_adc + press_comp = ((press_comp - (varp2 / 4096)) * 6250) / varp1 + varp1 = (self.par_p9 * press_comp * press_comp) / 2147483648 + varp2 = press_comp * (self.par_p8 / 32768) + varp3 = (press_comp / 256) * (press_comp / 256) * (press_comp / 256) * (self.par_p10 / 131072) + press_comp = press_comp + (varp1 + varp2 + varp3 + (self.par_p7 * 128)) / 16 + + #read gasresistence if enabled + gas_res = 0 + if self.use_gas and gas_ready: + gas_adc_MSB = self.bus.read_byte_data(self.address, 0x2C) + gas_adc_LSB = self.bus.read_byte_data(self.address, 0x2D) + gas_adc = ((gas_adc_LSB & 0b11000000) >> 6) + (gas_adc_MSB << 2) + gas_range_XSB = self.bus.read_byte_data(self.address, 0x2D) + gas_range = gas_range_XSB & 0b00001111 - #recalculate and set temperature - if self.use_gas: - varg1 = (self.par_g1 / 16) + 49 - varg2 = ((self.par_g2 / 32768) * 0.0005) + 0.00235 - varg3 = self.par_g3 / 1024 - varg4 = varg1 * (1 + (varg2 * self.gasTemp)) - varg5 = varg4 + (varg3 * self.ambient_temp) - tempset = (3.4 * ((varg5 * (4 / (4 + self.res_heat_range)) * (1 / (1 + (self.res_heat_val * 0.002)))) - 25)) - self.bus.write_byte_data(self.address, 0x5A, int(tempset)) + varg1 = 262144 >> gas_range + varg2 = gas_adc - 512 + varg2 *= 3 + varg2 = 4096 + varg2 + gas_res = (10000 * varg1) / varg2 + gas_res *= 100 + + + #recalculate and set temperature + if self.use_gas: + varg1 = (self.par_g1 / 16) + 49 + varg2 = ((self.par_g2 / 32768) * 0.0005) + 0.00235 + varg3 = self.par_g3 / 1024 + varg4 = varg1 * (1 + (varg2 * self.gasTemp)) + varg5 = varg4 + (varg3 * self.ambient_temp) + tempset = (3.4 * ((varg5 * (4 / (4 + self.res_heat_range)) * (1 / (1 + (self.res_heat_val * 0.002)))) - 25)) + self.bus.write_byte_data(self.address, 0x5A, int(tempset)) - #turn off heater - self.bus.write_byte_data(self.address, 0x70, 0b1000) - - return { - "humidity": hum_comp, - "temperature": temp_comp, - "pressure": press_comp, - "gas_resistance": gas_res, - } + #turn off heater + self.bus.write_byte_data(self.address, 0x70, 0b1000) + + return { + "humidity": hum_comp, + "temperature": temp_comp, + "pressure": press_comp, + "gas_resistance": gas_res, + } From 533eb9aa078594bb337b4020af4883a1a51c2440 Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Fri, 13 Dec 2024 18:23:37 +0100 Subject: [PATCH 14/26] added examples and tests for bme --- examples/BME688.py | 5 +++ src/sts1_sensors/sensors/BME688.py | 18 ++++----- src/sts1_sensors/sensors/__init__.py | 7 ++-- tests/test_BME688.py | 59 ++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 12 deletions(-) create mode 100644 examples/BME688.py create mode 100644 tests/test_BME688.py diff --git a/examples/BME688.py b/examples/BME688.py new file mode 100644 index 0000000..1d7adbe --- /dev/null +++ b/examples/BME688.py @@ -0,0 +1,5 @@ +from sts1_sensors import BME688 + +bme = BME688() +t = bme.get_values() +print(t) diff --git a/src/sts1_sensors/sensors/BME688.py b/src/sts1_sensors/sensors/BME688.py index 356d149..b846a69 100644 --- a/src/sts1_sensors/sensors/BME688.py +++ b/src/sts1_sensors/sensors/BME688.py @@ -1,9 +1,12 @@ +from collections import namedtuple import os import time from sts1_sensors.sensors.AbstractSensor import AbstractSensor from sts1_sensors.utils import twos_comp +BME688Data = namedtuple("BME688Data", "pressure_hPa humidity_percent temperature_C gas_resistance_ohms") + class BME688(AbstractSensor): """Pressure, humidity, temperature and gas sensor. """ @@ -11,9 +14,11 @@ class BME688(AbstractSensor): _possible_temperature_osrs = [1, 2, 4, 8, 16] _possible_humidity_osrs = [1, 2, 4, 8, 16] _possible_pressure_osrs = [1, 2, 4, 8, 16] + + # filter coefficients iir filter. applies to temperature and pressure data only. _possible_iirs = [0, 1, 3, 7, 15, 31, 63, 127] - def __init__(self, temperature_osr=1, humidity_osr=1, pressure_osr=1, iir=0, + def __init__(self, temperature_osr=1, humidity_osr=1, pressure_osr=1, iir=1, use_gas=True, gas_temperature=200, gas_time=1000, address=None, bus=None): super().__init__(possible_addresses=[0x76, 0x77], bus=bus) @@ -227,7 +232,7 @@ def _finish_setup(self): varg1 = (self.par_g1 / 16) + 49 varg2 = ((self.par_g2 / 32768) * 0.0005) + 0.00235 varg3 = self.par_g3 / 1024 - varg4 = varg1 * (1 + (varg2 * self.gasTemp)) + varg4 = varg1 * (1 + (varg2 * self.gas_temperature)) varg5 = varg4 + (varg3 * self.ambient_temp) tempset = (3.4 * ((varg5 * (4 / (4 + self.res_heat_range)) * (1 / (1 + (self.res_heat_val * 0.002)))) - 25)) @@ -320,7 +325,7 @@ def get_values(self): varg1 = (self.par_g1 / 16) + 49 varg2 = ((self.par_g2 / 32768) * 0.0005) + 0.00235 varg3 = self.par_g3 / 1024 - varg4 = varg1 * (1 + (varg2 * self.gasTemp)) + varg4 = varg1 * (1 + (varg2 * self.gas_temperature)) varg5 = varg4 + (varg3 * self.ambient_temp) tempset = (3.4 * ((varg5 * (4 / (4 + self.res_heat_range)) * (1 / (1 + (self.res_heat_val * 0.002)))) - 25)) self.bus.write_byte_data(self.address, 0x5A, int(tempset)) @@ -328,9 +333,4 @@ def get_values(self): #turn off heater self.bus.write_byte_data(self.address, 0x70, 0b1000) - return { - "humidity": hum_comp, - "temperature": temp_comp, - "pressure": press_comp, - "gas_resistance": gas_res, - } + return BME688Data(press_comp, hum_comp, temp_comp, gas_res) diff --git a/src/sts1_sensors/sensors/__init__.py b/src/sts1_sensors/sensors/__init__.py index e526113..b885cec 100644 --- a/src/sts1_sensors/sensors/__init__.py +++ b/src/sts1_sensors/sensors/__init__.py @@ -1,3 +1,4 @@ -from .ADXL345 import * -from .L3GD20H import * -from .TMP112 import * +from .ADXL345 import ADXL345 +from .BME688 import BME688 +from .L3GD20H import L3GD20H +from .TMP112 import TMP112 diff --git a/tests/test_BME688.py b/tests/test_BME688.py new file mode 100644 index 0000000..6a0825a --- /dev/null +++ b/tests/test_BME688.py @@ -0,0 +1,59 @@ +import os + +import pytest + +from sts1_sensors import BME688 + +def test_class_creation1(): + BME688() + +def test_class_creation2(): + BME688(temperature_osr=2, humidity_osr=4, pressure_osr=8) + +def test_class_creation3(): + BME688(use_gas=False) + +def test_class_creation4(): + from smbus2 import SMBus + with SMBus(1) as bus: + BME688(bus=bus) + +def test_class_creation5(): + BME688(address=0x76) + +def test_class_creation6(): + os.environ["STS1_SENSOR_ADDRESS_BME688"] = "0x76" + BME688() + del os.environ["STS1_SENSOR_ADDRESS_BME688"] + +def test_get_values1(): + accel = BME688(use_gas=True) + accel.get_values() + +def test_get_values2(): + accel = BME688(use_gas=False) + accel.get_values() + +def test_set_wrong_address1(): + with pytest.raises(ValueError): + BME688(address=0x99) + +def test_set_wrong_address2(): + with pytest.raises(ValueError): + os.environ["STS1_SENSOR_ADDRESS_BME688"] = "0x98" + BME688() + del os.environ["STS1_SENSOR_ADDRESS_BME688"] + +def test_set_wrong_gas_temperature(): + with pytest.raises(ValueError): + BME688(gas_temperature=500) + +def test_set_wrong_gas_time1(): + with pytest.raises(ValueError): + BME688(gas_time=0) + +def test_set_wrong_gas_time2(): + with pytest.raises(ValueError): + BME688(gas_time=4500) + + From e07fa7b2eae6cd1ebd69b0374faa6668e4b5e0ff Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Fri, 13 Dec 2024 21:20:30 +0100 Subject: [PATCH 15/26] moved files --- .python-version | 2 +- examples/BMM150.py | 45 +++++ examples/BMM150Example.py | 12 -- examples/testtest.py | 27 +++ pyproject.toml | 6 +- src/sts1_sensors/__init__.py | 2 +- src/sts1_sensors/{sensors => edu}/ADXL345.py | 2 +- src/sts1_sensors/{sensors => edu}/BME688.py | 11 +- src/sts1_sensors/{sensors => edu}/BMM150.py | 33 +++- src/sts1_sensors/{sensors => edu}/L3GD20H.py | 4 +- src/sts1_sensors/{sensors => edu}/TMP112.py | 2 +- src/sts1_sensors/{sensors => edu}/__init__.py | 1 + .../{sensors => edu}/pysatdosclient.py | 0 .../{sensors => nonedu}/GUVA_C32.py | 2 +- src/sts1_sensors/nonedu/__init__.py | 1 + .../{sensors => utils}/AbstractSensor.py | 0 src/sts1_sensors/{ => utils}/utils.py | 0 uv.lock | 186 +++++++++++------- 18 files changed, 228 insertions(+), 108 deletions(-) create mode 100644 examples/BMM150.py delete mode 100644 examples/BMM150Example.py create mode 100644 examples/testtest.py rename src/sts1_sensors/{sensors => edu}/ADXL345.py (98%) rename src/sts1_sensors/{sensors => edu}/BME688.py (98%) rename src/sts1_sensors/{sensors => edu}/BMM150.py (62%) rename src/sts1_sensors/{sensors => edu}/L3GD20H.py (95%) rename src/sts1_sensors/{sensors => edu}/TMP112.py (97%) rename src/sts1_sensors/{sensors => edu}/__init__.py (80%) rename src/sts1_sensors/{sensors => edu}/pysatdosclient.py (100%) rename src/sts1_sensors/{sensors => nonedu}/GUVA_C32.py (97%) create mode 100644 src/sts1_sensors/nonedu/__init__.py rename src/sts1_sensors/{sensors => utils}/AbstractSensor.py (100%) rename src/sts1_sensors/{ => utils}/utils.py (100%) diff --git a/.python-version b/.python-version index 24ee5b1..2c07333 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.13 +3.11 diff --git a/examples/BMM150.py b/examples/BMM150.py new file mode 100644 index 0000000..50f8921 --- /dev/null +++ b/examples/BMM150.py @@ -0,0 +1,45 @@ +import math + +import bmm150 + +# import bmm150.bmm150_defs + +# import mock +from smbus2 import SMBus + +# with mock.patch("bmm150.bmm150_defs.BMM150_I2C_Address", 0x10): + +# bmm150.BMM150_I2C_Address = 0x10 +bmm150.bmm150_defs.BMM150_I2C_Address = 0x10 +# __import__('bmm150', dict(BMM150_I2C_Address=0x10, **globals())) + +class PatchedSMBus: + + def __init__(self, bus_number): + self.bus = SMBus(bus_number) + + def read_byte_data(self, ignored, *args, **kwargs): + return self.bus.read_byte_data(0x10, *args, **kwargs) + + def write_byte_data(self, ignored, *args, **kwargs): + return self.bus.write_byte_data(0x10, *args, **kwargs) + + def read_i2c_block_data(self, ignored, *args, **kwargs): + return self.bus.read_i2c_block_data(0x10, *args, **kwargs) + + +bmm = bmm150.BMM150(auto_init=False) +bmm.i2c_bus = PatchedSMBus(1) +bmm.initialize() + +x, y, z = bmm.read_mag_data() + +heading_rads = math.atan2(x, y) + +heading_degrees = math.degrees(heading_rads) + +print(f"X : {x:.2f}µT") +print(f"Y : {y:.2f}µT") +print(f"Z : {z:.2f}µT") + +print(f"Heading: {heading_degrees:.2f}°") diff --git a/examples/BMM150Example.py b/examples/BMM150Example.py deleted file mode 100644 index f95d450..0000000 --- a/examples/BMM150Example.py +++ /dev/null @@ -1,12 +0,0 @@ -import sts1_sensors as STS1 -from smbus2 import SMBus -import time - -with SMBus(1) as bus: - BMM = STS1.BMM150(bus) - BMM.set_address(0x10) - BMM.set_datarate(10) - BMM.setup() - while True: - print("X: %.2fuT, Y: %.2fuT, Z: %.2fuT" % (BMM.getXuT(), BMM.getYuT(), BMM.getZuT())) - time.sleep(1) diff --git a/examples/testtest.py b/examples/testtest.py new file mode 100644 index 0000000..ed61e46 --- /dev/null +++ b/examples/testtest.py @@ -0,0 +1,27 @@ +import bme680 +import time + +sensor = bme680.BME680() + +sensor.set_humidity_oversample(bme680.OS_2X) +sensor.set_pressure_oversample(bme680.OS_4X) +sensor.set_temperature_oversample(bme680.OS_8X) +sensor.set_filter(bme680.FILTER_SIZE_3) + +sensor.set_gas_status(bme680.ENABLE_GAS_MEAS) +sensor.set_gas_heater_temperature(320) +sensor.set_gas_heater_duration(150) +sensor.select_gas_heater_profile(0) + +while True: + if sensor.get_sensor_data(): + output = "{0:.2f} C,{1:.2f} hPa,{2:.2f} %RH".format(sensor.data.temperature, sensor.data.pressure, sensor.data.humidity) + + if sensor.data.heat_stable: + print("{0},{1} Ohms".format(output, sensor.data.gas_resistance)) + + else: + print(output) + + time.sleep(1) + \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 2edc4a6..89c76dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,9 +3,11 @@ name = "sts1_sensors" version = "0.3.5" description = "A sensor library for the CubeSat STS1 (TU Wien Space Team)." readme = "README.md" -requires-python = ">=3.13" +requires-python = ">=3.11,<3.12" dependencies = [ - "smbus2>=0.5.0", + "bme680>=2.0.0", + "bmm150>=0.2.2", + "smbus2>=0.4.2,<0.5.0", "structlog>=24.4.0", ] authors = [ diff --git a/src/sts1_sensors/__init__.py b/src/sts1_sensors/__init__.py index 5e36334..d119a7d 100644 --- a/src/sts1_sensors/__init__.py +++ b/src/sts1_sensors/__init__.py @@ -1 +1 @@ -from .sensors import * +from .edu import * diff --git a/src/sts1_sensors/sensors/ADXL345.py b/src/sts1_sensors/edu/ADXL345.py similarity index 98% rename from src/sts1_sensors/sensors/ADXL345.py rename to src/sts1_sensors/edu/ADXL345.py index 61e3208..4606c2e 100644 --- a/src/sts1_sensors/sensors/ADXL345.py +++ b/src/sts1_sensors/edu/ADXL345.py @@ -1,6 +1,6 @@ import os -from sts1_sensors.sensors.AbstractSensor import AbstractSensor +from sts1_sensors.utils.AbstractSensor import AbstractSensor class ADXL345(AbstractSensor): """Digital accelerometer. diff --git a/src/sts1_sensors/sensors/BME688.py b/src/sts1_sensors/edu/BME688.py similarity index 98% rename from src/sts1_sensors/sensors/BME688.py rename to src/sts1_sensors/edu/BME688.py index b846a69..924c15b 100644 --- a/src/sts1_sensors/sensors/BME688.py +++ b/src/sts1_sensors/edu/BME688.py @@ -2,8 +2,8 @@ import os import time -from sts1_sensors.sensors.AbstractSensor import AbstractSensor -from sts1_sensors.utils import twos_comp +from sts1_sensors.utils.AbstractSensor import AbstractSensor +from sts1_sensors.utils.utils import twos_comp BME688Data = namedtuple("BME688Data", "pressure_hPa humidity_percent temperature_C gas_resistance_ohms") @@ -250,9 +250,10 @@ def get_values(self): gas_ready = False if self.use_gas: for _ in range(10): - if self.bus.read_byte_data(self.address, 0x1D) != 0b10000000: - time.sleep(0.05) - continue + if self.bus.read_byte_data(self.address, 0x1D) == 0b10000000: + break + time.sleep(0.05) + gas_ready = True # get temperature adc value diff --git a/src/sts1_sensors/sensors/BMM150.py b/src/sts1_sensors/edu/BMM150.py similarity index 62% rename from src/sts1_sensors/sensors/BMM150.py rename to src/sts1_sensors/edu/BMM150.py index a860b0d..e43ca50 100644 --- a/src/sts1_sensors/sensors/BMM150.py +++ b/src/sts1_sensors/edu/BMM150.py @@ -1,25 +1,32 @@ import os import time -from sts1_sensors.sensors.AbstractSensor import AbstractSensor +from sts1_sensors.utils.AbstractSensor import AbstractSensor class BMM150(AbstractSensor): """Geomagnetic sensor. + + Datasheet: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmm150-ds001.pdf """ - _possible_datarates = [1, 2, 6, 8, 15, 20, 25, 30] + _possible_datarates = [10, 2, 6, 8, 15, 20, 25, 30] - def __init__(self, datarate=8, address=None, bus=None): + def __init__(self, datarate=10, address=None, bus=None): super().__init__(possible_addresses=[0x10, 0x11, 0x12, 0x13], bus=bus) self.address = address or int(os.environ.get("STS1_SENSOR_ADDRESS_BMM150", "0x10"), 16) self.datarate = datarate self.xyz_addresses = {"x": 0x42, "y": 0x44, "z": 0x46} - self.bus.write_byte_data(self.addr, 0x4B, 1) - time.sleep(1) - self.bus.write_byte_data(self.addr, 0x4C, self.poss_datarate.index(self.datarate) << 3) - self.bus.write_byte_data(self.addr, 0x51, 0b1111) - self.bus.write_byte_data(self.addr, 0x52, 0b1111) + self.bus.write_byte_data(self.address, 0x4B, 1) + self.bus.write_byte_data(self.address, 0x4C, 0b111000 & self._possible_datarates.index(self.datarate) << 3) + self.bus.write_byte_data(self.address, 0x51, 0b1111) + self.bus.write_byte_data(self.address, 0x52, 0b1111) + + # Wait up to 10 secs for sensor to be ready + for _ in range(20): + if self.bus.read_byte_data(self.address, 0x48) & 1 == 1: + break + time.sleep(0.5) @property def datarate(self): @@ -37,11 +44,12 @@ def datarate(self, datarate): def _get_raw(self, var): lsb, msb = self.bus.read_i2c_block_data(self.address, self.xyz_addresses[var], 2) k = (msb << 8) | lsb - k = k >> 3 if var in ("x", "y"): + k = k >> 3 shift = 12 elif var == "z": + k = k >> 1 shift = 15 if (k >> shift) == 1: @@ -53,13 +61,18 @@ def get_raw(self): return self._get_raw("x"), self._get_raw("y"), self._get_raw("z") def _get_ut(self, var): + + # lsb = self.bus.read_byte_data(self.address, self.xyz_addresses[var]) + # msb = self.bus.read_byte_data(self.address, self.xyz_addresses[var] + 1) + lsb, msb = self.bus.read_i2c_block_data(self.address, self.xyz_addresses[var], 2) k = (msb << 8) | lsb - k = k >> 3 if var in ("x", "y"): + k = k >> 3 shift = 12 elif var == "z": + k = k >> 1 shift = 14 if (k >> shift) == 1: diff --git a/src/sts1_sensors/sensors/L3GD20H.py b/src/sts1_sensors/edu/L3GD20H.py similarity index 95% rename from src/sts1_sensors/sensors/L3GD20H.py rename to src/sts1_sensors/edu/L3GD20H.py index 4f9be47..2a11246 100644 --- a/src/sts1_sensors/sensors/L3GD20H.py +++ b/src/sts1_sensors/edu/L3GD20H.py @@ -1,7 +1,7 @@ import os -from sts1_sensors.sensors.AbstractSensor import AbstractSensor -from sts1_sensors.utils import twos_comp +from sts1_sensors.utils.AbstractSensor import AbstractSensor +from sts1_sensors.utils.utils import twos_comp class L3GD20H(AbstractSensor): """Three-axis gyroscope diff --git a/src/sts1_sensors/sensors/TMP112.py b/src/sts1_sensors/edu/TMP112.py similarity index 97% rename from src/sts1_sensors/sensors/TMP112.py rename to src/sts1_sensors/edu/TMP112.py index 57b6eec..f71205c 100644 --- a/src/sts1_sensors/sensors/TMP112.py +++ b/src/sts1_sensors/edu/TMP112.py @@ -2,7 +2,7 @@ from smbus2 import i2c_msg -from sts1_sensors.sensors.AbstractSensor import AbstractSensor +from sts1_sensors.utils.AbstractSensor import AbstractSensor class TMP112(AbstractSensor): """High-accuracy temperature sensor. diff --git a/src/sts1_sensors/sensors/__init__.py b/src/sts1_sensors/edu/__init__.py similarity index 80% rename from src/sts1_sensors/sensors/__init__.py rename to src/sts1_sensors/edu/__init__.py index b885cec..59929ed 100644 --- a/src/sts1_sensors/sensors/__init__.py +++ b/src/sts1_sensors/edu/__init__.py @@ -1,4 +1,5 @@ from .ADXL345 import ADXL345 from .BME688 import BME688 +from .BMM150 import BMM150 from .L3GD20H import L3GD20H from .TMP112 import TMP112 diff --git a/src/sts1_sensors/sensors/pysatdosclient.py b/src/sts1_sensors/edu/pysatdosclient.py similarity index 100% rename from src/sts1_sensors/sensors/pysatdosclient.py rename to src/sts1_sensors/edu/pysatdosclient.py diff --git a/src/sts1_sensors/sensors/GUVA_C32.py b/src/sts1_sensors/nonedu/GUVA_C32.py similarity index 97% rename from src/sts1_sensors/sensors/GUVA_C32.py rename to src/sts1_sensors/nonedu/GUVA_C32.py index b45096c..031d5a2 100644 --- a/src/sts1_sensors/sensors/GUVA_C32.py +++ b/src/sts1_sensors/nonedu/GUVA_C32.py @@ -1,7 +1,7 @@ import os from smbus2 import i2c_msg -from sts1_sensors.sensors.AbstractSensor import AbstractSensor +from sts1_sensors.utils.AbstractSensor import AbstractSensor class GUVA_C32(AbstractSensor): """Ultraviolet light sensor. diff --git a/src/sts1_sensors/nonedu/__init__.py b/src/sts1_sensors/nonedu/__init__.py new file mode 100644 index 0000000..fb49d29 --- /dev/null +++ b/src/sts1_sensors/nonedu/__init__.py @@ -0,0 +1 @@ +from .GUVA_C32 import GUVA_C32 diff --git a/src/sts1_sensors/sensors/AbstractSensor.py b/src/sts1_sensors/utils/AbstractSensor.py similarity index 100% rename from src/sts1_sensors/sensors/AbstractSensor.py rename to src/sts1_sensors/utils/AbstractSensor.py diff --git a/src/sts1_sensors/utils.py b/src/sts1_sensors/utils/utils.py similarity index 100% rename from src/sts1_sensors/utils.py rename to src/sts1_sensors/utils/utils.py diff --git a/uv.lock b/uv.lock index e9675e7..d71bc54 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -requires-python = ">=3.13" +requires-python = "==3.11.*" [[package]] name = "alabaster" @@ -17,6 +17,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, { name = "sniffio" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f6/40/318e58f669b1a9e00f5c4453910682e2d9dd594334539c7b7817dabb765f/anyio-4.7.0.tar.gz", hash = "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48", size = 177076 } wheels = [ @@ -53,6 +54,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed", size = 147925 }, ] +[[package]] +name = "bme680" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "smbus2" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4b/92/8ac453cac43f019387259a01e0f4e13c5917cc0925f077a32e308522a9ca/bme680-2.0.0.tar.gz", hash = "sha256:7395afd9bc95fc3d643cc873ad2f5c27d3c2d18ccf4771505124e0e2042c0d74", size = 80399 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/19/8c926497c8be9fc65bbd09d3ab44cb5f78219defc5dbb57d606dd5e39444/bme680-2.0.0-py3-none-any.whl", hash = "sha256:1802bd5ba98354ebb9318185051c913e1a3c2f06771e8f91eb40e5838795e66e", size = 14360 }, +] + +[[package]] +name = "bmm150" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "enum-tools" }, + { name = "smbus2" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/a1/c7938c1bdf75a50ba938fc52583b5be71e8819caca65151fcecd1f59881f/bmm150-0.2.2-py3-none-any.whl", hash = "sha256:9c2c531af60a9ab5a41c3ad892ae72100431c3554ff0ac395586ddad8935520b", size = 8468 }, +] + [[package]] name = "certifi" version = "2024.8.30" @@ -68,21 +93,21 @@ version = "3.4.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f2/4f/e1808dc01273379acc506d18f1504eb2d299bd4131743b9fc54d7be4df1e/charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", size = 106620 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/89/68a4c86f1a0002810a27f12e9a7b22feb198c59b2f05231349fbce5c06f4/charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", size = 194617 }, - { url = "https://files.pythonhosted.org/packages/4f/cd/8947fe425e2ab0aa57aceb7807af13a0e4162cd21eee42ef5b053447edf5/charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", size = 125310 }, - { url = "https://files.pythonhosted.org/packages/5b/f0/b5263e8668a4ee9becc2b451ed909e9c27058337fda5b8c49588183c267a/charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", size = 119126 }, - { url = "https://files.pythonhosted.org/packages/ff/6e/e445afe4f7fda27a533f3234b627b3e515a1b9429bc981c9a5e2aa5d97b6/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", size = 139342 }, - { url = "https://files.pythonhosted.org/packages/a1/b2/4af9993b532d93270538ad4926c8e37dc29f2111c36f9c629840c57cd9b3/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", size = 149383 }, - { url = "https://files.pythonhosted.org/packages/fb/6f/4e78c3b97686b871db9be6f31d64e9264e889f8c9d7ab33c771f847f79b7/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", size = 142214 }, - { url = "https://files.pythonhosted.org/packages/2b/c9/1c8fe3ce05d30c87eff498592c89015b19fade13df42850aafae09e94f35/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", size = 144104 }, - { url = "https://files.pythonhosted.org/packages/ee/68/efad5dcb306bf37db7db338338e7bb8ebd8cf38ee5bbd5ceaaaa46f257e6/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", size = 146255 }, - { url = "https://files.pythonhosted.org/packages/0c/75/1ed813c3ffd200b1f3e71121c95da3f79e6d2a96120163443b3ad1057505/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", size = 140251 }, - { url = "https://files.pythonhosted.org/packages/7d/0d/6f32255c1979653b448d3c709583557a4d24ff97ac4f3a5be156b2e6a210/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", size = 148474 }, - { url = "https://files.pythonhosted.org/packages/ac/a0/c1b5298de4670d997101fef95b97ac440e8c8d8b4efa5a4d1ef44af82f0d/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", size = 151849 }, - { url = "https://files.pythonhosted.org/packages/04/4f/b3961ba0c664989ba63e30595a3ed0875d6790ff26671e2aae2fdc28a399/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", size = 149781 }, - { url = "https://files.pythonhosted.org/packages/d8/90/6af4cd042066a4adad58ae25648a12c09c879efa4849c705719ba1b23d8c/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482", size = 144970 }, - { url = "https://files.pythonhosted.org/packages/cc/67/e5e7e0cbfefc4ca79025238b43cdf8a2037854195b37d6417f3d0895c4c2/charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", size = 94973 }, - { url = "https://files.pythonhosted.org/packages/65/97/fc9bbc54ee13d33dc54a7fcf17b26368b18505500fc01e228c27b5222d80/charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", size = 102308 }, + { url = "https://files.pythonhosted.org/packages/9c/61/73589dcc7a719582bf56aae309b6103d2762b526bffe189d635a7fcfd998/charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c", size = 193339 }, + { url = "https://files.pythonhosted.org/packages/77/d5/8c982d58144de49f59571f940e329ad6e8615e1e82ef84584c5eeb5e1d72/charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944", size = 124366 }, + { url = "https://files.pythonhosted.org/packages/bf/19/411a64f01ee971bed3231111b69eb56f9331a769072de479eae7de52296d/charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee", size = 118874 }, + { url = "https://files.pythonhosted.org/packages/4c/92/97509850f0d00e9f14a46bc751daabd0ad7765cff29cdfb66c68b6dad57f/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c", size = 138243 }, + { url = "https://files.pythonhosted.org/packages/e2/29/d227805bff72ed6d6cb1ce08eec707f7cfbd9868044893617eb331f16295/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6", size = 148676 }, + { url = "https://files.pythonhosted.org/packages/13/bc/87c2c9f2c144bedfa62f894c3007cd4530ba4b5351acb10dc786428a50f0/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea", size = 141289 }, + { url = "https://files.pythonhosted.org/packages/eb/5b/6f10bad0f6461fa272bfbbdf5d0023b5fb9bc6217c92bf068fa5a99820f5/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc", size = 142585 }, + { url = "https://files.pythonhosted.org/packages/3b/a0/a68980ab8a1f45a36d9745d35049c1af57d27255eff8c907e3add84cf68f/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5", size = 144408 }, + { url = "https://files.pythonhosted.org/packages/d7/a1/493919799446464ed0299c8eef3c3fad0daf1c3cd48bff9263c731b0d9e2/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594", size = 139076 }, + { url = "https://files.pythonhosted.org/packages/fb/9d/9c13753a5a6e0db4a0a6edb1cef7aee39859177b64e1a1e748a6e3ba62c2/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c", size = 146874 }, + { url = "https://files.pythonhosted.org/packages/75/d2/0ab54463d3410709c09266dfb416d032a08f97fd7d60e94b8c6ef54ae14b/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365", size = 150871 }, + { url = "https://files.pythonhosted.org/packages/8d/c9/27e41d481557be53d51e60750b85aa40eaf52b841946b3cdeff363105737/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129", size = 148546 }, + { url = "https://files.pythonhosted.org/packages/ee/44/4f62042ca8cdc0cabf87c0fc00ae27cd8b53ab68be3605ba6d071f742ad3/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236", size = 143048 }, + { url = "https://files.pythonhosted.org/packages/01/f8/38842422988b795220eb8038745d27a675ce066e2ada79516c118f291f07/charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99", size = 94389 }, + { url = "https://files.pythonhosted.org/packages/0b/6e/b13bd47fa9023b3699e94abf565b5a2f0b0be6e9ddac9812182596ee62e4/charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27", size = 101752 }, { url = "https://files.pythonhosted.org/packages/bf/9b/08c0432272d77b04803958a4598a51e2a4b51c06640af8b8f0f908c18bf2/charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", size = 49446 }, ] @@ -116,6 +141,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 }, ] +[[package]] +name = "enum-tools" +version = "0.9.0.post1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/93/a9243f5ce62906d86eacce0fd1adb015fc565c054e47b58ff781d89818cb/enum_tools-0.9.0.post1.tar.gz", hash = "sha256:e59eb1c16667400b185f8a61ac427029919be2ec48b9ca04aa1b388a42fb14d5", size = 18718 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/b3/b5608d203e4d4e896f65bd3973702ab77c90e332bfc783e968b5ac6f1a3f/enum_tools-0.9.0.post1-py3-none-any.whl", hash = "sha256:d8fd962054e7e400fa7f0a196f7607f19ef78aca4b288543ecb330f890edb60d", size = 61672 }, +] + [[package]] name = "furo" version = "2024.8.6" @@ -197,26 +235,16 @@ version = "3.0.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, - { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, - { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, - { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, - { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, - { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, - { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, - { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, - { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, - { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, - { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, - { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, - { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, - { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, - { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, - { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, - { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, - { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, - { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, - { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, + { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 }, + { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 }, + { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 }, + { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 }, + { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 }, + { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 }, + { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 }, + { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 }, + { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 }, + { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 }, ] [[package]] @@ -305,15 +333,15 @@ version = "6.0.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, - { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, - { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, - { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, - { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, - { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, - { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, - { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, - { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, ] [[package]] @@ -333,11 +361,11 @@ wheels = [ [[package]] name = "smbus2" -version = "0.5.0" +version = "0.4.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/10/c9/6d85aa809e107adf85303010a59b340be109c8f815cbedc5c08c73bcffef/smbus2-0.5.0.tar.gz", hash = "sha256:4a5946fd82277870c2878befdb1a29bb28d15cda14ea4d8d2d54cf3d4bdcb035", size = 16950 } +sdist = { url = "https://files.pythonhosted.org/packages/98/17/9663936e52a348b3ad1c85e6ca6071d2abf00a5f64f2df50bec8dcca6e16/smbus2-0.4.3.tar.gz", hash = "sha256:36f2288a8e1a363cb7a7b2244ec98d880eb5a728a2494ac9c71e9de7bf6a803a", size = 16801 } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/9f/2235ba9001e3c29fc342eeb222104420bcb7bac51555f0c034376a744075/smbus2-0.5.0-py2.py3-none-any.whl", hash = "sha256:1a15c3b9fa69357beb038cc0b5d37939702f8bfde1ddc89ca9f17d8461dbe949", size = 11527 }, + { url = "https://files.pythonhosted.org/packages/d6/5b/3ada173f07b4ec9bfa03b779e59ecada48eb7cb1a29f51cfce70edce7f3f/smbus2-0.4.3-py2.py3-none-any.whl", hash = "sha256:a2fc29cfda4081ead2ed61ef2c4fc041d71dd40a8d917e85216f44786fca2d1d", size = 11553 }, ] [[package]] @@ -518,6 +546,8 @@ name = "sts1-sensors" version = "0.3.5" source = { editable = "." } dependencies = [ + { name = "bme680" }, + { name = "bmm150" }, { name = "smbus2" }, { name = "structlog" }, ] @@ -534,7 +564,9 @@ dev = [ [package.metadata] requires-dist = [ - { name = "smbus2", specifier = ">=0.5.0" }, + { name = "bme680", specifier = ">=2.0.0" }, + { name = "bmm150", specifier = ">=0.2.2" }, + { name = "smbus2", specifier = ">=0.4.2,<0.5.0" }, { name = "structlog", specifier = ">=24.4.0" }, ] @@ -548,6 +580,15 @@ dev = [ { name = "sphinx-autobuild", specifier = ">=2024.10.3" }, ] +[[package]] +name = "typing-extensions" +version = "4.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, +] + [[package]] name = "urllib3" version = "2.2.3" @@ -579,18 +620,19 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/3c/7e/4569184ea04b501840771b8fcecee19b2233a8b72c196061263c0ef23c0b/watchfiles-1.0.3.tar.gz", hash = "sha256:f3ff7da165c99a5412fe5dd2304dd2dbaaaa5da718aad942dcb3a178eaa70c56", size = 38185 } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/77/0ceb864c854c59bc5326484f88a900c70b4a05e3792e0ce340689988dd5e/watchfiles-1.0.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e153a690b7255c5ced17895394b4f109d5dcc2a4f35cb809374da50f0e5c456a", size = 391061 }, - { url = "https://files.pythonhosted.org/packages/00/66/327046cfe276a6e4af1a9a58fc99321e25783e501dc68c4c82de2d1bd3a7/watchfiles-1.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ac1be85fe43b4bf9a251978ce5c3bb30e1ada9784290441f5423a28633a958a7", size = 381177 }, - { url = "https://files.pythonhosted.org/packages/66/8a/420e2833deaa88e8ca7d94a497ec60fde610c66206a1776f049dc5ad3a4e/watchfiles-1.0.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2ec98e31e1844eac860e70d9247db9d75440fc8f5f679c37d01914568d18721", size = 441293 }, - { url = "https://files.pythonhosted.org/packages/58/56/2627795ecdf3f0f361458cfa74c583d5041615b9ad81bc25f8c66a6c44a2/watchfiles-1.0.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0179252846be03fa97d4d5f8233d1c620ef004855f0717712ae1c558f1974a16", size = 446209 }, - { url = "https://files.pythonhosted.org/packages/8f/d0/11c8dcd8a9995f0c075d76f1d06068bbb7a17583a19c5be75361497a4074/watchfiles-1.0.3-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:995c374e86fa82126c03c5b4630c4e312327ecfe27761accb25b5e1d7ab50ec8", size = 471227 }, - { url = "https://files.pythonhosted.org/packages/cb/8f/baa06574eaf48173882c4cdc3636993d0854661be7d88193e015ef996c73/watchfiles-1.0.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29b9cb35b7f290db1c31fb2fdf8fc6d3730cfa4bca4b49761083307f441cac5a", size = 493205 }, - { url = "https://files.pythonhosted.org/packages/ee/e8/9af886b4d3daa281047b542ffd2eb8f76dae9dd6ca0e21c5df4593b98574/watchfiles-1.0.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f8dc09ae69af50bead60783180f656ad96bd33ffbf6e7a6fce900f6d53b08f1", size = 489090 }, - { url = "https://files.pythonhosted.org/packages/81/02/62085db54b151fc02e22d47b288d19e99031dc9af73151289a7ab6621f9a/watchfiles-1.0.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:489b80812f52a8d8c7b0d10f0d956db0efed25df2821c7a934f6143f76938bd6", size = 442610 }, - { url = "https://files.pythonhosted.org/packages/61/81/980439c5d3fd3c69ba7124a56e1016d0b824ced2192ffbfe7062d53f524b/watchfiles-1.0.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:228e2247de583475d4cebf6b9af5dc9918abb99d1ef5ee737155bb39fb33f3c0", size = 614781 }, - { url = "https://files.pythonhosted.org/packages/55/98/e11401d8e9cd5d2bd0e95e9bf750f397489681965ee0c72fb84732257912/watchfiles-1.0.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1550be1a5cb3be08a3fb84636eaafa9b7119b70c71b0bed48726fd1d5aa9b868", size = 612637 }, - { url = "https://files.pythonhosted.org/packages/50/be/8393b68f2add0f839be6863f151bd6a7b242efc6eb2ce0c9f7d135d529cc/watchfiles-1.0.3-cp313-cp313-win32.whl", hash = "sha256:16db2d7e12f94818cbf16d4c8938e4d8aaecee23826344addfaaa671a1527b07", size = 271170 }, - { url = "https://files.pythonhosted.org/packages/f0/da/725f97a8b1b4e7b3e4331cce3ef921b12568af3af403b9f0f61ede036898/watchfiles-1.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:160eff7d1267d7b025e983ca8460e8cc67b328284967cbe29c05f3c3163711a3", size = 285246 }, + { url = "https://files.pythonhosted.org/packages/24/a8/06e2d5f840b285718a09be7c71ea19b7177b005cec87b8923dd7e8541b20/watchfiles-1.0.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ffe709b1d0bc2e9921257569675674cafb3a5f8af689ab9f3f2b3f88775b960f", size = 394821 }, + { url = "https://files.pythonhosted.org/packages/57/9f/f98a57ada3d4b1fcd0e325aa6c307e2248ecb048f71c96fba34a602f02e7/watchfiles-1.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:418c5ce332f74939ff60691e5293e27c206c8164ce2b8ce0d9abf013003fb7fe", size = 384898 }, + { url = "https://files.pythonhosted.org/packages/a3/31/33ba914010cbfd01033ca3727aff6585b6b2ea2b051b6fbaecdf4e2160b9/watchfiles-1.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f492d2907263d6d0d52f897a68647195bc093dafed14508a8d6817973586b6b", size = 441710 }, + { url = "https://files.pythonhosted.org/packages/d9/dd/e56b2ef07c2c34e4152950f0ce98a1081215ef027cf39e5dab61a0f8bd95/watchfiles-1.0.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:48c9f3bc90c556a854f4cab6a79c16974099ccfa3e3e150673d82d47a4bc92c9", size = 447681 }, + { url = "https://files.pythonhosted.org/packages/60/8f/3837df33f3d0cbef8ae59559891d688490bf2960373ea077ff11cbf79115/watchfiles-1.0.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75d3bcfa90454dba8df12adc86b13b6d85fda97d90e708efc036c2760cc6ba44", size = 472312 }, + { url = "https://files.pythonhosted.org/packages/5a/b3/95d103e5bb609b20f175e8acdf8b32c4b091f96f781c066fd3bff2b17778/watchfiles-1.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5691340f259b8f76b45fb31b98e594d46c36d1dc8285efa7975f7f50230c9093", size = 494779 }, + { url = "https://files.pythonhosted.org/packages/4f/f0/9fdc60daf5abf7b0deb225c9b0a37fd72dc407fa33f071ae2f70e84e268c/watchfiles-1.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e263cc718545b7f897baeac1f00299ab6fabe3e18caaacacb0edf6d5f35513c", size = 492090 }, + { url = "https://files.pythonhosted.org/packages/96/e5/a9967e77f173280ab1abbfd7ead90f2b94060574968baf5e6d7cbe9dd490/watchfiles-1.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c6cf7709ed3e55704cc06f6e835bf43c03bc8e3cb8ff946bf69a2e0a78d9d77", size = 443713 }, + { url = "https://files.pythonhosted.org/packages/60/38/e5390d4633a558878113e45d32e39d30cf58eb94e0359f41737be209321b/watchfiles-1.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:703aa5e50e465be901e0e0f9d5739add15e696d8c26c53bc6fc00eb65d7b9469", size = 615306 }, + { url = "https://files.pythonhosted.org/packages/5c/27/8a1ee74544c93e3242ca073087b45c64367aeb6897b622e43c8172c2b421/watchfiles-1.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bfcae6aecd9e0cb425f5145afee871465b98b75862e038d42fe91fd753ddd780", size = 614333 }, + { url = "https://files.pythonhosted.org/packages/fc/f8/25698f5b734907662b50acf3e81996053abdfe26fcf38804d028412876a8/watchfiles-1.0.3-cp311-cp311-win32.whl", hash = "sha256:6a76494d2c5311584f22416c5a87c1e2cb954ff9b5f0988027bc4ef2a8a67181", size = 270987 }, + { url = "https://files.pythonhosted.org/packages/39/78/f600dee7b387e6088c8d1f4c898a4340d07aecfe6406bd90ec4c1925ef08/watchfiles-1.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:cf745cbfad6389c0e331786e5fe9ae3f06e9d9c2ce2432378e1267954793975c", size = 284098 }, + { url = "https://files.pythonhosted.org/packages/ca/6f/27ba8aec0a4b45a6063454465eefb42777158081d9df18eab5f1d6a3bd8a/watchfiles-1.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:2dcc3f60c445f8ce14156854a072ceb36b83807ed803d37fdea2a50e898635d6", size = 276804 }, ] [[package]] @@ -599,16 +641,16 @@ version = "14.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f4/1b/380b883ce05bb5f45a905b61790319a28958a9ab1e4b6b95ff5464b60ca1/websockets-14.1.tar.gz", hash = "sha256:398b10c77d471c0aab20a845e7a60076b6390bfdaac7a6d2edb0d2c59d75e8d8", size = 162840 } wheels = [ - { url = "https://files.pythonhosted.org/packages/34/77/812b3ba5110ed8726eddf9257ab55ce9e85d97d4aa016805fdbecc5e5d48/websockets-14.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3630b670d5057cd9e08b9c4dab6493670e8e762a24c2c94ef312783870736ab9", size = 161966 }, - { url = "https://files.pythonhosted.org/packages/8d/24/4fcb7aa6986ae7d9f6d083d9d53d580af1483c5ec24bdec0978307a0f6ac/websockets-14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36ebd71db3b89e1f7b1a5deaa341a654852c3518ea7a8ddfdf69cc66acc2db1b", size = 159625 }, - { url = "https://files.pythonhosted.org/packages/f8/47/2a0a3a2fc4965ff5b9ce9324d63220156bd8bedf7f90824ab92a822e65fd/websockets-14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5b918d288958dc3fa1c5a0b9aa3256cb2b2b84c54407f4813c45d52267600cd3", size = 159857 }, - { url = "https://files.pythonhosted.org/packages/dd/c8/d7b425011a15e35e17757e4df75b25e1d0df64c0c315a44550454eaf88fc/websockets-14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00fe5da3f037041da1ee0cf8e308374e236883f9842c7c465aa65098b1c9af59", size = 169635 }, - { url = "https://files.pythonhosted.org/packages/93/39/6e3b5cffa11036c40bd2f13aba2e8e691ab2e01595532c46437b56575678/websockets-14.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8149a0f5a72ca36720981418eeffeb5c2729ea55fa179091c81a0910a114a5d2", size = 168578 }, - { url = "https://files.pythonhosted.org/packages/cf/03/8faa5c9576299b2adf34dcccf278fc6bbbcda8a3efcc4d817369026be421/websockets-14.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77569d19a13015e840b81550922056acabc25e3f52782625bc6843cfa034e1da", size = 169018 }, - { url = "https://files.pythonhosted.org/packages/8c/05/ea1fec05cc3a60defcdf0bb9f760c3c6bd2dd2710eff7ac7f891864a22ba/websockets-14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cf5201a04550136ef870aa60ad3d29d2a59e452a7f96b94193bee6d73b8ad9a9", size = 169383 }, - { url = "https://files.pythonhosted.org/packages/21/1d/eac1d9ed787f80754e51228e78855f879ede1172c8b6185aca8cef494911/websockets-14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:88cf9163ef674b5be5736a584c999e98daf3aabac6e536e43286eb74c126b9c7", size = 168773 }, - { url = "https://files.pythonhosted.org/packages/0e/1b/e808685530185915299740d82b3a4af3f2b44e56ccf4389397c7a5d95d39/websockets-14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:836bef7ae338a072e9d1863502026f01b14027250a4545672673057997d5c05a", size = 168757 }, - { url = "https://files.pythonhosted.org/packages/b6/19/6ab716d02a3b068fbbeb6face8a7423156e12c446975312f1c7c0f4badab/websockets-14.1-cp313-cp313-win32.whl", hash = "sha256:0d4290d559d68288da9f444089fd82490c8d2744309113fc26e2da6e48b65da6", size = 162834 }, - { url = "https://files.pythonhosted.org/packages/6c/fd/ab6b7676ba712f2fc89d1347a4b5bdc6aa130de10404071f2b2606450209/websockets-14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8621a07991add373c3c5c2cf89e1d277e49dc82ed72c75e3afc74bd0acc446f0", size = 163277 }, + { url = "https://files.pythonhosted.org/packages/97/ed/c0d03cb607b7fe1f7ff45e2cd4bb5cd0f9e3299ced79c2c303a6fff44524/websockets-14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:449d77d636f8d9c17952628cc7e3b8faf6e92a17ec581ec0c0256300717e1512", size = 161949 }, + { url = "https://files.pythonhosted.org/packages/06/91/bf0a44e238660d37a2dda1b4896235d20c29a2d0450f3a46cd688f43b239/websockets-14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a35f704be14768cea9790d921c2c1cc4fc52700410b1c10948511039be824aac", size = 159606 }, + { url = "https://files.pythonhosted.org/packages/ff/b8/7185212adad274c2b42b6a24e1ee6b916b7809ed611cbebc33b227e5c215/websockets-14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b1f3628a0510bd58968c0f60447e7a692933589b791a6b572fcef374053ca280", size = 159854 }, + { url = "https://files.pythonhosted.org/packages/5a/8a/0849968d83474be89c183d8ae8dcb7f7ada1a3c24f4d2a0d7333c231a2c3/websockets-14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c3deac3748ec73ef24fc7be0b68220d14d47d6647d2f85b2771cb35ea847aa1", size = 169402 }, + { url = "https://files.pythonhosted.org/packages/bd/4f/ef886e37245ff6b4a736a09b8468dae05d5d5c99de1357f840d54c6f297d/websockets-14.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7048eb4415d46368ef29d32133134c513f507fff7d953c18c91104738a68c3b3", size = 168406 }, + { url = "https://files.pythonhosted.org/packages/11/43/e2dbd4401a63e409cebddedc1b63b9834de42f51b3c84db885469e9bdcef/websockets-14.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cf0ad281c979306a6a34242b371e90e891bce504509fb6bb5246bbbf31e7b6", size = 168776 }, + { url = "https://files.pythonhosted.org/packages/6d/d6/7063e3f5c1b612e9f70faae20ebaeb2e684ffa36cb959eb0862ee2809b32/websockets-14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cc1fc87428c1d18b643479caa7b15db7d544652e5bf610513d4a3478dbe823d0", size = 169083 }, + { url = "https://files.pythonhosted.org/packages/49/69/e6f3d953f2fa0f8a723cf18cd011d52733bd7f6e045122b24e0e7f49f9b0/websockets-14.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f95ba34d71e2fa0c5d225bde3b3bdb152e957150100e75c86bc7f3964c450d89", size = 168529 }, + { url = "https://files.pythonhosted.org/packages/70/ff/f31fa14561fc1d7b8663b0ed719996cf1f581abee32c8fb2f295a472f268/websockets-14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9481a6de29105d73cf4515f2bef8eb71e17ac184c19d0b9918a3701c6c9c4f23", size = 168475 }, + { url = "https://files.pythonhosted.org/packages/f1/15/b72be0e4bf32ff373aa5baef46a4c7521b8ea93ad8b49ca8c6e8e764c083/websockets-14.1-cp311-cp311-win32.whl", hash = "sha256:368a05465f49c5949e27afd6fbe0a77ce53082185bbb2ac096a3a8afaf4de52e", size = 162833 }, + { url = "https://files.pythonhosted.org/packages/bc/ef/2d81679acbe7057ffe2308d422f744497b52009ea8bab34b6d74a2657d1d/websockets-14.1-cp311-cp311-win_amd64.whl", hash = "sha256:6d24fc337fc055c9e83414c94e1ee0dee902a486d19d2a7f0929e49d7d604b09", size = 163263 }, { url = "https://files.pythonhosted.org/packages/b0/0b/c7e5d11020242984d9d37990310520ed663b942333b83a033c2f20191113/websockets-14.1-py3-none-any.whl", hash = "sha256:4d4fc827a20abe6d544a119896f6b78ee13fe81cbfef416f3f2ddf09a03f0e2e", size = 156277 }, ] From e58342a308a93acd9f5ae3f3d1bba4291a60ab3f Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Fri, 13 Dec 2024 21:43:25 +0100 Subject: [PATCH 16/26] BMM150 rewrite --- examples/BMM150.py | 49 ++------------ src/sts1_sensors/edu/BMM150.py | 88 ++++++-------------------- src/sts1_sensors/utils/PatchedSMBus.py | 15 +++++ 3 files changed, 42 insertions(+), 110 deletions(-) create mode 100644 src/sts1_sensors/utils/PatchedSMBus.py diff --git a/examples/BMM150.py b/examples/BMM150.py index 50f8921..d858bf0 100644 --- a/examples/BMM150.py +++ b/examples/BMM150.py @@ -1,45 +1,8 @@ -import math +from sts1_sensors import BMM150 -import bmm150 +mag = BMM150() +x, y, z = mag.read_mag_data() +degrees = mag.get_heading() -# import bmm150.bmm150_defs - -# import mock -from smbus2 import SMBus - -# with mock.patch("bmm150.bmm150_defs.BMM150_I2C_Address", 0x10): - -# bmm150.BMM150_I2C_Address = 0x10 -bmm150.bmm150_defs.BMM150_I2C_Address = 0x10 -# __import__('bmm150', dict(BMM150_I2C_Address=0x10, **globals())) - -class PatchedSMBus: - - def __init__(self, bus_number): - self.bus = SMBus(bus_number) - - def read_byte_data(self, ignored, *args, **kwargs): - return self.bus.read_byte_data(0x10, *args, **kwargs) - - def write_byte_data(self, ignored, *args, **kwargs): - return self.bus.write_byte_data(0x10, *args, **kwargs) - - def read_i2c_block_data(self, ignored, *args, **kwargs): - return self.bus.read_i2c_block_data(0x10, *args, **kwargs) - - -bmm = bmm150.BMM150(auto_init=False) -bmm.i2c_bus = PatchedSMBus(1) -bmm.initialize() - -x, y, z = bmm.read_mag_data() - -heading_rads = math.atan2(x, y) - -heading_degrees = math.degrees(heading_rads) - -print(f"X : {x:.2f}µT") -print(f"Y : {y:.2f}µT") -print(f"Z : {z:.2f}µT") - -print(f"Heading: {heading_degrees:.2f}°") +print(f"{x=}, {y=}, {z=}") +print(f"Heading: {degrees:.2f}°") diff --git a/src/sts1_sensors/edu/BMM150.py b/src/sts1_sensors/edu/BMM150.py index e43ca50..6c1bdcc 100644 --- a/src/sts1_sensors/edu/BMM150.py +++ b/src/sts1_sensors/edu/BMM150.py @@ -1,87 +1,41 @@ +import math import os import time +import bmm150 + from sts1_sensors.utils.AbstractSensor import AbstractSensor +from sts1_sensors.utils.PatchedSMBus import PatchedSMBus + class BMM150(AbstractSensor): """Geomagnetic sensor. Datasheet: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmm150-ds001.pdf """ - _possible_datarates = [10, 2, 6, 8, 15, 20, 25, 30] - - def __init__(self, datarate=10, address=None, bus=None): + def __init__(self, address=None): + a = address or int(os.environ.get("STS1_SENSOR_ADDRESS_BMM150", "0x10"), 16) + bus = PatchedSMBus(int(os.environ.get("STS1_SENSORS_I2C_BUS_ADDRESS", 1)), address=a) super().__init__(possible_addresses=[0x10, 0x11, 0x12, 0x13], bus=bus) - self.address = address or int(os.environ.get("STS1_SENSOR_ADDRESS_BMM150", "0x10"), 16) - self.datarate = datarate - self.xyz_addresses = {"x": 0x42, "y": 0x44, "z": 0x46} - - self.bus.write_byte_data(self.address, 0x4B, 1) - self.bus.write_byte_data(self.address, 0x4C, 0b111000 & self._possible_datarates.index(self.datarate) << 3) - self.bus.write_byte_data(self.address, 0x51, 0b1111) - self.bus.write_byte_data(self.address, 0x52, 0b1111) + self.address = a + self.bmm = bmm150.BMM150(auto_init=False) + self.bmm.i2c_bus = self.bus + self.bmm.initialize() + # Wait up to 10 secs for sensor to be ready for _ in range(20): - if self.bus.read_byte_data(self.address, 0x48) & 1 == 1: + if self.bus.read_byte_data(0x48) & 1 == 1: break time.sleep(0.5) - @property - def datarate(self): - return self._datarate - - @datarate.setter - def datarate(self, datarate): - if datarate not in self._possible_datarates: - s = f"The datarate {datarate} does not exist." - s += f" Choose one of {self._possible_datarates}." - raise ValueError(s) - self._datarate = datarate - - - def _get_raw(self, var): - lsb, msb = self.bus.read_i2c_block_data(self.address, self.xyz_addresses[var], 2) - k = (msb << 8) | lsb + def get_raw_magnetic_data(self): + return self.bmm.read_raw_mag_data() - if var in ("x", "y"): - k = k >> 3 - shift = 12 - elif var == "z": - k = k >> 1 - shift = 15 - - if (k >> shift) == 1: - k = (1 << shift) - (k & 0b111111111111111) - k = k * (-1) - return k + def get_magnetic_data(self): + return self.bmm.read_mag_data() - def get_raw(self): - return self._get_raw("x"), self._get_raw("y"), self._get_raw("z") - - def _get_ut(self, var): - - # lsb = self.bus.read_byte_data(self.address, self.xyz_addresses[var]) - # msb = self.bus.read_byte_data(self.address, self.xyz_addresses[var] + 1) - - lsb, msb = self.bus.read_i2c_block_data(self.address, self.xyz_addresses[var], 2) - k = (msb << 8) | lsb - - if var in ("x", "y"): - k = k >> 3 - shift = 12 - elif var == "z": - k = k >> 1 - shift = 14 - - if (k >> shift) == 1: - k = k - (1 << (shift+1)) - - if var in ("x", "y"): - return k / 3.15076 - elif var == "z": - return k / 6.5536 - - def get_ut(self): - return self._get_ut("x"), self._get_ut("y"), self._get_ut("z") + def get_heading(self): + x, y, _ = self.get_magnetic_data() + return math.degrees(math.atan2(x, y)) diff --git a/src/sts1_sensors/utils/PatchedSMBus.py b/src/sts1_sensors/utils/PatchedSMBus.py new file mode 100644 index 0000000..023a92b --- /dev/null +++ b/src/sts1_sensors/utils/PatchedSMBus.py @@ -0,0 +1,15 @@ +from smbus2 import SMBus + +class PatchedSMBus: + def __init__(self, bus_number, address): + self.bus = SMBus(bus_number) + self.address = address + + def read_byte_data(self, ignored, *args, **kwargs): + return self.bus.read_byte_data(self.address, *args, **kwargs) + + def write_byte_data(self, ignored, *args, **kwargs): + return self.bus.write_byte_data(self.address, *args, **kwargs) + + def read_i2c_block_data(self, ignored, *args, **kwargs): + return self.bus.read_i2c_block_data(self.address, *args, **kwargs) From 86e1539d697cd481ada0bd1b0c64945f88a77f82 Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Fri, 13 Dec 2024 21:51:08 +0100 Subject: [PATCH 17/26] bmm fixes --- examples/BMM150.py | 7 ++++--- src/sts1_sensors/edu/BMM150.py | 2 +- src/sts1_sensors/utils/PatchedSMBus.py | 3 +++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/BMM150.py b/examples/BMM150.py index d858bf0..e48d2b6 100644 --- a/examples/BMM150.py +++ b/examples/BMM150.py @@ -1,8 +1,9 @@ from sts1_sensors import BMM150 mag = BMM150() -x, y, z = mag.read_mag_data() -degrees = mag.get_heading() +x, y, z = mag.get_magnetic_data() +print(f"{x=:.2f} µT, {y=:.2f} µT, {z=:.2f} µT") -print(f"{x=}, {y=}, {z=}") +degrees = mag.get_heading() print(f"Heading: {degrees:.2f}°") + diff --git a/src/sts1_sensors/edu/BMM150.py b/src/sts1_sensors/edu/BMM150.py index 6c1bdcc..4c8d4c4 100644 --- a/src/sts1_sensors/edu/BMM150.py +++ b/src/sts1_sensors/edu/BMM150.py @@ -26,7 +26,7 @@ def __init__(self, address=None): # Wait up to 10 secs for sensor to be ready for _ in range(20): - if self.bus.read_byte_data(0x48) & 1 == 1: + if self.bus.read_byte_data(0, 0x48) & 1 == 1: break time.sleep(0.5) diff --git a/src/sts1_sensors/utils/PatchedSMBus.py b/src/sts1_sensors/utils/PatchedSMBus.py index 023a92b..2a125c6 100644 --- a/src/sts1_sensors/utils/PatchedSMBus.py +++ b/src/sts1_sensors/utils/PatchedSMBus.py @@ -13,3 +13,6 @@ def write_byte_data(self, ignored, *args, **kwargs): def read_i2c_block_data(self, ignored, *args, **kwargs): return self.bus.read_i2c_block_data(self.address, *args, **kwargs) + + def __del__(self): + self.bus.close() From 31355f859eac36878815f5b999768ae045ecb471 Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Fri, 13 Dec 2024 22:14:45 +0100 Subject: [PATCH 18/26] added tests, doc --- README.md | 25 ++++++++++--- src/sts1_sensors/utils/__init__.py | 0 tests/test_BME688.py | 59 ------------------------------ tests/test_BMM150.py | 38 +++++++++++++++++++ 4 files changed, 57 insertions(+), 65 deletions(-) create mode 100644 src/sts1_sensors/utils/__init__.py delete mode 100644 tests/test_BME688.py create mode 100644 tests/test_BMM150.py diff --git a/README.md b/README.md index 2638518..671f3d8 100644 --- a/README.md +++ b/README.md @@ -2,25 +2,38 @@ # sts1-sensors -Streamline the process of handling sensors on the Raspi-Hat / EDU module. +Streamline the process of handling sensors for the STS1 project. -The following sensors are available on the EDU module: +The following sensors are available both on the satellite and **on the EDU module**: * [`ADXL345`](https://www.analog.com/en/products/adxl345.html) - Digital accelerometer. * [`BME688`](https://www.bosch-sensortec.com/products/environmental-sensors/gas-sensors/bme688/) - Pressure, humidity, temperature and gas sensor. * [`BMM150`](https://www.bosch-sensortec.com/products/motion-sensors/magnetometers/bmm150/) - Geomagnetic sensor. -* [`GUVA_C32`](https://www.digikey.de/de/products/detail/genicom-co-ltd/GUVA-C32SM/9960949) - Ultraviolet light sensor. * [`L3GD20H`](https://www.pololu.com/file/0J731/L3GD20H.pdf) - Three-axis gyroscope. * [`TMP112`](https://www.ti.com/product/TMP112) - High-accuracy temperature sensor. +The following sensors are available **on the satellite only**: +* [`GUVA_C32`](https://www.digikey.de/de/products/detail/genicom-co-ltd/GUVA-C32SM/9960949) - Ultraviolet light sensor. + ## Quickstart ```python -from sts1_sensors import ADXL345, TMP112 +from sts1_sensors import ADXL345, BMM150, L3GD20H, TMP112 # Accelerometer -accel = ADXL345(range=2, datarate=50) +accel = ADXL345() x, y, z = accel.get_g() -print(f"X: {x:.2f}g, Y: {y:.2f}g, Z: {z:.2f}g") +print(f"{x=:.2f} g, {y=:.2f} g, {z=:.2f} g") + +# Geomagnetic sensor +mag = BMM150() +x, y, z = mag.get_magnetic_data() +print(f"{x=:.2f} µT, {y=:.2f} µT, {z=:.2f} µT") +print(f"Heading: {mag.get_heading():.2f}°") + +# Gyroscope +gyro = L3GD20H() +x, y, z = gyro.get_position() +print(f"{x=:.2f} dpfs, {y=:.2f} dpfs, {z=:.2f} dpfs") # Temperature sensor temp = TMP112() diff --git a/src/sts1_sensors/utils/__init__.py b/src/sts1_sensors/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_BME688.py b/tests/test_BME688.py deleted file mode 100644 index 6a0825a..0000000 --- a/tests/test_BME688.py +++ /dev/null @@ -1,59 +0,0 @@ -import os - -import pytest - -from sts1_sensors import BME688 - -def test_class_creation1(): - BME688() - -def test_class_creation2(): - BME688(temperature_osr=2, humidity_osr=4, pressure_osr=8) - -def test_class_creation3(): - BME688(use_gas=False) - -def test_class_creation4(): - from smbus2 import SMBus - with SMBus(1) as bus: - BME688(bus=bus) - -def test_class_creation5(): - BME688(address=0x76) - -def test_class_creation6(): - os.environ["STS1_SENSOR_ADDRESS_BME688"] = "0x76" - BME688() - del os.environ["STS1_SENSOR_ADDRESS_BME688"] - -def test_get_values1(): - accel = BME688(use_gas=True) - accel.get_values() - -def test_get_values2(): - accel = BME688(use_gas=False) - accel.get_values() - -def test_set_wrong_address1(): - with pytest.raises(ValueError): - BME688(address=0x99) - -def test_set_wrong_address2(): - with pytest.raises(ValueError): - os.environ["STS1_SENSOR_ADDRESS_BME688"] = "0x98" - BME688() - del os.environ["STS1_SENSOR_ADDRESS_BME688"] - -def test_set_wrong_gas_temperature(): - with pytest.raises(ValueError): - BME688(gas_temperature=500) - -def test_set_wrong_gas_time1(): - with pytest.raises(ValueError): - BME688(gas_time=0) - -def test_set_wrong_gas_time2(): - with pytest.raises(ValueError): - BME688(gas_time=4500) - - diff --git a/tests/test_BMM150.py b/tests/test_BMM150.py new file mode 100644 index 0000000..53e92c6 --- /dev/null +++ b/tests/test_BMM150.py @@ -0,0 +1,38 @@ +import os + +import pytest + +from sts1_sensors import BMM150 + +def test_class_creation1(): + BMM150() + +def test_class_creation5(): + BMM150(address=0x10) + +def test_class_creation6(): + os.environ["STS1_SENSOR_ADDRESS_BMM150"] = "0x10" + BMM150() + del os.environ["STS1_SENSOR_ADDRESS_BMM150"] + +def test_get_values1(): + mag = BMM150() + mag.get_raw_magnetic_data() + +def test_get_values2(): + mag = BMM150() + mag.get_magnetic_data() + +def test_get_values3(): + mag = BMM150() + mag.get_heading() + +def test_set_wrong_address1(): + with pytest.raises(ValueError): + BMM150(address=0x99) + +def test_set_wrong_address2(): + with pytest.raises(ValueError): + os.environ["STS1_SENSOR_ADDRESS_BMM150"] = "0x98" + BMM150() + del os.environ["STS1_SENSOR_ADDRESS_BMM150"] From a5e789c3a30cbc89e61a4a7cca0b30477b02414d Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Fri, 13 Dec 2024 22:38:08 +0100 Subject: [PATCH 19/26] examples updated --- examples/BME688.py | 5 - examples/allSensors.py | 13 -- examples/{ => edu}/ADXL345.py | 0 .../ADXL345_calibration.py} | 6 +- examples/{testtest.py => edu/BME688.py} | 0 examples/{ => edu}/BMM150.py | 0 examples/{ => edu}/L3GD20H.py | 0 examples/{ => edu}/TMP112.py | 0 examples/edu/all_sensors.py | 28 ++++ examples/{ => nonedu}/SATDOS3Example.py | 0 examples/raspiHat.py | 13 -- src/sts1_sensors/main.py | 150 ------------------ .../{edu => nonedu}/pysatdosclient.py | 0 13 files changed, 31 insertions(+), 184 deletions(-) delete mode 100644 examples/BME688.py delete mode 100644 examples/allSensors.py rename examples/{ => edu}/ADXL345.py (100%) rename examples/{calibration_example.py => edu/ADXL345_calibration.py} (82%) rename examples/{testtest.py => edu/BME688.py} (100%) rename examples/{ => edu}/BMM150.py (100%) rename examples/{ => edu}/L3GD20H.py (100%) rename examples/{ => edu}/TMP112.py (100%) create mode 100644 examples/edu/all_sensors.py rename examples/{ => nonedu}/SATDOS3Example.py (100%) delete mode 100644 examples/raspiHat.py delete mode 100644 src/sts1_sensors/main.py rename src/sts1_sensors/{edu => nonedu}/pysatdosclient.py (100%) diff --git a/examples/BME688.py b/examples/BME688.py deleted file mode 100644 index 1d7adbe..0000000 --- a/examples/BME688.py +++ /dev/null @@ -1,5 +0,0 @@ -from sts1_sensors import BME688 - -bme = BME688() -t = bme.get_values() -print(t) diff --git a/examples/allSensors.py b/examples/allSensors.py deleted file mode 100644 index 7c4fdc5..0000000 --- a/examples/allSensors.py +++ /dev/null @@ -1,13 +0,0 @@ -import sts1_sensors as STS1 -from smbus2 import SMBus -import time - -with SMBus(1) as bus: - sensors = STS1.main.Sensors(bus) - #sensors.disable_GUVA_C32() - sensors.setup() - while True: - string = "TempBME: {0:.2f} °C | Hum: {1:.2f} % | Press: {2:.2f} hPa | GasRes:{3:.2f} Ohms | TempTMP: {4:.2f} | GX: {5:.2f} | GY: {6:.2f} | GZ: {7:.2f} | UVA: {8:.2f}" - val = sensors.getData() - print(string.format(val.tempBME, val.hum, val.press / 100.0, val.gasRes, val.tempTMP, val.gX, val.gY, val.gZ, val.uva)) - time.sleep(1) diff --git a/examples/ADXL345.py b/examples/edu/ADXL345.py similarity index 100% rename from examples/ADXL345.py rename to examples/edu/ADXL345.py diff --git a/examples/calibration_example.py b/examples/edu/ADXL345_calibration.py similarity index 82% rename from examples/calibration_example.py rename to examples/edu/ADXL345_calibration.py index c82555f..fd1123f 100644 --- a/examples/calibration_example.py +++ b/examples/edu/ADXL345_calibration.py @@ -2,11 +2,11 @@ import time from sts1_sensors import ADXL345 -accel = ADXL345(range=2, datarate=50) +accel = ADXL345(datarate=1.56) +# takes 20 secs measurements = [] -# takes 40 secs -for _ in range(200): +for _ in range(100): measurements.append(accel.get_g()) time.sleep(.2) diff --git a/examples/testtest.py b/examples/edu/BME688.py similarity index 100% rename from examples/testtest.py rename to examples/edu/BME688.py diff --git a/examples/BMM150.py b/examples/edu/BMM150.py similarity index 100% rename from examples/BMM150.py rename to examples/edu/BMM150.py diff --git a/examples/L3GD20H.py b/examples/edu/L3GD20H.py similarity index 100% rename from examples/L3GD20H.py rename to examples/edu/L3GD20H.py diff --git a/examples/TMP112.py b/examples/edu/TMP112.py similarity index 100% rename from examples/TMP112.py rename to examples/edu/TMP112.py diff --git a/examples/edu/all_sensors.py b/examples/edu/all_sensors.py new file mode 100644 index 0000000..54606c9 --- /dev/null +++ b/examples/edu/all_sensors.py @@ -0,0 +1,28 @@ +import time + +import structlog +from sts1_sensors import ADXL345, BMM150, L3GD20H, TMP112 + +log = structlog.get_logger() + +accel = ADXL345() # Accelerometer +mag = BMM150() # Geomagnetic sensor +gyro = L3GD20H() # Gyroscope +temp = TMP112() # Temperature sensor + +for _ in range(10): + gx, gy, gz = accel.get_g() + s = f"{gx=:.2f}, {gy=:.2f}, {gz=:.2f}" + + mx, my, mz = mag.get_magnetic_data() + s += f", {mx=:.2f} µT, {my=:.2f} µT, {mz=:.2f} µT" + s += f", Heading: {mag.get_heading():.2f}°" + + px, py, pz = gyro.get_position() + s += f", {px=:.2f} dpfs, {py=:.2f} dpfs, {pz=:.2f} dpfs" + + t = temp.get_temp() + s += f", {t:.2f} °C" + + log.info(s) + time.sleep(2) diff --git a/examples/SATDOS3Example.py b/examples/nonedu/SATDOS3Example.py similarity index 100% rename from examples/SATDOS3Example.py rename to examples/nonedu/SATDOS3Example.py diff --git a/examples/raspiHat.py b/examples/raspiHat.py deleted file mode 100644 index 3dc4eb6..0000000 --- a/examples/raspiHat.py +++ /dev/null @@ -1,13 +0,0 @@ -import sts1_sensors as STS1 -from smbus2 import SMBus -import time - -with SMBus(1) as bus: - sensors = STS1.main.Sensors(bus) - sensors.disable_GUVA_C32() - sensors.setup() - while True: - string = "TempBME: {0:.2f} °C | Hum: {1:.2f} % | Press: {2:.2f} hPa | GasRes:{3:.2f} Ohms | TempTMP: {4:.2f} | GX: {5:.2f} | GY: {6:.2f} | GZ: {7:.2f} | UVA: {8:.2f}" - val = sensors.getData() - print(string.format(val.tempBME, val.hum, val.press / 100.0, val.gasRes, val.tempTMP, val.gX, val.gY, val.gZ, val.uva)) - time.sleep(1) diff --git a/src/sts1_sensors/main.py b/src/sts1_sensors/main.py deleted file mode 100644 index e17eddf..0000000 --- a/src/sts1_sensors/main.py +++ /dev/null @@ -1,150 +0,0 @@ -from smbus2 import i2c_msg -from .ADXL345 import ADXL345 -from .TMP112 import TMP112 -from .BMM150 import BMM150 -from .L3GD20H import L3GD20H -from .GUVA_C32 import GUVA_C32 -from .BME688 import BME688 -import time - -def twos_comp(val, bits): - if val & (1 << (bits - 1)) != 0: - val = val - (1 << bits) - return val - -def current_millis_time(): - return round(time.time()*1000) - -# ------------------------sensor wrapper output data class----------------------------- -class SensorsData(): - tempTMP = 0 - tempBME = 0 - hum = 0 - press = 0 - gasRes = 0 - accX = 0 - accY = 0 - accZ = 0 - gX = 0 - gY = 0 - gZ = 0 - uva = 0 - uvaI = 0 - magX = 0 - magY = 0 - magZ = 0 - angVelX = 0 - angVelY = 0 - angVelZ = 0 - - -# ------------------------sensor wrapper class----------------------------- -class Sensors(): - BME688stat = True - TMP112stat = True - ADXL345stat = True - BMM150stat = True - GUVA_C32stat = True - L3GD20Hstat = True - - - lastBME = 0 - - bus = 0 - def __init__(self, bus): - self.bus = bus - self.output = SensorsData() - def disable_BME688(self): - self.BME688stat = False - def disable_TMP112(self): - self.TMP112stat = False - def disable_ADXL345(self): - self.ADXL345stat = False - def disable_BMM150(self): - self.BMM150stat = False - def disable_GUVA_C32(self): - self.GUVA_C32stat = False - def disable_L3GD20H(self): - self.L3GD20Hstat = False - def setup(self): - if self.BME688stat: - self.BME = BME688(self.bus) - self.BME.set_address(0x76) - self.BME.set_tempOSR(2) - self.BME.set_humOSR(4) - self.BME.set_pressOSR(8) - self.BME.set_IIR(3) - self.BME.set_gasTemp(320) - self.BME.set_gasTime(150) - self.BME.setup() - if self.TMP112stat: - self.TMP = TMP112(self.bus) - self.TMP.set_address(0x48) - self.TMP.set_conversionrate(4) - self.TMP.set_mode(1) - self.TMP.setup() - if self.ADXL345stat: - self.ADXL = ADXL345(self.bus) - self.ADXL.set_address(0x53) - self.ADXL.set_datarate(100) - self.ADXL.set_range(2) - self.ADXL.setup() - if self.BMM150stat: - self.BMM = BMM150(self.bus) - self.BMM.set_address(0x10) - self.BMM.set_datarate(30) - self.BMM.setup() - if self.GUVA_C32stat: - self.GUVA = GUVA_C32(self.bus) - self.GUVA.set_address(0x39) - self.GUVA.set_resolution(100) - self.GUVA.set_range(128) - self.GUVA.setup() - if self.L3GD20Hstat: - self.L3GD20H = L3GD20H(self.bus) - self.L3GD20H.set_address(0x6A) - self.L3GD20H.set_datarate(800) - self.L3GD20H.set_range(2000) - self.L3GD20H.setup() - - def getData(self): - #TMP112 - if self.TMP112stat: - self.output.tempTMP = self.TMP.getTemp() - - #BME688 - if self.BME688stat: - if(current_millis_time() - self.lastBME > 1000): - val = self.BME.getVal() - self.output.tempBME = val.temperature - self.output.hum = val.humidity - self.output.press = val.pressure - self.output.gasRes = val.gasResistance - self.lastBME = current_millis_time() - - #BMM150 - if self.BMM150stat: - self.output.magX = self.BMM.getXuT() - self.output.magY = self.BMM.getYuT() - self.output.magZ = self.BMM.getZuT() - - #ADXL345 - if self.ADXL345stat: - self.output.accX = self.ADXL.getXRaw() - self.output.accY = self.ADXL.getYRaw() - self.output.accZ = self.ADXL.getZRaw() - - self.output.gX = self.ADXL.getXGs() - self.output.gY = self.ADXL.getYGs() - self.output.gZ = self.ADXL.getZGs() - - #GUVA_C32 - if self.GUVA_C32stat: - self.output.uva = self.GUVA.getRawUVA() - self.output.uvaI = self.GUVA.getUVA() - - return self.output - - - - diff --git a/src/sts1_sensors/edu/pysatdosclient.py b/src/sts1_sensors/nonedu/pysatdosclient.py similarity index 100% rename from src/sts1_sensors/edu/pysatdosclient.py rename to src/sts1_sensors/nonedu/pysatdosclient.py From 4c5a9ea242684f3f6d3332f20eb41d1a52464d0e Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Fri, 13 Dec 2024 23:29:48 +0100 Subject: [PATCH 20/26] bmm --- src/sts1_sensors/edu/BMM150.py | 29 ++++++++++++++++++++------ src/sts1_sensors/utils/PatchedSMBus.py | 3 ++- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/sts1_sensors/edu/BMM150.py b/src/sts1_sensors/edu/BMM150.py index 4c8d4c4..ba6159c 100644 --- a/src/sts1_sensors/edu/BMM150.py +++ b/src/sts1_sensors/edu/BMM150.py @@ -4,19 +4,24 @@ import bmm150 -from sts1_sensors.utils.AbstractSensor import AbstractSensor from sts1_sensors.utils.PatchedSMBus import PatchedSMBus - -class BMM150(AbstractSensor): +class BMM150: """Geomagnetic sensor. Datasheet: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmm150-ds001.pdf """ - def __init__(self, address=None): + + def __init__(self, address=None, bus=None): + self.possible_addresses = [0x10, 0x11, 0x12, 0x13] a = address or int(os.environ.get("STS1_SENSOR_ADDRESS_BMM150", "0x10"), 16) - bus = PatchedSMBus(int(os.environ.get("STS1_SENSORS_I2C_BUS_ADDRESS", 1)), address=a) - super().__init__(possible_addresses=[0x10, 0x11, 0x12, 0x13], bus=bus) + + if bus is None: + self.manage_bus = True + self.bus = PatchedSMBus(int(os.environ.get("STS1_SENSORS_I2C_BUS_ADDRESS", 1)), address=a) + else: + self.manage_bus = False + self.bus = bus self.address = a @@ -30,6 +35,18 @@ def __init__(self, address=None): break time.sleep(0.5) + @property + def address(self): + return self._address + + @address.setter + def address(self, address): + if address not in self.possible_addresses: + s = f"The address {hex(address)} does not exist." + s += f" Choose one of {self.possible_addresses}." + raise ValueError(s) + self._address = address + def get_raw_magnetic_data(self): return self.bmm.read_raw_mag_data() diff --git a/src/sts1_sensors/utils/PatchedSMBus.py b/src/sts1_sensors/utils/PatchedSMBus.py index 2a125c6..302bd3d 100644 --- a/src/sts1_sensors/utils/PatchedSMBus.py +++ b/src/sts1_sensors/utils/PatchedSMBus.py @@ -1,7 +1,8 @@ from smbus2 import SMBus class PatchedSMBus: - def __init__(self, bus_number, address): + def __init__(self, bus_number, address, bus=None): + self.bus = SMBus(bus_number) self.address = address From cb93f427e73f80f4e90e80eeeec7e865b8682026 Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Fri, 13 Dec 2024 23:34:33 +0100 Subject: [PATCH 21/26] interface unified --- src/sts1_sensors/edu/BMM150.py | 9 +-------- src/sts1_sensors/utils/PatchedSMBus.py | 20 ++++++++++++++------ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/sts1_sensors/edu/BMM150.py b/src/sts1_sensors/edu/BMM150.py index ba6159c..36b4ab5 100644 --- a/src/sts1_sensors/edu/BMM150.py +++ b/src/sts1_sensors/edu/BMM150.py @@ -11,19 +11,12 @@ class BMM150: Datasheet: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmm150-ds001.pdf """ - def __init__(self, address=None, bus=None): self.possible_addresses = [0x10, 0x11, 0x12, 0x13] a = address or int(os.environ.get("STS1_SENSOR_ADDRESS_BMM150", "0x10"), 16) - if bus is None: - self.manage_bus = True - self.bus = PatchedSMBus(int(os.environ.get("STS1_SENSORS_I2C_BUS_ADDRESS", 1)), address=a) - else: - self.manage_bus = False - self.bus = bus - self.address = a + self.bus = PatchedSMBus(address=a, bus=bus) self.bmm = bmm150.BMM150(auto_init=False) self.bmm.i2c_bus = self.bus diff --git a/src/sts1_sensors/utils/PatchedSMBus.py b/src/sts1_sensors/utils/PatchedSMBus.py index 302bd3d..598bef1 100644 --- a/src/sts1_sensors/utils/PatchedSMBus.py +++ b/src/sts1_sensors/utils/PatchedSMBus.py @@ -1,10 +1,21 @@ +import os + from smbus2 import SMBus class PatchedSMBus: - def __init__(self, bus_number, address, bus=None): - - self.bus = SMBus(bus_number) + def __init__(self, address, bus=None): self.address = address + + if bus is None: + self.manage_bus = True + self.bus = SMBus(int(os.environ.get("STS1_SENSORS_I2C_BUS_ADDRESS", 1))) + else: + self.manage_bus = False + self.bus = bus + + def __del__(self): + if self.manage_bus: + self.bus.close() def read_byte_data(self, ignored, *args, **kwargs): return self.bus.read_byte_data(self.address, *args, **kwargs) @@ -14,6 +25,3 @@ def write_byte_data(self, ignored, *args, **kwargs): def read_i2c_block_data(self, ignored, *args, **kwargs): return self.bus.read_i2c_block_data(self.address, *args, **kwargs) - - def __del__(self): - self.bus.close() From fd148a2c2380ed316b0d67d5f6149491025b666a Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Sat, 14 Dec 2024 00:19:51 +0100 Subject: [PATCH 22/26] bme688 --- README.md | 1 + src/sts1_sensors/edu/BME688.py | 328 +++++---------------------------- 2 files changed, 43 insertions(+), 286 deletions(-) diff --git a/README.md b/README.md index 671f3d8..db0daf1 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ print(f"{x=:.2f} dpfs, {y=:.2f} dpfs, {z=:.2f} dpfs") temp = TMP112() print(f"{temp.get_temp():.2f} °C") ``` +More examples, see examples folder. ## Installation diff --git a/src/sts1_sensors/edu/BME688.py b/src/sts1_sensors/edu/BME688.py index 924c15b..c0f67a2 100644 --- a/src/sts1_sensors/edu/BME688.py +++ b/src/sts1_sensors/edu/BME688.py @@ -1,63 +1,30 @@ -from collections import namedtuple import os -import time -from sts1_sensors.utils.AbstractSensor import AbstractSensor -from sts1_sensors.utils.utils import twos_comp +import bme680 -BME688Data = namedtuple("BME688Data", "pressure_hPa humidity_percent temperature_C gas_resistance_ohms") +from sts1_sensors.utils.AbstractSensor import AbstractSensor class BME688(AbstractSensor): """Pressure, humidity, temperature and gas sensor. """ # over-sampling rates - _possible_temperature_osrs = [1, 2, 4, 8, 16] - _possible_humidity_osrs = [1, 2, 4, 8, 16] - _possible_pressure_osrs = [1, 2, 4, 8, 16] - - # filter coefficients iir filter. applies to temperature and pressure data only. - _possible_iirs = [0, 1, 3, 7, 15, 31, 63, 127] - - def __init__(self, temperature_osr=1, humidity_osr=1, pressure_osr=1, iir=1, - use_gas=True, gas_temperature=200, gas_time=1000, - address=None, bus=None): + _possible_temperature_osrs = [None, 1, 2, 4, 8, 16] + _possible_humidity_osrs = [None, 1, 2, 4, 8, 16] + _possible_pressure_osrs = [None, 1, 2, 4, 8, 16] + _possible_iir_filter_sizes = [0, 1, 3, 7, 15, 31, 63, 127] + + def __init__(self, temperature_osr=8, humidity_osr=2, pressure_osr=4, iir_filter_size=3, + temperature_offset=0, address=None, bus=None): super().__init__(possible_addresses=[0x76, 0x77], bus=bus) self.address = address or int(os.environ.get("STS1_SENSOR_ADDRESS_BME688", "0x76"), 16) + self.bme680 = bme680.BME680(self.address, self.bus) + self.temperature_osr = temperature_osr self.humidity_osr = humidity_osr self.pressure_osr = pressure_osr - self.iir = iir - self.use_gas = use_gas - self.gas_temperature = gas_temperature - self.gas_time = gas_time - - self.ambient_temp = 25 - self.par_t1 = 0 - self.par_t2 = 0 - self.par_t3 = 0 - self.par_h1 = 0 - self.par_h2 = 0 - self.par_h3 = 0 - self.par_h4 = 0 - self.par_h5 = 0 - self.par_h6 = 0 - self.par_h7 = 0 - self.par_p1 = 0 - self.par_p2 = 0 - self.par_p3 = 0 - self.par_p4 = 0 - self.par_p5 = 0 - self.par_p6 = 0 - self.par_p7 = 0 - self.par_p8 = 0 - self.par_p9 = 0 - self.par_p10 = 0 - self.par_g1 = 0 - self.par_g2 = 0 - self.par_g3 = 0 - - self._finish_setup() + self.temperature_offset = temperature_offset + self.iir_filter_size = iir_filter_size @property def temperature_osr(self): @@ -66,10 +33,11 @@ def temperature_osr(self): @temperature_osr.setter def temperature_osr(self, temperature_osr): if temperature_osr not in self._possible_temperature_osrs: - s = f"The temperature_osr {temperature_osr} does not exist." + s = f"Unexpected temperature_osr {temperature_osr}." s += f" Choose one of {self._possible_temperature_osrs}." raise ValueError(s) self._temperature_osr = temperature_osr + self.bme680.set_temperature_oversample(self._possible_temperature_osrs.index(temperature_osr)) @property def humidity_osr(self): @@ -78,10 +46,11 @@ def humidity_osr(self): @humidity_osr.setter def humidity_osr(self, humidity_osr): if humidity_osr not in self._possible_humidity_osrs: - s = f"The humidity_osr {humidity_osr} does not exist." + s = f"Unexpected humidity_osr {humidity_osr}." s += f" Choose one of {self._possible_humidity_osrs}." raise ValueError(s) self._humidity_osr = humidity_osr + self.bme680.set_humidity_oversample(self._possible_humidity_osrs.index(humidity_osr)) @property def pressure_osr(self): @@ -90,248 +59,35 @@ def pressure_osr(self): @pressure_osr.setter def pressure_osr(self, pressure_osr): if pressure_osr not in self._possible_pressure_osrs: - s = f"The pressure_osr {pressure_osr} does not exist." + s = f"Unexpected pressure_osr {pressure_osr}." s += f" Choose one of {self._possible_pressure_osrs}." raise ValueError(s) - self._pressure_osr = pressure_osr + self._pressure_osr = pressure_osr + self.bme680.set_pressure_oversample(self._possible_pressure_osrs.index(pressure_osr)) @property - def iir(self): - return self._iir - - @iir.setter - def iir(self, iir): - if iir not in self._possible_iirs: - s = f"The iir {iir} does not exist." - s += f" Choose one of {self._possible_iirs}." + def iir_filter_size(self): + return self._iir_filter_size + + @iir_filter_size.setter + def iir_filter_size(self, iir_filter_size): + if iir_filter_size not in self._possible_iir_filter_sizes: + s = f"Unexpected iir_filter_size {iir_filter_size}." + s += f" Choose one of {self._possible_iir_filter_sizes}." raise ValueError(s) - self._iir = iir - - @property - def gas_temperature(self): - return self._gas_temperature - - @gas_temperature.setter - def gas_temperature(self, gas_temperature): - if gas_temperature < 200 or gas_temperature > 400: - raise ValueError(f"gas_temperature has to be between 200° and 400° Celcius (both inclusive) but was {gas_temperature}.") - self._gas_temperature = gas_temperature + self._iir_filter_size = iir_filter_size + self.bme680.set_filter(self._possible_iir_filter_sizes.index(iir_filter_size)) @property - def gas_time(self): - return self._gas_time - - @gas_time.setter - def gas_time(self, gas_time): - if gas_time <= 0 or gas_time >= 4096: - raise ValueError(f"gas_time has to be between 0 and 4095 ms (both inclusive) but was {gas_time}.") - self._gas_time = gas_time - - def _finish_setup(self): - h = self._possible_humidity_osrs.index(self.humidity_osr) - self.bus.write_byte_data(self.address, 0x72, h) - - t = self._possible_temperature_osrs.index(self.temperature_osr) << 5 - p = self._possible_pressure_osrs.index(self.pressure_osr) << 2 - self.bus.write_byte_data(self.address, 0x74, t + p) - - iir = self._possible_iirs.index(self.iir) << 2 - self.bus.write_byte_data(self.address, 0x75, iir) - - if self.use_gas: - # enable gas conversion and activate heater step 0 - self.bus.write_byte_data(self.address, 0x71, 0b00100000) - if self.gas_time <= 64: - t = self.gas_time - elif self.gas_time / 4 <= 64: - t = 0b01000000 + self.gas_time // 4 - elif self.gas_time / 16 <= 64: - t = 0b10000000 + self.gas_time // 16 - elif self.gas_time / 64 <= 64: - t = 0b11000000 + self.gas_time // 64 - - self.bus.write_byte_data(self.address, 0x64, t) - - else: - # only Temperature, Humidity and Pressure - self.bus.write_byte_data(self.address, 0x71, 0) - self.bus.write_byte_data(self.address, 0x64, 0) - self.bus.write_byte_data(self.address, 0x5A, 0) - self.bus.write_byte_data(self.address, 0x70, 0b1000) - - #get comp values - par_t1_LSB = self.bus.read_byte_data(self.address, 0xE9) - par_t1_MSB = self.bus.read_byte_data(self.address, 0xEA) - self.par_t1 = twos_comp((par_t1_MSB<<8) + par_t1_LSB, 16) - - par_t2_LSB = self.bus.read_byte_data(self.address, 0x8A) - par_t2_MSB = self.bus.read_byte_data(self.address, 0x8B) - self.par_t2 = twos_comp((par_t2_MSB<<8) + par_t2_LSB, 16) - - self.par_t3 = twos_comp(self.bus.read_byte_data(self.address, 0x8C),8) - - par_h1_LSB = self.bus.read_byte_data(self.address, 0xE2) - par_h1_MSB = self.bus.read_byte_data(self.address, 0xE3) - self.par_h1 = twos_comp((par_h1_MSB<<4) + (par_h1_LSB & 0b00001111), 12) - - par_h2_LSB = self.bus.read_byte_data(self.address, 0xE2) - par_h2_MSB = self.bus.read_byte_data(self.address, 0xE1) - self.par_h2 = twos_comp((par_h2_MSB<<4) + ((par_h2_LSB & 0b11110000) >> 4), 12) - - self.par_h3 = twos_comp(self.bus.read_byte_data(self.address, 0xE4),8) - self.par_h4 = twos_comp(self.bus.read_byte_data(self.address, 0xE5),8) - self.par_h5 = twos_comp(self.bus.read_byte_data(self.address, 0xE6),8) - self.par_h6 = twos_comp(self.bus.read_byte_data(self.address, 0xE7),8) - self.par_h7 = twos_comp(self.bus.read_byte_data(self.address, 0xE8),8) - - par_p1_LSB = self.bus.read_byte_data(self.address, 0x8E) - par_p1_MSB = self.bus.read_byte_data(self.address, 0x8F) - self.par_p1 = (par_p1_MSB<<8) + par_p1_LSB - - par_p2_LSB = self.bus.read_byte_data(self.address, 0x90) - par_p2_MSB = self.bus.read_byte_data(self.address, 0x91) - self.par_p2 = twos_comp((par_p2_MSB<<8) + par_p2_LSB,16) - - self.par_p3 = twos_comp(self.bus.read_byte_data(self.address, 0x92),8) - - par_p4_LSB = self.bus.read_byte_data(self.address, 0x94) - par_p4_MSB = self.bus.read_byte_data(self.address, 0x95) - self.par_p4 = twos_comp((par_p4_MSB<<8) + par_p4_LSB,16) - - par_p5_LSB = self.bus.read_byte_data(self.address, 0x96) - par_p5_MSB = self.bus.read_byte_data(self.address, 0x97) - self.par_p5 = twos_comp((par_p5_MSB<<8) + par_p5_LSB,16) - - self.par_p6 = twos_comp(self.bus.read_byte_data(self.address, 0x99),8) - - self.par_p7 = twos_comp(self.bus.read_byte_data(self.address, 0x98),8) - - par_p8_LSB = self.bus.read_byte_data(self.address, 0x9C) - par_p8_MSB = self.bus.read_byte_data(self.address, 0x9D) - self.par_p8 = twos_comp((par_p8_MSB<<8) + par_p8_LSB,16) - - par_p9_LSB = self.bus.read_byte_data(self.address, 0x9E) - par_p9_MSB = self.bus.read_byte_data(self.address, 0x9F) - self.par_p9 = twos_comp((par_p9_MSB<<8) + par_p9_LSB,16) - - self.par_p10 = twos_comp(self.bus.read_byte_data(self.address, 0xA0),8) - - self.par_g1 = twos_comp(self.bus.read_byte_data(self.address, 0xED),8) - - par_g2_LSB = self.bus.read_byte_data(self.address, 0xEB) - par_g2_MSB = self.bus.read_byte_data(self.address, 0xEC) - self.par_g2 = twos_comp((par_g2_MSB<<8) + par_g2_LSB,16) - - self.par_g3 = twos_comp(self.bus.read_byte_data(self.address, 0xEE),8) - - self.res_heat_range = (self.bus.read_byte_data(self.address, 0x02) & 0b00110000) >> 4 - - self.res_heat_val = self.bus.read_byte_data(self.address, 0x00) - - if self.use_gas: - varg1 = (self.par_g1 / 16) + 49 - varg2 = ((self.par_g2 / 32768) * 0.0005) + 0.00235 - varg3 = self.par_g3 / 1024 - varg4 = varg1 * (1 + (varg2 * self.gas_temperature)) - varg5 = varg4 + (varg3 * self.ambient_temp) - tempset = (3.4 * ((varg5 * (4 / (4 + self.res_heat_range)) * (1 / (1 + (self.res_heat_val * 0.002)))) - 25)) - - self.bus.write_byte_data(self.address, 0x5A, int(tempset)) - - def get_values(self): - # initiate force mode -> single measurement - self.bus.write_byte_data(self.address, 0x70, 0) - - t = self._possible_temperature_osrs.index(self.temperature_osr) << 5 - p = self._possible_pressure_osrs.index(self.pressure_osr) << 2 - self.bus.write_byte_data(self.address, 0x74, 1 + t + p) - - # if gas measurement mode is active wait until gas measurement is finished - gas_ready = False - if self.use_gas: - for _ in range(10): - if self.bus.read_byte_data(self.address, 0x1D) == 0b10000000: - break - time.sleep(0.05) - - gas_ready = True - - # get temperature adc value - temp_adc_MSB = self.bus.read_byte_data(self.address, 0x22) - temp_adc_LSB = self.bus.read_byte_data(self.address, 0x23) - temp_adc_XSB = self.bus.read_byte_data(self.address, 0x24) - temp_adc = ((temp_adc_XSB & 0b11110000) >> 4) + (temp_adc_LSB << 4) + (temp_adc_MSB << 12) - - # compensate temperature adc value - vart1 = ((temp_adc / 16384) - (self.par_t1 / 1024)) * self.par_t2 - vart2 = (((temp_adc / 131072) - (self.par_t1 / 8192)) * ((temp_adc / 131072) - (self.par_t1 / 8192))) * self.par_t3 * 16 - t_fine = vart1 + vart2 - temp_comp = t_fine / 5120 - - self.ambient_temp = temp_comp - - - #get humidity adc value - hum_adc_MSB = self.bus.read_byte_data(self.address, 0x25) - hum_adc_LSB = self.bus.read_byte_data(self.address, 0x26) - hum_adc = (hum_adc_LSB << 0) + (hum_adc_MSB << 8) - - #compensate humidity adc value - varh1 = hum_adc - ((self.par_h1 * 16) + ((self.par_h3 / 2) * temp_comp)) - varh2 = varh1 * ((self.par_h2 / 262144) * (1 + ((self.par_h4 / 16384) * temp_comp) + ((self.par_h5 / 1048576) * temp_comp * temp_comp))) - varh3 = self.par_h6 / 16384 - varh4 = self.par_h7 / 2097152 - hum_comp = varh2 + ((varh3 + (varh4 * temp_comp)) * varh2 * varh2) - - #get pressure adc value - press_adc_MSB = self.bus.read_byte_data(self.address, 0x1F) - press_adc_LSB = self.bus.read_byte_data(self.address, 0x20) - press_adc_XSB = self.bus.read_byte_data(self.address, 0x21) - press_adc = ((press_adc_XSB & 0b11110000) >> 4) + (press_adc_LSB << 4) + (press_adc_MSB << 12) - - #compensate pressure adc value - varp1 = (t_fine / 2) - 64000 - varp2 = varp1 * varp1 * (self.par_p6 / 131072) - varp2 = varp2 + (varp1 * (self.par_p5 * 2)) - varp2 = (varp2 / 4) + (self.par_p4 * 65536) - varp1 = (((self.par_p3 * varp1 * varp1) / 16384) + (self.par_p2 * varp1)) / 524288 - varp1 = (1 + (varp1 / 32768)) * self.par_p1 - press_comp = 1048576 - press_adc - press_comp = ((press_comp - (varp2 / 4096)) * 6250) / varp1 - varp1 = (self.par_p9 * press_comp * press_comp) / 2147483648 - varp2 = press_comp * (self.par_p8 / 32768) - varp3 = (press_comp / 256) * (press_comp / 256) * (press_comp / 256) * (self.par_p10 / 131072) - press_comp = press_comp + (varp1 + varp2 + varp3 + (self.par_p7 * 128)) / 16 - - #read gasresistence if enabled - gas_res = 0 - if self.use_gas and gas_ready: - gas_adc_MSB = self.bus.read_byte_data(self.address, 0x2C) - gas_adc_LSB = self.bus.read_byte_data(self.address, 0x2D) - gas_adc = ((gas_adc_LSB & 0b11000000) >> 6) + (gas_adc_MSB << 2) - - gas_range_XSB = self.bus.read_byte_data(self.address, 0x2D) - gas_range = gas_range_XSB & 0b00001111 - - varg1 = 262144 >> gas_range - varg2 = gas_adc - 512 - varg2 *= 3 - varg2 = 4096 + varg2 - gas_res = (10000 * varg1) / varg2 - gas_res *= 100 - - - #recalculate and set temperature - if self.use_gas: - varg1 = (self.par_g1 / 16) + 49 - varg2 = ((self.par_g2 / 32768) * 0.0005) + 0.00235 - varg3 = self.par_g3 / 1024 - varg4 = varg1 * (1 + (varg2 * self.gas_temperature)) - varg5 = varg4 + (varg3 * self.ambient_temp) - tempset = (3.4 * ((varg5 * (4 / (4 + self.res_heat_range)) * (1 / (1 + (self.res_heat_val * 0.002)))) - 25)) - self.bus.write_byte_data(self.address, 0x5A, int(tempset)) - - #turn off heater - self.bus.write_byte_data(self.address, 0x70, 0b1000) - - return BME688Data(press_comp, hum_comp, temp_comp, gas_res) + def temperature_offset(self): + return self._temperature_offset + + @temperature_offset.setter + def temperature_offset(self, temperature_offset): + self._temperature_offset = temperature_offset + self.bme680.set_temp_offset(temperature_offset) + + def get_sensor_data(self): + self.bme680.get_sensor_data() + return self.bme680.data + \ No newline at end of file From 6942310dbde56895580e84450bb359f5f05530ac Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Sat, 14 Dec 2024 01:18:12 +0100 Subject: [PATCH 23/26] bme688 --- README.md | 11 +++++- examples/edu/ADXL345.py | 2 ++ examples/edu/BME688.py | 32 ++++++++--------- examples/edu/all_sensors.py | 14 ++++++-- src/sts1_sensors/edu/BME688.py | 66 ++++++++++++++++++++++++++++++++-- 5 files changed, 100 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index db0daf1..a53d737 100644 --- a/README.md +++ b/README.md @@ -17,13 +17,22 @@ The following sensors are available **on the satellite only**: ## Quickstart ```python -from sts1_sensors import ADXL345, BMM150, L3GD20H, TMP112 +from sts1_sensors import ADXL345, BME688, BMM150, L3GD20H, TMP112 # Accelerometer accel = ADXL345() x, y, z = accel.get_g() print(f"{x=:.2f} g, {y=:.2f} g, {z=:.2f} g") +# Temperature, pressure, humidity and gas sensor +multi = BME688(enable_gas_measurements=True) +t = multi.get_temperature() +p = multi.get_pressure() +h = multi.get_humidity() +heat = multi.get_heat_stable() +res = multi.get_gas_resistance() +print(f"{t:.2f} °C, {p:.2f} hPa, {h:.2f} %RH, {heat=}, {res:.2f} Ohms") + # Geomagnetic sensor mag = BMM150() x, y, z = mag.get_magnetic_data() diff --git a/examples/edu/ADXL345.py b/examples/edu/ADXL345.py index f142e84..d5b8305 100644 --- a/examples/edu/ADXL345.py +++ b/examples/edu/ADXL345.py @@ -1,5 +1,7 @@ import time + import structlog + from sts1_sensors import ADXL345 log = structlog.get_logger() diff --git a/examples/edu/BME688.py b/examples/edu/BME688.py index ed61e46..b6142ec 100644 --- a/examples/edu/BME688.py +++ b/examples/edu/BME688.py @@ -1,27 +1,23 @@ -import bme680 import time -sensor = bme680.BME680() +import structlog -sensor.set_humidity_oversample(bme680.OS_2X) -sensor.set_pressure_oversample(bme680.OS_4X) -sensor.set_temperature_oversample(bme680.OS_8X) -sensor.set_filter(bme680.FILTER_SIZE_3) +from sts1_sensors import BME688 -sensor.set_gas_status(bme680.ENABLE_GAS_MEAS) -sensor.set_gas_heater_temperature(320) -sensor.set_gas_heater_duration(150) -sensor.select_gas_heater_profile(0) +sensor = BME688() +log = structlog.get_logger() -while True: - if sensor.get_sensor_data(): - output = "{0:.2f} C,{1:.2f} hPa,{2:.2f} %RH".format(sensor.data.temperature, sensor.data.pressure, sensor.data.humidity) +sensor.enable_gas_measurements = True +sensor.gas_heater_temperature = 320 +sensor.gas_heater_duration = 150 - if sensor.data.heat_stable: - print("{0},{1} Ohms".format(output, sensor.data.gas_resistance)) +for i in range(10): + t = sensor.get_temperature() + p = sensor.get_pressure() + h = sensor.get_humidity() - else: - print(output) + heat = sensor.get_heat_stable() + res = sensor.get_gas_resistance() + log.info(f"{t:.2f} °C, {p:.2f} hPa, {h:.2f} %RH, {heat=}, {res:.2f} Ohms") time.sleep(1) - \ No newline at end of file diff --git a/examples/edu/all_sensors.py b/examples/edu/all_sensors.py index 54606c9..a5416c3 100644 --- a/examples/edu/all_sensors.py +++ b/examples/edu/all_sensors.py @@ -1,7 +1,7 @@ import time import structlog -from sts1_sensors import ADXL345, BMM150, L3GD20H, TMP112 +from sts1_sensors import ADXL345, BME688, BMM150, L3GD20H, TMP112 log = structlog.get_logger() @@ -9,6 +9,7 @@ mag = BMM150() # Geomagnetic sensor gyro = L3GD20H() # Gyroscope temp = TMP112() # Temperature sensor +multi = BME688(enable_gas_measurements=True) # Pressure, humidity, temperature and gas sensor for _ in range(10): gx, gy, gz = accel.get_g() @@ -21,8 +22,15 @@ px, py, pz = gyro.get_position() s += f", {px=:.2f} dpfs, {py=:.2f} dpfs, {pz=:.2f} dpfs" - t = temp.get_temp() - s += f", {t:.2f} °C" + t1 = temp.get_temp() + s += f", temp1 {t1:.2f} °C" + t2 = multi.get_temperature() + p = multi.get_pressure() + h = multi.get_humidity() + heat = multi.get_heat_stable() + res = multi.get_gas_resistance() + s += f", temp2 {t2:.2f} °C, {p:.2f} hPa, {h:.2f} %RH, {heat=}, {res:.2f} Ohms" + log.info(s) time.sleep(2) diff --git a/src/sts1_sensors/edu/BME688.py b/src/sts1_sensors/edu/BME688.py index c0f67a2..54d9310 100644 --- a/src/sts1_sensors/edu/BME688.py +++ b/src/sts1_sensors/edu/BME688.py @@ -1,4 +1,5 @@ import os +import time import bme680 @@ -14,11 +15,14 @@ class BME688(AbstractSensor): _possible_iir_filter_sizes = [0, 1, 3, 7, 15, 31, 63, 127] def __init__(self, temperature_osr=8, humidity_osr=2, pressure_osr=4, iir_filter_size=3, - temperature_offset=0, address=None, bus=None): + temperature_offset=0, enable_gas_measurements=False, + gas_heater_temperature=320, gas_heater_duration=150, + address=None, bus=None): super().__init__(possible_addresses=[0x76, 0x77], bus=bus) self.address = address or int(os.environ.get("STS1_SENSOR_ADDRESS_BME688", "0x76"), 16) self.bme680 = bme680.BME680(self.address, self.bus) + self.last_query_time_ms = round(time.time() * 1000) self.temperature_osr = temperature_osr self.humidity_osr = humidity_osr @@ -26,6 +30,10 @@ def __init__(self, temperature_osr=8, humidity_osr=2, pressure_osr=4, iir_filter self.temperature_offset = temperature_offset self.iir_filter_size = iir_filter_size + self.enable_gas_measurements = enable_gas_measurements + self.gas_heater_temperature = gas_heater_temperature + self.gas_heater_duration = gas_heater_duration + @property def temperature_osr(self): return self._temperature_osr @@ -86,8 +94,60 @@ def temperature_offset(self): def temperature_offset(self, temperature_offset): self._temperature_offset = temperature_offset self.bme680.set_temp_offset(temperature_offset) + + @property + def enable_gas_measurements(self): + return self._enable_gas_measurements - def get_sensor_data(self): - self.bme680.get_sensor_data() + @enable_gas_measurements.setter + def enable_gas_measurements(self, enable_gas_measurements): + self._enable_gas_measurements = enable_gas_measurements + if enable_gas_measurements: + self.bme680.set_gas_status(bme680.ENABLE_GAS_MEAS) + else: + self.bme680.set_gas_status(bme680.DISABLE_GAS_MEAS) + + @property + def gas_heater_temperature(self): + return self._gas_heater_temperature + + @gas_heater_temperature.setter + def gas_heater_temperature(self, gas_heater_temperature): + self._gas_heater_temperature = gas_heater_temperature + self.bme680.set_gas_heater_temperature(gas_heater_temperature) + + @property + def gas_heater_duration(self): + return self._gas_heater_duration + + @gas_heater_duration.setter + def gas_heater_duration(self, gas_heater_duration): + self._gas_heater_duration = gas_heater_duration + self.bme680.set_gas_heater_duration(gas_heater_duration) + + def get_all_data(self): + curr_time_ms = round(time.time() * 1000) + if curr_time_ms - self.last_query_time_ms >= 10: + self.bme680.get_sensor_data() + self.last_query_time_ms = curr_time_ms return self.bme680.data + + def get_heat_stable(self): + return self.get_all_data().heat_stable + + def get_temperature(self): + # Temperature in degree celsius + return self.get_all_data().temperature + + def get_pressure(self): + # Pressure in hPa + return self.get_all_data().pressure + + def get_humidity(self): + # Humidity in % relative humidity + return self.get_all_data().humidity + + def get_gas_resistance(self): + # Gas resistance in Ohms + return self.get_all_data().gas_resistance \ No newline at end of file From 68c45b47f5ae4ed0ef593e0890cf66e2e0bf7bd3 Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Sat, 14 Dec 2024 01:27:13 +0100 Subject: [PATCH 24/26] tests --- tests/test_BME688.py | 56 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 tests/test_BME688.py diff --git a/tests/test_BME688.py b/tests/test_BME688.py new file mode 100644 index 0000000..6240928 --- /dev/null +++ b/tests/test_BME688.py @@ -0,0 +1,56 @@ +import os + +import pytest + +from sts1_sensors import BME688 + +def test_class_creation1(): + BME688() + +def test_class_creation5(): + BME688(address=0x76) + +def test_class_creation6(): + os.environ["STS1_SENSOR_ADDRESS_BME688"] = "0x76" + BME688() + del os.environ["STS1_SENSOR_ADDRESS_BME688"] + +def test_get_values1(): + s = BME688() + s.get_temperature() + +def test_get_values2(): + s = BME688() + s.get_pressure() + +def test_get_values3(): + s = BME688() + s.get_humidity() + +def test_get_values4(): + s = BME688(enable_gas_measurements=True) + s.get_heat_stable() + +def test_get_values5(): + s = BME688(enable_gas_measurements=True) + s.get_gas_resistance() + +def test_get_values6(): + s = BME688(enable_gas_measurements=True) + s.get_all_data() + +def test_set_values1(): + s = BME688() + s.enable_gas_measurements = True + s.gas_heater_temperature = 320 + s.gas_heater_duration = 150 + +def test_set_wrong_address1(): + with pytest.raises(ValueError): + BME688(address=0x99) + +def test_set_wrong_address2(): + with pytest.raises(ValueError): + os.environ["STS1_SENSOR_ADDRESS_BME688"] = "0x98" + BME688() + del os.environ["STS1_SENSOR_ADDRESS_BME688"] From c5390311d2f82ff8671f17cf060a1056e59b48a1 Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Sat, 14 Dec 2024 01:31:41 +0100 Subject: [PATCH 25/26] bump version --- docs/source/conf.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index c1a2e63..7280827 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -13,7 +13,7 @@ project = 'sts1-sensors' copyright = '2024, Simon Köfinger, Florian Rohrer' author = 'Simon Köfinger, Florian Rohrer' -release = 'v0.3.5' +release = 'v0.4.0' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/pyproject.toml b/pyproject.toml index 89c76dc..c30c5cb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sts1_sensors" -version = "0.3.5" +version = "0.4.0" description = "A sensor library for the CubeSat STS1 (TU Wien Space Team)." readme = "README.md" requires-python = ">=3.11,<3.12" From 9c55fd0edae67adb5795476a5ffb1b2a41bd89b0 Mon Sep 17 00:00:00 2001 From: Florian Rohrer Date: Sat, 14 Dec 2024 17:11:30 +0100 Subject: [PATCH 26/26] docs --- README.md | 4 +-- docs/source/conf.py | 4 +-- docs/source/index.rst | 10 ++++---- examples/edu/ADXL345.py | 4 +-- examples/edu/L3GD20H.py | 4 +-- examples/edu/all_sensors.py | 4 +-- pyproject.toml | 2 +- src/sts1_sensors/edu/ADXL345.py | 10 +++++++- src/sts1_sensors/edu/BME688.py | 43 ++++++++++++++++++++++++++++++--- src/sts1_sensors/edu/BMM150.py | 25 +++++++++++++++++-- src/sts1_sensors/edu/L3GD20H.py | 21 ++++++++++++++-- src/sts1_sensors/edu/TMP112.py | 7 ++++++ src/sts1_sensors/utils/utils.py | 6 +++++ tests/test_L3GD20H.py | 4 +-- 14 files changed, 121 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index a53d737..a4d48a5 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,8 @@ print(f"Heading: {mag.get_heading():.2f}°") # Gyroscope gyro = L3GD20H() -x, y, z = gyro.get_position() -print(f"{x=:.2f} dpfs, {y=:.2f} dpfs, {z=:.2f} dpfs") +x, y, z = gyro.get_dps() +print(f"{x=:.2f} dps, {y=:.2f} dps, {z=:.2f} dps") # Temperature sensor temp = TMP112() diff --git a/docs/source/conf.py b/docs/source/conf.py index 7280827..485a824 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -13,7 +13,7 @@ project = 'sts1-sensors' copyright = '2024, Simon Köfinger, Florian Rohrer' author = 'Simon Köfinger, Florian Rohrer' -release = 'v0.4.0' +release = 'v0.4.1' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration @@ -53,7 +53,7 @@ def skip_member_variables(app, what, name, obj, skip, options): if what == "attribute": skip = True - elif what == "module" and "Abstract" in name: + elif what == "module" and "Abstract" in name or "PatchedSMBus" in name: skip = True return skip diff --git a/docs/source/index.rst b/docs/source/index.rst index efcf59b..8fb5cd5 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -3,12 +3,12 @@ Main -.. toctree:: - :hidden: - :maxdepth: 2 - :caption: Contents +.. .. toctree:: +.. :hidden: +.. :maxdepth: 2 +.. :caption: Contents - ADXL345 +.. ADXL345 .. include:: ../../README.md :parser: myst_parser.sphinx_ diff --git a/examples/edu/ADXL345.py b/examples/edu/ADXL345.py index d5b8305..047d08d 100644 --- a/examples/edu/ADXL345.py +++ b/examples/edu/ADXL345.py @@ -10,5 +10,5 @@ while True: x, y, z = accel.get_g() - log.info(f"X: {x:.2f}g, Y: {y:.2f}g, Z: {z:.2f}g") - time.sleep(.2) + log.info(f"{x=:.2f} g, {y=:.2f} g, {z=:.2f} g") + time.sleep(.2) \ No newline at end of file diff --git a/examples/edu/L3GD20H.py b/examples/edu/L3GD20H.py index dfca814..2e30c47 100644 --- a/examples/edu/L3GD20H.py +++ b/examples/edu/L3GD20H.py @@ -7,6 +7,6 @@ gyro = L3GD20H(range=245, datarate=12.5) while True: - x, y, z = gyro.get_position() - log.info(f"X: {x:.2f}dpfs, Y: {y:.2f}dpfs, Z: {z:.2f}dpfs") + x, y, z = gyro.get_dps() + log.info(f"X: {x:.2f}dps, Y: {y:.2f}dps, Z: {z:.2f}dps") time.sleep(.25) diff --git a/examples/edu/all_sensors.py b/examples/edu/all_sensors.py index a5416c3..5b91ecc 100644 --- a/examples/edu/all_sensors.py +++ b/examples/edu/all_sensors.py @@ -19,8 +19,8 @@ s += f", {mx=:.2f} µT, {my=:.2f} µT, {mz=:.2f} µT" s += f", Heading: {mag.get_heading():.2f}°" - px, py, pz = gyro.get_position() - s += f", {px=:.2f} dpfs, {py=:.2f} dpfs, {pz=:.2f} dpfs" + px, py, pz = gyro.get_dps() + s += f", {px=:.2f} dps, {py=:.2f} dps, {pz=:.2f} dps" t1 = temp.get_temp() s += f", temp1 {t1:.2f} °C" diff --git a/pyproject.toml b/pyproject.toml index c30c5cb..234e27e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sts1_sensors" -version = "0.4.0" +version = "0.4.1" description = "A sensor library for the CubeSat STS1 (TU Wien Space Team)." readme = "README.md" requires-python = ">=3.11,<3.12" diff --git a/src/sts1_sensors/edu/ADXL345.py b/src/sts1_sensors/edu/ADXL345.py index 4606c2e..05fda95 100644 --- a/src/sts1_sensors/edu/ADXL345.py +++ b/src/sts1_sensors/edu/ADXL345.py @@ -12,12 +12,20 @@ def __init__(self, range=2, datarate=50, x_offset=0, y_offset=0, z_offset=0, add """Digital accelerometer. :param int range: How many gs the sensor should be able measure. Allowed values: `[2, 4, 8, 16]`. Defaults to 2. - :param int datarate: How often the sensor should measure in Hz. Allowed values: `[0.10, 0.20, 0.39, 0.78, 1.56, 3.13, 6.25, 12.5, 25, 50, 100, 200, 400, 800, 1600, 3200]`. Defaults to 50. + :param int datarate: Number of measurements per second [Hz]. Allowed values: `[0.10, 0.20, 0.39, 0.78, 1.56, 3.13, 6.25, 12.5, 25, 50, 100, 200, 400, 800, 1600, 3200]`. Defaults to 50. :param int x_offset: x-axis offset, defaults to 0. :param int y_offset: y-axis offset, defaults to 0. :param int z_offset: z-axis offset, defaults to 0. :param hexadecimal address: Physical address of the sensor on the board (see `i2cdetect` command). Allowed values: `[0x1D, 0x3A, 0x3B, 0x53]`. If None, the environment variable `STS1_SENSOR_ADDRESS_AVXL345` will be used. If environment variable is not found, 0x53 will be used. :param SMBus bus: A SMBus object. If None, this class will generate its own, defaults to None. + + Example: + + .. code-block:: python + + accel = ADXL345(range=2, datarate=50) + x, y, z = accel.get_g() + print(f"{x=:.2f} g, {y=:.2f} g, {z=:.2f} g") """ super().__init__(possible_addresses=[0x1D, 0x3A, 0x3B, 0x53], bus=bus) diff --git a/src/sts1_sensors/edu/BME688.py b/src/sts1_sensors/edu/BME688.py index 54d9310..9a023e0 100644 --- a/src/sts1_sensors/edu/BME688.py +++ b/src/sts1_sensors/edu/BME688.py @@ -18,6 +18,33 @@ def __init__(self, temperature_osr=8, humidity_osr=2, pressure_osr=4, iir_filter temperature_offset=0, enable_gas_measurements=False, gas_heater_temperature=320, gas_heater_duration=150, address=None, bus=None): + """Pressure, humidity, temperature and gas sensor. + + Builds on top of the library `bme680 `_. + + :param int temperature_osr: Temperature oversampling rate. Allowed values: `[None, 1, 2, 4, 8, 16]`. A higher oversampling value means more stable sensor readings with less noise and jitter However each step of oversampling adds about 2ms to the latency, causing a slower response time to fast transients. Defaults to 8. + :param int humidity_osr: Humidity oversampling rate. Allowed values: `[None, 1, 2, 4, 8, 16]`. A higher oversampling value means more stable sensor readings with less noise and jitter However each step of oversampling adds about 2ms to the latency, causing a slower response time to fast transients. Defaults to 2. + :param int pressure_osr: Pressure oversampling rate. Allowed values: `[None, 1, 2, 4, 8, 16]`. A higher oversampling value means more stable sensor readings with less noise and jitter However each step of oversampling adds about 2ms to the latency, causing a slower response time to fast transients. Defaults to 4. + :param int iir_filter_size: Number of infinite impulse response filter coefficients. Allowed values: `[0, 1, 3, 7, 15, 31, 63, 127]`. It removes short term fluctuations from the temperature and pressure readings (not humidity), increasing their resolution but reducing their bandwidth. Defaults to 3. + :param int temperature_offset: Temperature offset, defaults to 0. + :param bool enable_gas_measurements: Enable gas measurements, defaults to False. + :param int gas_heater_temperature: Set temperature of gas sensor heater [°C], between 200 and 400, defaults to 320. + :param int gas_heater_duration: Set duration of gas sensor heater [ms], between 1 and 4032. Approximately 20-30 ms are necessary for the heater to reach the intended target temperature. Defaults to 150. + :param hexadecimal address: Physical address of the sensor on the board (see `i2cdetect` command). Allowed values: `[0x76, 0x77]`. If None, the environment variable `STS1_SENSOR_ADDRESS_BME688` will be used. If environment variable is not found, 0x76 will be used. + :param SMBus bus: A SMBus object. If None, this class will generate its own, defaults to None. + + Example: + + .. code-block:: python + + sensor = BME688(temperature_osr=8, humidity_osr=2, pressure_osr=4, enable_gas_measurements=True) + t = sensor.get_temperature() + p = sensor.get_pressure() + h = sensor.get_humidity() + heat = sensor.get_heat_stable() + res = sensor.get_gas_resistance() + print(f"{t:.2f} °C, {p:.2f} hPa, {h:.2f} %RH, {heat=}, {res:.2f} Ohms") + """ super().__init__(possible_addresses=[0x76, 0x77], bus=bus) self.address = address or int(os.environ.get("STS1_SENSOR_ADDRESS_BME688", "0x76"), 16) @@ -126,6 +153,8 @@ def gas_heater_duration(self, gas_heater_duration): self.bme680.set_gas_heater_duration(gas_heater_duration) def get_all_data(self): + """Returns all sensor data. Calling this method after less than 10 ms will return the same (cached) result. + """ curr_time_ms = round(time.time() * 1000) if curr_time_ms - self.last_query_time_ms >= 10: self.bme680.get_sensor_data() @@ -133,21 +162,27 @@ def get_all_data(self): return self.bme680.data def get_heat_stable(self): + """Indicates whether or not the gas resistance value should be read. + """ return self.get_all_data().heat_stable def get_temperature(self): - # Temperature in degree celsius + """Temperature in degree celsius. + """ return self.get_all_data().temperature def get_pressure(self): - # Pressure in hPa + """Pressure in hPa. + """ return self.get_all_data().pressure def get_humidity(self): - # Humidity in % relative humidity + """Humidity in % relative humidity. + """ return self.get_all_data().humidity def get_gas_resistance(self): - # Gas resistance in Ohms + """Gas resistance in Ohms. + """ return self.get_all_data().gas_resistance \ No newline at end of file diff --git a/src/sts1_sensors/edu/BMM150.py b/src/sts1_sensors/edu/BMM150.py index 36b4ab5..6f217d3 100644 --- a/src/sts1_sensors/edu/BMM150.py +++ b/src/sts1_sensors/edu/BMM150.py @@ -8,10 +8,25 @@ class BMM150: """Geomagnetic sensor. - - Datasheet: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmm150-ds001.pdf """ + def __init__(self, address=None, bus=None): + """Geomagnetic sensor. + + Builds on top of the library `bmm150 `_. + + :param hexadecimal address: Physical address of the sensor on the board (see `i2cdetect` command). Allowed values: `[0x10, 0x11, 0x12, 0x13]`. If None, the environment variable `STS1_SENSOR_ADDRESS_BMM150` will be used. If environment variable is not found, 0x10 will be used. + :param SMBus bus: A SMBus object. If None, this class will generate its own, defaults to None. + + Example: + + .. code-block:: python + + mag = BMM150() + x, y, z = mag.get_magnetic_data() + print(f"{x=:.2f} µT, {y=:.2f} µT, {z=:.2f} µT") + print(f"Heading: {mag.get_heading():.2f}°") + """ self.possible_addresses = [0x10, 0x11, 0x12, 0x13] a = address or int(os.environ.get("STS1_SENSOR_ADDRESS_BMM150", "0x10"), 16) @@ -41,11 +56,17 @@ def address(self, address): self._address = address def get_raw_magnetic_data(self): + """Get raw magnetic data in µT. + """ return self.bmm.read_raw_mag_data() def get_magnetic_data(self): + """Get magnetic data in µT. + """ return self.bmm.read_mag_data() def get_heading(self): + """Get heading direction in degrees. Uses only x and y for calculation (z is ignored). + """ x, y, _ = self.get_magnetic_data() return math.degrees(math.atan2(x, y)) diff --git a/src/sts1_sensors/edu/L3GD20H.py b/src/sts1_sensors/edu/L3GD20H.py index 2a11246..f6b84ea 100644 --- a/src/sts1_sensors/edu/L3GD20H.py +++ b/src/sts1_sensors/edu/L3GD20H.py @@ -11,6 +11,21 @@ class L3GD20H(AbstractSensor): _possible_datarates_bin = [(0, 1), (1, 1), (2, 1), (0, 0), (1, 0), (2, 0), (3, 0)] def __init__(self, range=245, datarate=12.5, address=None, bus=None): + """Three-axis gyroscope. + + :param int range: Maximum angular velocity or speed of rotation that the gyro can read, measured in degrees per second (dps). Allowed values: `[245, 500, 2000]`, defaults to 245. + :param float datarate: Number of measurements per second [Hz]. Allowed values: `[12.5, 25, 50, 100, 200, 400, 800]`, defaults to 12.5. + :param hexadecimal address: Physical address of the sensor on the board (see `i2cdetect` command). Allowed values: `[0x6A, 0x6B]`. If None, the environment variable `STS1_SENSOR_ADDRESS_L3GD20H` will be used. If environment variable is not found, 0x6A will be used. + :param SMBus bus: A SMBus object. If None, this class will generate its own, defaults to None. + + Example: + + .. code-block:: python + + gyro = L3GD20H(range=245, datarate=12.5) + x, y, z = gyro.get_dps() + print(f"{x=:.2f} dps, {y=:.2f} dps, {z=:.2f} dps") + """ super().__init__(possible_addresses=[0x6A, 0x6B], bus=bus) self.address = address or int(os.environ.get("STS1_SENSOR_ADDRESS_L3GD20H", "0x6A"), 16) @@ -55,12 +70,14 @@ def _get_raw(self, var): lsb, msb = self.bus.read_i2c_block_data(self.address, self.xyz_addresses[var], 2) return twos_comp((msb << 8) + lsb, 16) - def get_position_raw(self): + def get_dps_raw(self): + "Get raw degrees per second." return self._get_raw("x"), self._get_raw("y"), self._get_raw("z") def _get_dps(self, var): k = self._get_raw(var) return k * self.dps_per_digit[self._possible_ranges.index(self.range)] - def get_position(self): + def get_dps(self): + "Get degrees per second." return self._get_dps("x"), self._get_dps("y"), self._get_dps("z") diff --git a/src/sts1_sensors/edu/TMP112.py b/src/sts1_sensors/edu/TMP112.py index f71205c..caff46f 100644 --- a/src/sts1_sensors/edu/TMP112.py +++ b/src/sts1_sensors/edu/TMP112.py @@ -16,6 +16,13 @@ def __init__(self, conversion_rate=1, extended_temp_range=True, address=None, bu :param bool extended_temp_range: If True, range: -55°C - 150°C, if False range: -55°C - 128°C, defaults to True. :param hexadecimal address: Physical address of the sensor on the board (see `i2cdetect` command). Allowed values: `[0x48, 0x49, 0x4A, 0x4B]`. If None, the environment variable `STS1_SENSOR_ADDRESS_TMP112` will be used. If environment variable is not found, 0x48 will be used. :param SMBus bus: A SMBus object. If None, this class will generate its own, defaults to None. + + Example: + + .. code-block:: python + + temp = TMP112(conversion_rate=1, extended_temp_range=True) + print(f"{temp.get_temp():.2f} °C") """ super().__init__(possible_addresses=[0x48, 0x49, 0x4A, 0x4B], bus=bus) diff --git a/src/sts1_sensors/utils/utils.py b/src/sts1_sensors/utils/utils.py index 92cc629..c911bf5 100644 --- a/src/sts1_sensors/utils/utils.py +++ b/src/sts1_sensors/utils/utils.py @@ -1,5 +1,11 @@ def twos_comp(val, bits): + """Convert a value into its 2-complement. + + :param int val: Value. + :param int bits: Number of available bits. + :return int: The number's 2-complement. + """ if val & (1 << (bits - 1)) != 0: val = val - (1 << bits) return val diff --git a/tests/test_L3GD20H.py b/tests/test_L3GD20H.py index 58d3916..d65dbd5 100644 --- a/tests/test_L3GD20H.py +++ b/tests/test_L3GD20H.py @@ -28,11 +28,11 @@ def test_class_creation6(): def test_get_postition1(): t = L3GD20H() - t.get_position_raw() + t.get_dps_raw() def test_get_postition2(): t = L3GD20H() - t.get_position() + t.get_dps() def test_set_wrong_address1(): with pytest.raises(ValueError):