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:

  1. Listens for connections — On TCP port 502 (or a serial port for RTU), it waits for incoming Modbus requests from a master.
  2. Parses standard function codes — It handles the core read functions (FC01 Read Coils, FC02 Read Discrete Inputs, FC03 Read Holding Registers, FC04 Read Input Registers) and write functions (FC05 Write Single Coil, FC06 Write Single Register, FC15 Write Multiple Coils, FC16 Write Multiple Registers).
  3. 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.
  4. 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 CodeNameData BlockOperation
FC01Read CoilsCoils (0xxxx)Read
FC02Read Discrete InputsDiscrete Inputs (1xxxx)Read
FC03Read Holding RegistersHolding Registers (4xxxx)Read
FC04Read Input RegistersInput Registers (3xxxx)Read
FC05Write Single CoilCoils (0xxxx)Write
FC06Write Single RegisterHolding Registers (4xxxx)Write
FC15Write Multiple CoilsCoils (0xxxx)Write
FC16Write Multiple RegistersHolding 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.

Port 502 requires elevation

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., COM10COM11). Run the slave on COM10, point your master at COM11.
  • 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
Match your parameters

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:

RegisterDescriptionTypeUnit
30001Phase 1 Line to Neutral VoltageFloat32V
30007Phase 1 CurrentFloat32A
30013Phase 1 Active PowerFloat32W
30071FrequencyFloat32Hz
30343Total Active EnergyFloat32kWh

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.

Test with realistic latency

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

Built-in Slave Simulator

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 Free

Common 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.

The 40001 trap

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.