diff --git a/examples/scc1_slf3x_example/slf3x_usage.py b/examples/scc1_slf3x_example/slf3x_usage.py index fdd4bdf..3bcd70e 100644 --- a/examples/scc1_slf3x_example/slf3x_usage.py +++ b/examples/scc1_slf3x_example/slf3x_usage.py @@ -12,6 +12,7 @@ with ShdlcSerialPort(port=args.serial_port, baudrate=115200) as port: device = Scc1ShdlcDevice(ShdlcConnection(port), slave_address=0) + device.set_sensor_type(Scc1Slf3x.SENSOR_TYPE) sensor = Scc1Slf3x(device) print("serial_number:", sensor.serial_number) print("product id:", sensor.product_id) 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 d682a60..e3117c6 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 @@ -12,12 +12,14 @@ with ShdlcSerialPort(port='COM5', baudrate=115200) as port: bridge = Scc1ShdlcDevice(ShdlcConnection(port), slave_address=0) + bridge.set_sensor_type(3) # SF06 devices channel = I2cChannel(bridge.get_i2c_transceiver(), slave_address=0x08, crc=CrcCalculator(8, 0x31, 0xff, 0x0)) sensor = Sf06LfDevice(channel) + try: sensor.stop_continuous_measurement() time.sleep(0.1) diff --git a/sensirion_uart_scc1/scc1_shdlc_device.py b/sensirion_uart_scc1/scc1_shdlc_device.py index cfe4647..29482b9 100644 --- a/sensirion_uart_scc1/scc1_shdlc_device.py +++ b/sensirion_uart_scc1/scc1_shdlc_device.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- import logging -from typing import Optional, Iterable, Union +from struct import unpack +from typing import Optional, Iterable, Union, List from packaging.version import Version from sensirion_shdlc_driver import ShdlcDevice, ShdlcConnection @@ -15,7 +16,7 @@ class Scc1ShdlcDevice(ShdlcDevice): """ - The Scc1 Shdlc device is used to communicate with various sensor using the Sensirion SCC1 sensor cable. + The Scc1 SHDLC device is used to communicate with various sensors using the Sensirion SCC1 sensor cable. """ def __init__(self, connection: ShdlcConnection, slave_address: int = 0) -> None: @@ -27,12 +28,15 @@ def __init__(self, connection: ShdlcConnection, slave_address: int = 0) -> None: super().__init__(connection, slave_address) self._version = self.get_version() self._serial_number = self.get_serial_number() + self._sensor_type = self.get_sensor_type() + self._i2c_address = self.get_sensor_address() + self._connected_i2c_addresses: List[int] = [] - def __str__(self): + def __str__(self) -> str: return f"SCC1-{self.serial_number}@{self.com_port}" @property - def com_port(self): + def com_port(self) -> str: return self.connection.port.description.split('@')[0] @property @@ -43,10 +47,75 @@ def serial_number(self) -> str: def firmware_version(self) -> Version: return Version(str(self._version.firmware)) + @property + def connected_i2c_addresses(self) -> List[int]: + """Returns the connected I2C addresses. You need to call find_chips to fill this attribute.""" + return self._connected_i2c_addresses + + def perform_i2c_scan(self) -> List[int]: + """ + Looks for i2c devices within a certain range on a certain port + :return: List of i2c addresses that responded to the scan + """ + result = self.transceive(0x29, [0x01], timeout=0.025) + return list(unpack('>{cnt}B'.format(cnt=len(result)), result)) + + def find_chips(self) -> List[int]: + """ + Looking for chips on all ports and sets the _connected_i2c_addresses attribute + :return: List of connected addresses + """ + self._connected_i2c_addresses = self.perform_i2c_scan() + return self._connected_i2c_addresses + + def get_sensor_type(self) -> Optional[int]: + """ + :return: the configured sensor type + """ + result = self.transceive(0x24, [], timeout=0.025) + if result: + return int(unpack('>B', result)[0]) + return None + + def set_sensor_type(self, sensor_type: int): + """ + Set sensor type + 0: Flow Sensor (SF04 based products) + 1: Humidity Sensor (SHTxx products) + 2: Flow Sensor (SF05 based products) + 3: Flow Sensor (SF06 based products) (Firmware ≥1.7) + 4: Reserved + :param sensor_type: One of the supported sensor types 0-4 + + """ + if sensor_type not in range(5): + raise ValueError('Sensor type not supported') + self.transceive(0x24, [sensor_type], timeout=0.01) + self._sensor_type = sensor_type + + def get_sensor_address(self) -> Optional[int]: + """ + :return: the configured i2c address + """ + result = self.transceive(0x25, [], timeout=0.025) + if result: + return int(unpack('>B', result)[0]) + return None + + def set_sensor_address(self, i2c_address: int) -> None: + """ + Configure the sensors i2c address and write it to EEPROM + :param i2c_address: the i2c address + """ + if i2c_address not in range(128): + raise ValueError('I2C address out of range. Address has to be within the range 0...127') + self.transceive(0x25, [i2c_address], timeout=0.01) + self._i2c_address = i2c_address + def sensor_reset(self) -> None: """ - Execute a hard reset on the sensor and check for correct response. Active - continuous/single measurement is stopped and the sensor is left in idle state. + Execute a hard reset on the sensor and check for the correct response. + Active continuous/single measurement is stopped, and the sensor is left in idle state. """ self.transceive(0x66, [], 0.3) @@ -74,8 +143,8 @@ def get_i2c_transceiver(self) -> I2cTransceiver: """ An I2cTransceiver object is required in or der to use the cable with public python i2c drivers. - In general all functionality of the sensors are available in the public python drivers as well. The - throughput of the public python driver will be lower than the throughput that can be achieved with - the sensor specific api of the SCC1 sensor cable. + In general, all functionality of the sensors is available in the public python drivers as well. + The throughput of the public python driver will be lower than the throughput that can be achieved with + the sensor-specific api of the SCC1 sensor cable. """ return Scc1I2cTransceiver(self)