Why simulate Modbus in Python instead of using a GUI tool?
Three reasons engineering teams reach for pymodbus specifically: it runs headless in CI/CD without a display, register values can be scripted and version-controlled alongside test code, and a server starts/stops in milliseconds so you can spin up dozens of simulated slaves in a single test run without licensing per-instance.
Installing pymodbus
pip install pymodbus
pymodbus 3.x supports Python 3.9+ and ships both synchronous and asyncio server APIs. The examples below use the synchronous API, which is the simplest starting point for a one-off simulated slave.
Minimal Modbus TCP slave simulator
This starts a Modbus TCP server on port 502 with holding registers pre-loaded with test values — enough to point any Modbus master (SCADA, PLC, or another script) at 127.0.0.1:502 and start polling immediately:
from pymodbus.datastore import (
ModbusSequentialDataBlock,
ModbusSlaveContext,
ModbusServerContext,
)
from pymodbus.server import StartTcpServer
# Pre-load holding registers 0-9 with test values
store = ModbusSlaveContext(
di=ModbusSequentialDataBlock(0, [0] * 100), # discrete inputs
co=ModbusSequentialDataBlock(0, [0] * 100), # coils
hr=ModbusSequentialDataBlock(0, [111, 222, 333, 0, 0, 0, 0, 0, 0, 0]), # holding registers
ir=ModbusSequentialDataBlock(0, [0] * 100), # input registers
)
context = ModbusServerContext(slaves=store, single=True)
StartTcpServer(context=context, address=("0.0.0.0", 502))
Any master reading holding registers 0-2 (function code 03) will get back 111, 222, 333 immediately — no PLC, no wiring, no real device required.
Updating register values while the server runs
Because store is a live Python object, you can update register values from another thread while the server is serving requests — useful for simulating a sensor value that changes over time in a test scenario:
import random, threading, time
def drift_holding_register():
while True:
value = random.randint(200, 250)
store.setValues(3, 0, [value]) # function code 3 = holding register
time.sleep(2)
threading.Thread(target=drift_holding_register, daemon=True).start()
Simulating Modbus RTU (serial) with pymodbus
Swap StartTcpServer for StartSerialServer to simulate an RTU slave over a real or virtual COM port (pair with a null-modem emulator like com0com on Windows to test without hardware):
from pymodbus.server import StartSerialServer
StartSerialServer(
context=context,
port="COM5", # or /dev/ttyUSB0 on Linux
baudrate=9600,
parity="N",
stopbits=1,
bytesize=8,
)
pymodbus vs. a desktop Modbus simulator
| Scenario | pymodbus (scripted) | Desktop GUI simulator |
|---|---|---|
| CI/CD integration test | Best fit — headless, fast startup | Not designed for headless runs |
| Non-developer manually checking a SCADA screen | Requires writing code | Best fit — point and click register edits |
| Dozens of simulated slaves at once | Easy to script and loop | Manageable, more clicking per slave |
| Live traffic view / packet inspection | Needs Wireshark or logging code | Often built in |
| License cost | Free, open source | Free tier, paid license for continued use |
The two aren't mutually exclusive — many teams use pymodbus in automated test suites and a desktop simulator for manual exploratory testing during development.
Related pages
Prefer a GUI over writing Python?
ModbusSimulator gives you master + slave modes, multiple simulated devices, and live register editing — no code required. Free 30-day trial, then a $99 one-time license.
Download Free tierFAQ
What is the best Python library for simulating a Modbus slave?
pymodbus is the de facto standard — an actively maintained, pure-Python implementation of the Modbus protocol with client, server, and simulator APIs for both TCP and serial (RTU/ASCII).
Can pymodbus simulate a Modbus TCP and RTU slave at the same time?
Yes, but as two separate server processes — StartTcpServer and StartSerialServer are separate entry points. You can run both in the same script using threads or asyncio if you need a slave reachable over both transports.
Do I need real hardware to test with a pyModbusSimulator?
No. A pymodbus TCP server binds to 127.0.0.1 or any local interface, so a Modbus master (SCADA, PLC logic, or another script) can poll it exactly like a real device with zero physical hardware.
Is pymodbus good enough for CI/CD test pipelines?
Yes — it's a common choice for automated integration tests because it starts and stops in milliseconds, requires no license, and its ModbusServerContext lets you script register values per test case.
When should I use a GUI Modbus simulator instead of pymodbus?
When you (or non-developer teammates) need to manually poke register values, watch live traffic, or configure multiple slaves without writing code. A desktop simulator with a GUI is faster for ad-hoc PLC/SCADA testing; pymodbus wins for scripted, repeatable, or CI-driven tests.