How to Simulate a Modbus Slave Device
A Modbus slave simulator is a software tool that pretends to be a real Modbus device. It listens for incoming requests from a master (client) and responds exactly the way a physical device would — with register values, coil states, and proper exception codes. This lets you develop, test, and debug your SCADA, HMI, or PLC integration without needing the actual field hardware connected.
Why You Need a Slave Simulator
If you have ever waited three weeks for a power meter to arrive before you could start writing your polling logic, you already know why slave simulators exist. Here are the scenarios where they save real time and money:
- Testing SCADA/HMI without field hardware — Your SCADA project is due in two weeks but the site hasn't been built yet. A simulator lets you develop and validate every screen, alarm, and data point against a virtual device that behaves identically to the real one.
- Developing PLC programs before devices arrive — You can write and test your PLC's Modbus master logic in the office, weeks before commissioning. When you arrive on site, the code already works.
- Regression testing after firmware updates — A device vendor ships a firmware update that changes register behaviour. Before deploying, run your full test suite against a simulator configured with the new register map to catch breaking changes.
- Training new engineers on Modbus — Let trainees experiment with reading registers, writing coils, and deliberately triggering exception codes without any risk to production equipment.
- Load testing and performance benchmarking — Spin up 10 or 50 simulated slaves to see how your master handles high device counts, concurrent connections, or fast polling intervals.
What a Slave Simulator Does
At a protocol level, a Modbus slave simulator does four things:
- Listens for connections — On TCP port 502 (or a serial port for RTU), it waits for incoming Modbus requests from a master.
- Parses standard function codes — It handles the core read functions (
FC01Read Coils,FC02Read Discrete Inputs,FC03Read Holding Registers,FC04Read Input Registers) and write functions (FC05Write Single Coil,FC06Write Single Register,FC15Write Multiple Coils,FC16Write Multiple Registers). - Maintains a register space — It holds four data blocks in memory: coils (1-bit read/write), discrete inputs (1-bit read-only), holding registers (16-bit read/write), and input registers (16-bit read-only). You set initial values, and the simulator serves them on request.
- Lets you modify values dynamically — You can change register values at any time to simulate different device states: normal operation, alarm conditions, sensor drift, or counter rollover.
| Function Code | Name | Data Block | Operation |
|---|---|---|---|
FC01 | Read Coils | Coils (0xxxx) | Read |
FC02 | Read Discrete Inputs | Discrete Inputs (1xxxx) | Read |
FC03 | Read Holding Registers | Holding Registers (4xxxx) | Read |
FC04 | Read Input Registers | Input Registers (3xxxx) | Read |
FC05 | Write Single Coil | Coils (0xxxx) | Write |
FC06 | Write Single Register | Holding Registers (4xxxx) | Write |
FC15 | Write Multiple Coils | Coils (0xxxx) | Write |
FC16 | Write Multiple Registers | Holding Registers (4xxxx) | Write |
Setting Up a TCP Slave Simulator
Choosing a tool
Three widely-used options for Modbus TCP slave simulation:
- Modbus Slave by modbustools.com — A mature Windows GUI tool. Supports up to 247 slave IDs simultaneously, with direct register editing. The free version is fully functional with some limitations.
- diagslave — A lightweight command-line tool by Modbuspal. Runs on Windows, Linux, and macOS. Ideal for headless servers and CI/CD pipelines where you need a slave in a Docker container or automated test environment.
- ModRSsim2 — An open-source Windows simulator that supports all four data blocks with a spreadsheet-like register editor. Good for quick testing but no longer actively maintained.
Port considerations
The standard Modbus TCP port is 502. On both Windows and Linux, binding to any port below 1024 requires elevated privileges (Administrator on Windows, root or CAP_NET_BIND_SERVICE on Linux). For development and testing, use a high port like 5020 and configure your master to connect there instead.
If your simulator fails to start with "address already in use" or "permission denied", either run it as Administrator/root or switch to a port above 1024. Port 5020 is a common convention for Modbus testing.
diagslave example (TCP)
Start a TCP slave on port 5020, responding to slave ID 1, with all registers initialized to zero:
# Linux / macOS
./diagslave -m tcp -p 5020 -a 1
# Windows
diagslave.exe -m tcp -p 5020 -a 1
Your master can now connect to localhost:5020 (or your machine's IP) and poll slave ID 1. Every register will return 0x0000 until you write values to them from a master.
Firewall rules
If the master is on a different machine, you need to open the port:
# Windows (PowerShell as Administrator)
New-NetFirewallRule -DisplayName "Modbus Slave Simulator" `
-Direction Inbound -Protocol TCP -LocalPort 5020 -Action Allow
# Linux (iptables)
sudo iptables -A INPUT -p tcp --dport 5020 -j ACCEPT
# Linux (firewalld)
sudo firewall-cmd --add-port=5020/tcp --permanent
sudo firewall-cmd --reload
Setting Up an RTU Slave Simulator
Physical serial port
If you have a USB-to-RS485 adapter, you can run an RTU slave directly on a physical serial port. The setup is the same as TCP, but you specify the COM port (Windows) or device path (Linux) instead of a TCP port:
# Linux — diagslave on /dev/ttyUSB0 at 9600 baud, 8N1, slave ID 1
./diagslave -m rtu -a 1 -b 9600 -d 8 -s 1 -p none /dev/ttyUSB0
# Windows — diagslave on COM3
diagslave.exe -m rtu -a 1 -b 9600 -d 8 -s 1 -p none COM3
Virtual COM ports
When you don't have physical hardware, use virtual serial port pairs. A virtual COM port pair creates two connected ports: what you write to one appears on the other. Your slave simulator listens on one end, and your master connects to the other.
- Windows — com0com: Install com0com, create a pair (e.g.,
COM10↔COM11). Run the slave onCOM10, point your master atCOM11. - Linux — socat: Create a virtual pair with
socat:
# Create virtual serial pair
socat -d -d pty,raw,echo=0,link=/tmp/vcom0 pty,raw,echo=0,link=/tmp/vcom1
# In another terminal, start the slave on one end
./diagslave -m rtu -a 1 -b 9600 /tmp/vcom0
# Point your master at the other end: /tmp/vcom1
Baud rate, parity, data bits, and stop bits must be identical on both master and slave. If the master is set to 19200/8/E/1 and the slave is at 9600/8/N/1, you will get complete silence — no errors, no data, just timeouts.
Simulating Real Device Behaviour
A blank simulator that returns zeros on every register is useful for connectivity testing, but to actually validate your integration, you need the simulator to behave like the real device.
Mapping register addresses to match a real device
Start with the device's register map (from its datasheet). For example, the Eastron SDM630 energy meter has this layout:
| Register | Description | Type | Unit |
|---|---|---|---|
30001 | Phase 1 Line to Neutral Voltage | Float32 | V |
30007 | Phase 1 Current | Float32 | A |
30013 | Phase 1 Active Power | Float32 | W |
30071 | Frequency | Float32 | Hz |
30343 | Total Active Energy | Float32 | kWh |
Configure your simulator's input registers starting at address 0 (protocol-level) to hold these Float32 values. Remember: each Float32 occupies two consecutive 16-bit registers.
Setting realistic initial values
Don't leave everything at zero. Populate the simulator with values that match what you'd see in the field:
# Example: Set realistic values for an SDM630 simulator
Register 30001-30002: 230.5 (V) → Phase 1 voltage
Register 30007-30008: 12.3 (A) → Phase 1 current
Register 30013-30014: 2835.2 (W) → Phase 1 power
Register 30071-30072: 50.01 (Hz) → Frequency
Register 30343-30344: 15482.7 (kWh) → Total energy
Auto-changing values
Static values are fine for basic tests, but real devices have values that change over time. Good simulators let you apply value patterns:
- Sine wave — Ideal for simulating AC voltage (230V ± 5V) or temperature fluctuations. Set centre value, amplitude, and period.
- Auto-increment — Perfect for energy counters (kWh) that increase monotonically. Set the increment per second to match realistic consumption.
- Random walk — Simulates noisy sensor data like current draw or environmental sensors. Set a base value with a random deviation range.
- Step function — Toggles between two values on a timer, useful for simulating on/off states or alarm conditions.
Simulating exception responses
A good simulator doesn't just return data — it also returns errors when appropriate. Configure it to return exception codes for:
- Illegal Function (0x01) — When the master sends a function code the device doesn't support (e.g., FC05 write to a read-only device)
- Illegal Data Address (0x02) — When the master reads a register address outside the device's valid range
- Slave Device Failure (0x04) — To simulate a device in fault state
This lets you verify that your master handles errors correctly instead of crashing or displaying stale data when a device faults.
Testing Scenarios
Once your simulator is running, work through these scenarios systematically. Each one catches a different class of bug in your master implementation.
1. Normal operation
Poll all configured registers and verify the values appear correctly in your SCADA/HMI. Check that Float32 values decode with the right byte order, that units and scaling are correct, and that data updates at the expected polling interval.
2. Communication failure
Stop the simulator mid-poll and observe your master's behaviour. Does it display a timeout alarm? Does it freeze? Does it show stale data without any indication? A well-written master should flag the communication loss within one or two poll cycles and clearly mark the affected data as stale or unknown.
3. Slave address conflict
Start two simulators with the same slave ID on the same bus or TCP port. On RS485, this causes garbled responses (both devices talk simultaneously). On TCP, most implementations will connect to whichever resolves first. Verify that your master detects corrupted responses instead of silently accepting bad data.
4. Boundary testing
Read registers at the edges of the valid address range. Request register 65535, request a read of 125 registers (the max per FC03 request), or request zero registers. Your master should handle these edge cases without crashing, and the slave should return appropriate exception codes for invalid addresses.
5. Performance testing
Reduce your polling interval to 50ms or 100ms and monitor response times. How fast can your master cycle through all registers before timeouts start appearing? This is critical for systems with many devices — if you have 50 meters on a single RS485 bus at 9600 baud, polling all of them in under 5 seconds requires careful register grouping and request batching.
Some simulators can add artificial response delay (e.g., 50-200ms) to mimic real device behaviour. A local simulator responds in under 1ms, which is unrealistically fast. Adding delay catches timing bugs that only appear in the field.
Built-in Slave Simulation with ModBus Pro
No separate tool needed
ModBus Pro includes a built-in Modbus slave simulator. Configure up to 10 slave devices with configurable register spaces, auto-changing values (sine wave, random walk, auto-increment), and a live request log showing every incoming master request. Test your SCADA, PLC, or HMI against realistic virtual devices without installing anything else.
Download FreeCommon Pitfalls
1. Port 502 requires elevated privileges
On every operating system, binding to port 502 (or any port below 1024) needs admin/root permissions. If your simulator silently fails to start, check if it's running elevated. Better yet, use port 5020 for development and save 502 for production deployments.
2. Forgetting to set the correct slave ID
Many simulators default to slave ID 1, but your master may be configured to poll ID 3 or ID 10. Verify that the simulator's slave ID matches what the master expects. On Modbus TCP, the slave ID is the "Unit Identifier" in the MBAP header — it still matters, especially with gateways.
3. Register numbering: 0-based protocol vs 1-based documentation
This is the single most common source of "off by one" bugs in Modbus. The Modbus protocol uses 0-based addressing on the wire. Register 0 is the first holding register. But device documentation traditionally uses 1-based numbering with a prefix: holding register 0 is documented as "40001". When your datasheet says register 40001, you must request address 0 in the protocol frame.
If your simulator puts voltage data at address 0 but your master requests address 40001 (because that's what the datasheet says), the master is actually asking for protocol address 40001 — which is 40,000 registers past where your data is. You'll either get zeros or an exception code 02. Always check whether your tool applies the offset automatically or expects raw protocol addresses.
4. Byte order mismatch between simulator and master
Float32 values occupy two 16-bit registers, and the byte order varies between devices. The four possibilities are ABCD (Big Endian), CDAB (Big Endian word swap), BADC (Little Endian word swap), and DCBA (Little Endian). If your simulator uses ABCD but your master expects CDAB, every floating-point value will be wrong. Configure both to use the same byte order as the real device you're simulating.
5. Not testing write operations
Most testing focuses on reads. But if your system writes setpoints, control commands, or configuration values, you need to verify those too. Write a value via FC06 or FC16, then read it back with FC03 to confirm the simulator accepted it correctly. Test writing out-of-range values to verify your master gets the expected exception.