How to test Modbus TCP without a PLC: three ways
· By Vladimir Chemeris, developer of Modbus TCP FieldKit
Short answer: for a laptop, run a pymodbus server with the script below. For a test across the room, or with no laptop, run the FieldKit server on an iPhone or iPad and connect from your client. Then test against the real device before you sign off, because a simulator won’t reproduce its quirks.
Way 1: a Python server on your laptop
This script ran as shown on 30 October 2026 with pymodbus 3.16.1 and Python 3.13 on Linux. It uses the SimData and SimDevice classes, the newer API that replaces ModbusSequentialDataBlock, which pymodbus marks as deprecated.
python3 -m venv venv
venv/bin/pip install pymodbus
"""A Modbus TCP test server on port 1502."""
import asyncio
from pymodbus.server import ModbusTcpServer
from pymodbus.simulator import DataType, SimData, SimDevice
device = SimDevice(1, simdata=[
SimData(0, values=[100, 250, 1024], datatype=DataType.REGISTERS),
SimData(10, values=[0x42F6, 0xE979, 0xE979, 0x42F6], datatype=DataType.REGISTERS),
])
async def main():
server = ModbusTcpServer(context=[device], address=("0.0.0.0", 1502))
await server.serve_forever()
asyncio.run(main())
It serves unit 1 with holding registers 100, 250 and 1024 at address 0, and the float 123.456 at address 10 in ABCD and at address 12 in CDAB.
To see the raw frames, this client sends one request and prints both directions:
"""Sends one raw Modbus TCP request: read.py HOST UNIT PDU_HEX"""
import socket, struct, sys
host, unit, pdu = sys.argv[1], int(sys.argv[2]), bytes.fromhex(sys.argv[3])
frame = struct.pack(">HHHB", 1, 0, len(pdu) + 1, unit) + pdu
with socket.create_connection((host, 1502), timeout=2) as s:
s.sendall(frame)
print("TX", frame.hex(" ").upper())
print("RX", s.recv(260).hex(" ").upper())
$ python read.py 127.0.0.1 1 0300000003
TX 00 01 00 00 00 06 01 03 00 00 00 03
RX 00 01 00 00 00 09 01 03 06 00 64 00 FA 04 00
Watch out for shared memory. With this setup, all four areas read the same registers. A coil read of address 0 returned 64, the low byte of 100, and an input register read returned 100 too. A client using 04 by mistake still gets a good-looking answer, so this setup won’t catch a wrong function code. pymodbus can keep the four areas apart if you pass four lists of blocks to simdata, one per area.
Open the firewall if a client on another machine connects: allow TCP 1502 on the laptop.
Way 2: FieldKit on an iPhone or iPad
Open Server, keep port 1502 and tap Start. Point your client at the address on screen, on the same Wi-Fi network. You can edit coils and holding registers on screen, and writes from your client appear in the map about twice a second.
It suits a test on site when you want to check a SCADA connection or an HMI screen with no laptop, or watch writes arrive live. Plan around two limits: the server runs in the foreground, so keep the app open, and it listens on 1502 by default, since 502 isn’t guaranteed on iOS. FieldKit’s own client talks to it from a second device.
Way 3: a dedicated simulator
Windows and Linux users often reach for a standalone simulator. Modbus Slave from Witte Software is the commercial option on Windows, alongside Modbus Poll. proconX publishes a command-line tool called diagslave. We haven’t tested these for this article, so check the current versions and licences on the vendors’ sites.
Comparing the three
| Python server | FieldKit server | Dedicated simulator | |
|---|---|---|---|
| Needs a laptop | Yes | No | Yes |
| Scriptable | Yes | No | Varies |
| Edit values live | In code | On screen | Varies |
| Port 502 | With elevated rights | Not guaranteed | Varies |
| Runs unattended | Yes | No, foreground only | Yes |
What a simulator won’t show you
We sent bad requests to pymodbus, and several answers broke the specification. The table is here. A client tested against one simulator alone can break on a real device. Also missing are serial timing, connection limits and the odd register maps real devices have. Finish with the real thing, and start with reads.
Sources
- pymodbus documentation (BSD-3-Clause, version 3.16.1 tested)
- Local tests on 30 October 2026 on Linux
FAQ
What is the fastest way to get a Modbus TCP server for testing?
On a laptop, install pymodbus with pip and run the 12-line script in this article. It listens on port 1502 and serves three holding registers and a float in two word orders.
Why use port 1502 instead of 502?
On Linux and macOS, ports below 1024 require elevated rights. Port 1502 runs without them. Many clients let you set the port, and a test needs nothing more.
Does a simulator behave like a real device?
Only in part. A simulator shows you protocol basics, but its error behaviour and timing differ from real devices, so test against the real device before you sign off.