So I'm attempting to make some software to replicate Modbus Devices for GUI and systems testing and demos. Issue is I want to run one Device Simulator Python Program on the same machine as it's connecting to so it'll be hosted on Localhost. I want to be able to run a server with different SlaveID's on one Localhost IP - almost simulating a RTU to TCP Gateway. One program, representing multiple different simulated devices on different Slave IDs over TCP. If anyone knows how I can run one server but write to different SlaveIDs on the server or if I can run multiple server objects in one program with different SlaveID's and connect to the general IP?
Not sure how much that makes sense but if anyone has any suggestions please let me know!
Thanks!
I was partway through creating a Custom Server Class for the ModbusServerTCP class when I realised I probably wouldn't be able to host multiple servers through the same program and connect to multiple on the same IP even if I could.
Yes, that actually makes quite a lot of sense.
You can implement a server with multiple IDs, which is actually the subject of one of the examples within pymodbus, you can find it here.
Another (and probably easier) option is to look at the server_async.py example. If you run this example with the argument --slaves 3
your server will respond to IDs 0x01
to 0x03
. By default all slaves will store the same contents but you can change that easily by modifying them within the setup_server
function.
And there is a third option that you might want to use. Instead of defining a server with multiple IDs you can create multiple servers with one or more IDs running on different ports. To do that you just have to run multiple instances of the server_async.py example each one from a different command window changing the --port
argument. You can run as many servers as you want on the same IP as long as you use different ports. Obviously, if you try to run two servers on the same IP and port you will get an error.
For the first two options, make sure you are running a recent pymodbus version. Older versions were reported to be buggy with regard to multiple slave contexts.
I'm not sure what you mean by almost simulating a RTU to TCP Gateway but be aware that there is a serial forwarder that implements a simple forwarder from an RTU serial client to a Modbus TCP server, that might be useful for your purposes.
To make my answer a bit more useful lets just practice the theory above with a small exercise with the examples.
After downloading the examples we move to the folder where they are stored and run the following in a command line:
python3 server_async.py --comm tcp --log debug --port 5020 --store sequential --slaves 3
This will define a Modbus TCP server on your localhost listening on port 5020 and three slaves at addresses 0x01, 0x02 and 0x03.
We keep that window aside and open a new window, where we call python and run the following lines to import pymodbus and instantiate the client:
>>> from pymodbus.client import ModbusTcpClient
>>> client=ModbusTcpClient(localhost,5020)
Now we can write some registers:
>>> client.write_register(1,111,slave=0x01)
>>> client.write_register(1,222,slave=0x02)
And if we read them back:
>>> rr1=client.read_holding_registers(1,1,unit=0x01)
>>> rr2=client.read_holding_registers(1,1,unit=0x02)
And print the results:
>>> print(rr1.registers)
[111]
>>> print(rr2.registers)
[222]
Bob's your uncle! Register 1 on slave 0x01 contains value 111 and register 1 on slave 0x02 contains value 222.
Going back to the server window you can check the debug messages with the Modbus frames exchanged by server and client, something similar to what I got:
13:29:37 DEBUG async_io:127 Socket [('127.0.0.1', 5020)] opened
13:29:37 DEBUG async_io:350 TCP client connection established [('127.0.0.1', 60410)]
13:29:37 DEBUG async_io:222 Handling data: 0x0 0x1 0x0 0x0 0x0 0x6 0x1 0x6 0x0 0x1 0x0 0x6f
13:29:37 DEBUG socket_framer:171 Processing: 0x0 0x1 0x0 0x0 0x0 0x6 0x1 0x6 0x0 0x1 0x0 0x6f
13:29:37 DEBUG factory:216 Factory Request[WriteSingleRegisterRequest': 6]
13:29:37 DEBUG context:66 validate: fc-[6] address-2: count-1
13:29:37 DEBUG context:93 setValues[6] address-2: count-1
13:29:37 DEBUG context:80 getValues: fc-[6] address-2: count-1
13:29:37 DEBUG async_io:299 send: [WriteRegisterResponse 1 => 111]- b'00010000000601060001006f'
13:29:48 DEBUG async_io:222 Handling data: 0x0 0x2 0x0 0x0 0x0 0x6 0x1 0x3 0x0 0x1 0x0 0x1
13:29:48 DEBUG socket_framer:171 Processing: 0x0 0x2 0x0 0x0 0x0 0x6 0x1 0x3 0x0 0x1 0x0 0x1
13:29:48 DEBUG factory:216 Factory Request[ReadHoldingRegistersRequest': 3]
13:29:48 DEBUG context:66 validate: fc-[3] address-2: count-1
13:29:48 DEBUG context:80 getValues: fc-[3] address-2: count-1
13:29:48 DEBUG async_io:299 send: [ReadHoldingRegistersResponse (1)]- b'000200000005010302006f'