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

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.