Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Waveshare Modbus Relays #1575

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -890,15 +890,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))