diff --git a/doc/configuration.rst b/doc/configuration.rst index 4a3ffea66..d19b9277b 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -416,6 +416,32 @@ Arguments: Used by: - `ModbusCoilDriver`_ +WaveshareModbusTCPCoil +++++++++++++++++++++++ +A :any:`WaveshareModbusTCPCoil` describes a Waveshare branded coil accessible via *Modbus TCP*. + +.. code-block:: yaml + + WaveshareModbusTCPCoil: + host: '192.168.23.42' + coil: 1 + coil_count: 8 + +The example describes the coil ``1`` (zero indexed) of ``8`` on the Waveshare Modbus TCP relay +module ``192.168.23.42``. + +Arguments: + - host (str): hostname of the Modbus TCP server e.g. ``192.168.23.42:502`` + - coil (int): index of the coil, e.g. ``3`` + - invert (bool, default=False): whether the logic level is inverted + (active-low) + - write_multiple_coils (bool, default=False): whether to perform write + using "write multiple coils" method instead of "write single coil" + +Used by: + - `WaveShareModbusCoilDriver`_ + + DeditecRelais8 ++++++++++++++ A :any:`DeditecRelais8` describes a *Deditec USB GPO module* with 8 relays. @@ -2334,6 +2360,25 @@ Implements: Arguments: - None +WaveShareModbusCoilDriver +~~~~~~~~~~~~~~~~~~~~~~~~~ +A :any:`WaveShareModbusCoilDriver` controls a `WaveshareModbusTCPCoil`_ resource. +It can set and get the current state of the resource. + +Binds to: + coil: + - `WaveshareModbusTCPCoil`_ + +Implements: + - :any:`DigitalOutputProtocol` + +.. code-block:: yaml + + WaveShareModbusCoilDriver: {} + +Arguments: + - None + HIDRelayDriver ~~~~~~~~~~~~~~ An :any:`HIDRelayDriver` controls an `HIDRelay`_ or `NetworkHIDRelay`_ resource. diff --git a/labgrid/driver/modbusdriver.py b/labgrid/driver/modbusdriver.py index 5bddc2437..2866a220c 100644 --- a/labgrid/driver/modbusdriver.py +++ b/labgrid/driver/modbusdriver.py @@ -60,3 +60,34 @@ def get(self): if self.coil.invert: status = not status return status + + +@target_factory.reg_driver +@attr.s(eq=False) +class WaveShareModbusCoilDriver(ModbusCoilDriver): + """ + Waveshare Modbus Relay driver. + + Waveshare only implement the ability to query the status of all relays not just one. + https://www.waveshare.com/wiki/Modbus_RTU_Relay#Read_States_of_Relays + """ + + bindings = { + "coil": "WaveshareModbusTCPCoil", + } + + @Driver.check_active + def get(self): + status = self.client.read_coils(0x0000, self.coil.coil_count) + if status is None: + self._handle_error("read") + + status = status[self.coil.coil] + if self.coil.invert: + status = not status + return status + + def __attrs_post_init__(self): + super().__attrs_post_init__() + if self.coil.coil >= self.coil.coil_count: + raise ValueError("Coil exceeds coil count") diff --git a/labgrid/remote/client.py b/labgrid/remote/client.py index 5ab4f0683..ce68cd15e 100755 --- a/labgrid/remote/client.py +++ b/labgrid/remote/client.py @@ -890,7 +890,7 @@ def digital_io(self): action = self.args.action name = self.args.name target = self._get_target(place) - from ..resource import ModbusTCPCoil, OneWirePIO, HttpDigitalOutput + from ..resource import ModbusTCPCoil, OneWirePIO, HttpDigitalOutput, WaveshareModbusTCPCoil from ..resource.remote import NetworkDeditecRelais8, NetworkSysfsGPIO, NetworkLXAIOBusPIO, NetworkHIDRelay drv = None @@ -898,7 +898,9 @@ def digital_io(self): drv = target.get_driver("DigitalOutputProtocol", name=name) except NoDriverFoundError: for resource in target.resources: - if isinstance(resource, ModbusTCPCoil): + if isinstance(resource, WaveshareModbusTCPCoil): + drv = self._get_driver_or_new(target, "WaveShareModbusCoilDriver", name=name) + elif isinstance(resource, ModbusTCPCoil): drv = self._get_driver_or_new(target, "ModbusCoilDriver", name=name) elif isinstance(resource, OneWirePIO): drv = self._get_driver_or_new(target, "OneWirePIODriver", name=name) diff --git a/labgrid/resource/__init__.py b/labgrid/resource/__init__.py index dd7554dff..6ec9d5db8 100644 --- a/labgrid/resource/__init__.py +++ b/labgrid/resource/__init__.py @@ -1,7 +1,7 @@ from .base import SerialPort, NetworkInterface, EthernetPort, SysfsGPIO from .ethernetport import SNMPEthernetPort from .serialport import RawSerialPort, NetworkSerialPort -from .modbus import ModbusTCPCoil +from .modbus import ModbusTCPCoil, WaveshareModbusTCPCoil from .modbusrtu import ModbusRTU from .networkservice import NetworkService from .onewireport import OneWirePIO diff --git a/labgrid/resource/modbus.py b/labgrid/resource/modbus.py index 221bb6ef3..587dc9726 100644 --- a/labgrid/resource/modbus.py +++ b/labgrid/resource/modbus.py @@ -21,3 +21,17 @@ class ModbusTCPCoil(Resource): write_multiple_coils = attr.ib( default=False, validator=attr.validators.instance_of(bool) ) + +@target_factory.reg_resource +@attr.s(eq=False) +class WaveshareModbusTCPCoil(ModbusTCPCoil): + """This resource describes Waveshare brand Modbus TCP coil. + + Args: + host (str): hostname of the Modbus TCP server e.g. "192.168.23.42:502" + coil (int): index of the coil e.g. 3 + coil_count (int): The total number of coils on this module. + invert (bool): optional, whether the logic level is be inverted (active-low) + write_multiple_coils (bool): optional, whether write using multiple coils method""" + + coil_count = attr.ib(default=8, validator=attr.validators.instance_of(int))