Modbus#

Description#

We use the library PyModbus for our python programs that need a Modbus connection. This library provides classes for both client and server. Several examples are available directly on their webpage.

Here is a basic server with Holding registers :

from pymodbus.server.sync import StartTcpServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.constants import Endian
from pymodbus.payload import BinaryPayloadBuilder

# Holding registers start at :
START_ADDRESS_READ = 30000

# start server
def run_server():
   # Initialize the server configuration, we only need a few Holding Registers
   store = ModbusSlaveContext(
      hr=ModbusSequentialDataBlock(START_ADDRESS_READ, [0.0] * 100))
   context = ModbusServerContext(slaves=store, single=True)

   # Set one of the holding register
   builder = BinaryPayloadBuilder(endian=Endian.Big)
   builder.add_32bit_float(27.3)
   payload = builder.to_registers()
   context[0].setValues(3, START_ADDRESS_READ, payload)

   # Initialize the server information
   identity = ModbusDeviceIdentification()
   identity.VendorName = 'SPL'
   identity.ProductCode = ''
   identity.VendorUrl = ''
   identity.ProductName = 'Pymodbus Server'
   identity.ModelName = 'Virtual Server'
   identity.MajorMinorRevision = '2.3.0'

   # Run the server
   StartTcpServer(context, identity=identity, address=("localhost", 1234))


if __name__ == "__main__":
   print("Launching the server !")
   run_server()

The client that could read registers from this server :

from pymodbus.client.sync import ModbusTcpClient
from pymodbus.constants import Endian
from pymodbus.payload import BinaryPayloadDecoder
from pymodbus.register_read_message import ReadHoldingRegistersResponse
from pymodbus.pdu import ExceptionResponse
from pymodbus.exceptions import ModbusIOException


START_ADDRESS_READ = 30000
REGISTERS_NUMBER = 2
SLAVE_ID = 1

# from bytes to float
def process_data(modbus_response, verbose):
   decoder = BinaryPayloadDecoder.fromRegisters(modbus_response.registers, endian=Endian.Big)
   position = decoder.decode_32bit_float()
   return position


if __name__ == "__main__":
   client = ModbusTcpClient(ip_address_plc, port=modbus_port_plc)
   is_connected = client.connect()
   data = client.read_holding_registers(START_ADDRESS_READ, count=REGISTERS_NUMBER, unit=SLAVE_ID)
   if isinstance(data, ReadHoldingRegistersResponse):
      position = process_data(data, print_in_terminal)

Ressources#

The documentation of the PyModbus library can be found here.

Examples#

One can find a an example in the DT repo for the odometry.

Python Modbus