Skip to content

Commit

Permalink
Add support for Waveshare Modbus Relays
Browse files Browse the repository at this point in the history
Waveshare only implement the ability to query the status of all relays
(https://www.waveshare.com/wiki/Modbus_RTU_Relay#Read_States_of_Relays)
not just one, so a modification to the existing drivers is required.

Signed-off-by: Sebastian Goscik <[email protected]>
  • Loading branch information
ep1cman authored and Emantor committed Feb 17, 2025
1 parent cea524d commit 9be84ea
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 3 deletions.
45 changes: 45 additions & 0 deletions doc/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
31 changes: 31 additions & 0 deletions labgrid/driver/modbusdriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
6 changes: 4 additions & 2 deletions labgrid/remote/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -904,15 +904,17 @@ 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
try:
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)
Expand Down
2 changes: 1 addition & 1 deletion labgrid/resource/__init__.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
14 changes: 14 additions & 0 deletions labgrid/resource/modbus.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))

0 comments on commit 9be84ea

Please sign in to comment.