diff --git a/CHANGELOG.md b/CHANGELOG.md index bef5502..8e04e42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add interface for totalizer - Ensure linux file endings across the package - Fix several typos -- Update GitHub actions to latest versions +- Update GitHub actions to the latest versions - Update dependencies +- Improve usage examples ## [1.1.1] - 2024-4-10 diff --git a/README.md b/README.md index 1cd6f21..97c68ed 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,13 @@ We provide two examples to show two basic usage scenarios of the driver python ./examples/scc1_slf3x_example/slf3x_usage.py --serial-port ``` +- **SLF3x Totalizer Usage** + This example enables the totalizer, resets it before starting a measurement, and prints the accumulated totalizer flow together with the sensor's configured unit. + + ```bash + python ./examples/scc1_slf3x_example/slf3x_totalizer_usage.py --serial-port + ``` + - **USB-I2c-Bridge** This example shows how a public python driver can be used with the SCC1-USB cable. The example uses the public driver `sensirion_i2c_sf06_lf`. Before you run the example you need to install this driver. diff --git a/examples/scc1_slf3x_example/slf3x_totalizer_usage.py b/examples/scc1_slf3x_example/slf3x_totalizer_usage.py new file mode 100644 index 0000000..b9062f9 --- /dev/null +++ b/examples/scc1_slf3x_example/slf3x_totalizer_usage.py @@ -0,0 +1,45 @@ +import argparse +import time + +from sensirion_shdlc_driver import ShdlcSerialPort, ShdlcConnection + +from sensirion_uart_scc1.drivers.scc1_slf3x import Scc1Slf3x +from sensirion_uart_scc1.drivers.slf_common import get_volume_unit_label, SlfProductName +from sensirion_uart_scc1.scc1_shdlc_device import Scc1ShdlcDevice + +parser = argparse.ArgumentParser() +parser.add_argument('--serial-port', '-p', default='COM5') +args = parser.parse_args() + +with ShdlcSerialPort(port=args.serial_port, baudrate=115200) as port: + device = Scc1ShdlcDevice(ShdlcConnection(port), target_address=0) + device.sensor_reset() + device.set_sensor_type(Scc1Slf3x.SENSOR_TYPE) + sensor = Scc1Slf3x(device) + + print(f'Product: {SlfProductName.from_product_id(sensor.product_id)}') + print(f"product id: 0x{sensor.product_id:08X}") + print(f"Serial number: {sensor.serial_number}") + + flow_scale, unit = sensor.get_flow_unit_and_scale() + if flow_scale is None or unit is None: + raise RuntimeError("Could not determine the sensor flow unit and scale") + volume_unit_label = get_volume_unit_label(unit) + + sensor.set_totalizator_status(True) + sensor.reset_totalizator() + # Measure as fast as possible + sensor.start_continuous_measurement(interval_ms=0) + try: + for _ in range(10): + time.sleep(1) + totalizer_value = sensor.get_totalizator_value() + if totalizer_value is None: + print("Totalizer value not available") + continue + + totalized_flow = totalizer_value / flow_scale + print(f"Totalizer volume: {totalized_flow} {volume_unit_label}") + + finally: + sensor.stop_continuous_measurement() diff --git a/examples/scc1_slf3x_example/slf3x_usage.py b/examples/scc1_slf3x_example/slf3x_usage.py index adcce3d..017681e 100644 --- a/examples/scc1_slf3x_example/slf3x_usage.py +++ b/examples/scc1_slf3x_example/slf3x_usage.py @@ -3,7 +3,7 @@ from sensirion_shdlc_driver import ShdlcSerialPort, ShdlcConnection from sensirion_uart_scc1.drivers.scc1_slf3x import Scc1Slf3x -from sensirion_uart_scc1.drivers.slf_common import get_flow_unit_label +from sensirion_uart_scc1.drivers.slf_common import get_flow_unit_label, SlfProductName from sensirion_uart_scc1.scc1_shdlc_device import Scc1ShdlcDevice parser = argparse.ArgumentParser() @@ -12,19 +12,27 @@ with ShdlcSerialPort(port=args.serial_port, baudrate=115200) as port: device = Scc1ShdlcDevice(ShdlcConnection(port), target_address=0) + device.sensor_reset() device.set_sensor_type(Scc1Slf3x.SENSOR_TYPE) sensor = Scc1Slf3x(device) - print("serial_number:", sensor.serial_number) - print("product id:", sensor.product_id) - print("Flow;\tTemperature;\t Flag") + + print(f'Product: {SlfProductName.from_product_id(sensor.product_id)}') + print(f"product id: 0x{sensor.product_id:08X}") + print(f"Serial number: {sensor.serial_number}") + + print("Flow;\tTemperature;\tAir In Line;\tHigh Flow") flow_scale, unit = sensor.get_flow_unit_and_scale() + unit_label = get_flow_unit_label(unit) sensor.start_continuous_measurement(interval_ms=2) try: - for _ in range(1000): + for _ in range(50): remaining, lost, data = sensor.read_extended_buffer() - print(f"Remaining bytes {remaining} and {lost}-sample lost") - print() for flow, temperature, flag in data: - print(f'{flow / flow_scale} {get_flow_unit_label(unit)};\t{temperature / 200} C;\t {flag}') + air_in_line = bool(flag & 0x01) + high_flow = bool((flag >> 1) & 0x01) + flow_scaled = flow / flow_scale + temperature_scaled = temperature / 200 + + print(f'{flow_scaled:.3f} {unit_label};\t{temperature_scaled:.3f} C;\t{air_in_line};\t{high_flow}') finally: sensor.stop_continuous_measurement() diff --git a/examples/scc1_usb_to_i2c/scc1_usb_2_i2c_usage.py b/examples/scc1_usb_to_i2c/scc1_usb_2_i2c_usage.py index 0b2af90..2c0311e 100644 --- a/examples/scc1_usb_to_i2c/scc1_usb_2_i2c_usage.py +++ b/examples/scc1_usb_to_i2c/scc1_usb_2_i2c_usage.py @@ -8,10 +8,12 @@ from sensirion_i2c_sf06_lf.device import Sf06LfDevice from sensirion_shdlc_driver import ShdlcSerialPort, ShdlcConnection +from sensirion_uart_scc1.drivers.slf_common import SlfProductName from sensirion_uart_scc1.scc1_shdlc_device import Scc1ShdlcDevice with ShdlcSerialPort(port='COM5', baudrate=115200) as port: bridge = Scc1ShdlcDevice(ShdlcConnection(port), target_address=0) + bridge.sensor_reset() bridge.set_sensor_type(3) # SF06 devices channel = I2cChannel(bridge.get_i2c_transceiver(), @@ -25,11 +27,15 @@ time.sleep(0.1) except BaseException: ... - (product_identifier, serial_number - ) = sensor.read_product_identifier() - print(f"product_identifier: {product_identifier}; " f"serial_number: {serial_number}; ") + + product_identifier, serial_number = sensor.read_product_identifier() + + print(f'Product: {SlfProductName.from_product_id(sensor.product_id)}') + print(f"product id: 0x{sensor.product_id:08X}") + print(f"Serial number: {sensor.serial_number}") + sensor.start_h2o_continuous_measurement() - for i in range(500): + for i in range(50): try: time.sleep(0.02) (a_flow, a_temperature, a_signaling_flags diff --git a/sensirion_uart_scc1/drivers/slf_common.py b/sensirion_uart_scc1/drivers/slf_common.py index 4b2e7aa..42d26cc 100644 --- a/sensirion_uart_scc1/drivers/slf_common.py +++ b/sensirion_uart_scc1/drivers/slf_common.py @@ -7,6 +7,7 @@ from collections import OrderedDict from enum import Enum +from typing import Optional from sensirion_uart_scc1.scc1_exceptions import Scc1InvalidProductId @@ -23,7 +24,7 @@ class SlfMode(Enum): LIQUI_8 = 'Liquid 8' -class SlfMeasurementCommand(object): +class SlfMeasurementCommand: MEASUREMENT_COMMANDS = { SlfMode.LIQUI_0: 0x3603, SlfMode.LIQUI_1: 0x3608, @@ -79,7 +80,9 @@ class SlfProduct(Enum): SF06 = 'SF06' @staticmethod - def from_product_id(product_id: int) -> SlfProduct: + def from_product_id(product_id: Optional[int]) -> SlfProduct: + # remove last byte of the product id for detection + product_id = product_id >> 8 if product_id in SLF3x_PRODUCT_IDS: return SlfProduct.SLF3x elif product_id in LD20_2600B_PRODUCT_IDS: @@ -91,7 +94,10 @@ def from_product_id(product_id: int) -> SlfProduct: class SlfProductName: @staticmethod - def from_product(product: SlfProduct, product_id: int) -> str: + def from_product_id(product_id: Optional[int]) -> str: + # remove last byte of the product id for detection + product = SlfProduct.from_product_id(product_id) + product_id = product_id >> 8 if product is SlfProduct.SLF3x: return SLF3x_PRODUCT_NAME.get(product_id, 'SLF3x') elif product is SlfProduct.LD20: @@ -169,3 +175,9 @@ def get_flow_unit_label(flow_unit_raw: int) -> str: time_base = FLOW_UNIT_TIME_BASE.get((flow_unit_raw >> 4) & 0xF, '') volume = FLOW_UNIT_VOLUME.get((flow_unit_raw >> 8) & 0xF, '') return f'{prefix}{volume}/{time_base}' + + +def get_volume_unit_label(flow_unit_raw: int) -> str: + prefix = FLOW_UNIT_PREFIX.get(flow_unit_raw & 0xF, '') + volume = FLOW_UNIT_VOLUME.get((flow_unit_raw >> 8) & 0xF, '') + return f'{prefix}{volume}'