3
votes

I am trying to implement a simple synchronous TCP server using the Synchronous Server Example. However, I do not understand the syntax explanations in the documentation. The example includes the following code block:

store = ModbusSlaveContext(
     di=ModbusSequentialDataBlock(0, [17]*100),
     co=ModbusSequentialDataBlock(0, [17]*100),
     hr=ModbusSequentialDataBlock(0, [17]*100),
     ir=ModbusSequentialDataBlock(0, [17]*100))

context = ModbusServerContext(slaves=store, single=True)

Suppose I want to store a value of 152 to Input Register (ir) address 30001 and a value of 276 to address 30002? How should I adapt the above code?

2

2 Answers

2
votes

Suppose I want to store a value of 152 to 'Input Register (ir)' address 30001 and a value of 276 to address 30002? How should I adapt the above code?

Following code is what you want:

from pymodbus.server.sync import StartTcpServer
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext

import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s'
          ' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)


def run_server():
    store = ModbusSlaveContext(
        ir=ModbusSequentialDataBlock(30001, [152, 276]), 
        zero_mode=True
    )
    context = ModbusServerContext(slaves=store, single=True)
    StartTcpServer(context, address=("localhost", 5020))


if __name__ == "__main__":
    run_server()

Test Case:

from pymodbus.client.sync import ModbusTcpClient as ModbusClient

cli = ModbusClient('127.0.0.1', port=5020)
assert cli.connect()
res = cli.read_input_registers(30001, count=2, unit=1)
assert not res.isError()
print(res.registers)

Out:

[152, 276]
0
votes

From the Pymodbus docs.

The datastores only respond to the addresses that they are initialized to Therefore, if you initialize a DataBlock to addresses of 0x00 to 0xFF, a request to 0x100 will respond with an invalid address exception. This is because many devices exhibit this kind of behavior (but not all)::

 block = ModbusSequentialDataBlock(0x00, [0]*0xff)

The ModbusSequentialDataBlock takes two arugments during initialisation address and value.

address – The starting address
values –  List of values to initialise each address with. 

The total size of the datablock depends on the len(values) .

So looking at the above example , we are trying to create a sequential datablock of size 0xFF and each address initialised with value 0. Similarly In your case , if you want to store a value of 152 to 'Input Register (ir)' address 30001 and a value of 276 to address 30002, here is what you will have to do.

store = ModbusSlaveContext(

         di=ModbusSequentialDataBlock(0, [17]*100),
         co=ModbusSequentialDataBlock(0, [17]*100),
         hr=ModbusSequentialDataBlock(0, [17]*100),
         ir=ModbusSequentialDataBlock(0, [152, 276]), zero_mode=True)

context = ModbusServerContext(slaves=store, single=True)

Please note, the use of kwarg zero_mode=True with out this, read_input_registers request against offset 0 would return 276 instead of 152, this is because with out zero_mode=True pymodbus tries to store values based on section 4.4 of the specification, so address(0-7) will map to (1-8). If you don't want to use zero_mode then initialise the datablock as ir=ModbusSequentialDataBlock(1, [152, 276]).