sondahub

sondahub / Modbus TCP simulator

Modbus TCP simulator

A Modbus TCP gateway with four devices behind it that do something: a lift station that pumps down its wet well when you start a pump, a three-phase power meter, a drive that ramps to the speed you ask and trips when you rush it, and a sensor so slow your timeouts get tested.

Real Modbus TCP frames over WebSocket, or JSON. Each connection gets its own devices.

Connect

Address
wss://api.sondahub.com/modbus
Binary messages
Modbus TCP ADUs: transaction, protocol 0, length, unit, PDU
Text messages
JSON, answered in JSON
Subprotocol
modbus (optional)

Send the frame you would send to port 502 as one binary WebSocket message (several frames in one message, or one frame across two, are cut apart by their MBAP length) and the answer comes back the same way, transaction id kept. A frame whose protocol id is not 0 is dropped, as a server would.

Read holding registers 0–9 of unit 1, as bytes
→ 00 01  00 00  00 06  01  03 00 00 00 0A
← 00 01  00 00  00 17  01  03 14 01 40 00 50 01 C2 00 28 00 01 03 52 00 02 00 78 00 01 00 00

JSON, from any WebSocket client

A text message is a request in JSON — the function by code or by name, the values for a write — and the answer is decoded, with the request and response frames in hex beside it. A text message that is a frame in hex works too; help lists the units.

Requests
{"unit":1,"function":"read_input_registers","address":0,"count":22}
{"unit":1,"function":5,"address":3,"value":false}          # manual mode
{"unit":1,"function":5,"address":0,"value":true}           # start pump 1
{"unit":3,"function":16,"address":0,"values":[1,3000]}     # drive: run at 30 Hz
{"unit":2,"function":43,"code":2}                          # device identification
{"hex":"000100000006010300000002"}
An answer
{"unit":1,"function":3,"name":"Read Holding Registers","transaction":1,
 "request":"00010000000601030000000a","address":0,
 "values":[320,80,450,40,1,850,2,120,1,0],
 "response":"000100000017010314…","ms":1}

The gateway

UnitWhat answers
1Lift station LS-01 (PLC)
2Power meter PM-02
3Variable frequency drive VFD-03
4Room sensor on a radio link
0Broadcast: a write reaches units 1 to 4 and nobody answers, as the specification says. A read to unit 0 gets no answer either.
9Configured on the gateway, switched off in the field: after a second, exception 11 (gateway target device failed to respond).
255Unit 1, the way a device that ignores the unit id answers.
any otherException 10 (gateway path unavailable), at once.

Exceptions as the specification orders them: an unsupported function is 1, a bad quantity or value 3, an address outside the map 2. Multiple-register writes are checked whole — one bad value and nothing is written.

Function codes

CodeFunctionNotes
1 0x01Read Coils
read_coils
Up to 2,000 coils from a start address.
2 0x02Read Discrete Inputs
read_discrete_inputs
Up to 2,000 inputs.
3 0x03Read Holding Registers
read_holding_registers
Up to 125 registers.
4 0x04Read Input Registers
read_input_registers
Up to 125 registers.
5 0x05Write Single Coil
write_single_coil
FF00 for on, 0000 for off; anything else is exception 3.
6 0x06Write Single Register
write_single_register
One register; the answer echoes the request.
15 0x0FWrite Multiple Coils
write_multiple_coils
Up to 1,968 coils, packed eight to a byte, low bit first.
16 0x10Write Multiple Registers
write_multiple_registers
Up to 123 registers, checked all together: one bad value and nothing is written.
17 0x11Report Server ID
report_server_id
The device id, the run indicator and the model.
22 0x16Mask Write Register
mask_write_register
(current AND and_mask) OR (or_mask AND NOT and_mask).
23 0x17Read/Write Multiple Registers
read_write_multiple_registers
The write first, then the read, in one transaction.
43 0x2BRead Device Identification
read_device_identification
MEI type 14: basic, regular, extended or one object.

Unit 1: Lift station LS-01 (PLC)

Three pumps over a 2 m² wet well. In auto the PLC pumps down between the start and stop levels and alternates the lead pump; in manual you start and stop them. Unit 255 reaches it too. Functions 1, 2, 3, 4, 5, 6, 15, 16, 17, 22, 23, 43.

Coils (0x)

AddressNameNotes
0–2Pump 1–3 run commandread/write
bit
In auto the PLC writes these itself on every scan; switch coil 3 off to drive the pumps by hand.
3Auto moderead/write
bit
1: the PLC starts the lead pump at the start level, a lag pump at the high alarm level, stops them at the stop level and alternates the lead. 0: manual.
4Alarm resetread/write
bit
Write 1: latched alarms that are no longer true clear, and the coil drops back to 0.
5Inlet valve openread/write
bit
Closed: nothing flows in.
6Outlet valve openread/write
bit
Closed: running pumps deadhead — pressure up, flow and current down.
7Dosing pump enableread/write
bit
8–63Freeread/write
bit
Plain memory for your own tests.

Discrete inputs (1x)

AddressNameNotes
0–2Pump 1–3 running
bit
Feedback, not the command: a pump the dry-run protection stopped shows 0 here with its run coil still 1.
6High level float
bit
Level at or above 470 cm.
7Low level float
bit
Level at or below 30 cm.
8Door open
bit
The kiosk door opens for 45 seconds every 1,000 (and latches the intrusion alarm).
9Mains power OK
bit

Input registers (3x)

AddressNameNotes
0Wet well level
uint16, cm
0–500. Rises with the inflow, falls while pumps run.
1Inflow
uint16, L/s × 10
2Outflow
uint16, L/s × 10
45 L/s per running pump at full speed.
3–5Pump 1–3 motor current
uint16, A × 10
6Discharge pressure
uint16, kPa
7–9Pump 1–3 starts
uint16
Counts every start, wraps at 65,535.
10–11Total pumped volume
uint32, m³, high word first
12–13Wet well level
float32, m, ABCD
The level again, as an IEEE 754 float.
14–15Discharge pressure
float32, bar, ABCD
16Water temperature
int16, °C × 10
17Ambient temperature
int16, °C × 10
Goes below zero in the small hours: a signed register, two’s complement.
18–19PLC uptime
uint32, s
20Alarm word
bits
Latched until reset: bit 0 high level, 1 low level, 5 intrusion, 7 overflow.
21Status word
bits
Bit 0 auto, 1–3 pumps running, 4 inlet open, 5 outlet open, 6 dosing, 7 an alarm is active.
30–31Test float, ABCD
float32
123.456, big-endian (0x42F6 0xE979).
32–33Test float, CDAB
float32
Words swapped — the order many PLCs use.
34–35Test float, BADC
float32
Bytes swapped inside each word.
36–37Test float, DCBA
float32
Little-endian.
38–39Test int32
int32, ABCD
-123456.
40–43Test float64
float64, ABCDEFGH
123.456.
44–45Test uint32
uint32, ABCD
3000000000 — wrong if read as signed.
46–49Test int64
int64, ABCDEFGH
-1234567890123.
50–59Station name
ASCII, 2 per register, high byte first
“LS-01 LIFT STATION”.

Holding registers (4x)

AddressNameNotes
0Start levelread/write
uint16, cm
Default 320; 0–500, else exception 3.
1Stop levelread/write
uint16, cm
Default 80.
2High level alarmread/write
uint16, cm
Default 450; a lag pump starts here.
3Low level alarmread/write
uint16, cm
Default 40; every pump stops here, auto or manual, and stays stopped until the level is 20 cm above it (dry-run protection).
4Lead pumpread/write
uint16, 1–3
Alternates by itself after each pump-down.
5Pump speedread/write
uint16, % × 10
Default 850 (85 %); 0–1000. Flow, current and pressure follow.
6Most pumps at onceread/write
uint16, 1–3
Default 2.
7Dosing rateread/write
uint16, mL/min
0–5000.
8Station numberread/write
uint16
10–15PLC clockread/write
year, month, day, hour, minute, second (UTC)
Write it and it keeps your time from then on.
16–199Freeread/write
uint16
Plain memory for your own tests (9 too).

Unit 2: Power meter PM-02

A three-phase meter on the main incomer: twenty float32 measurements, read with function 3 or 4, and a configuration block at 1000. Functions 3, 4, 6, 16, 17, 43.

Input registers (3x)

AddressNameNotes
0–1Voltage L1–N
float32, V, ABCD
2–3Voltage L2–N
float32, V, ABCD
4–5Voltage L3–N
float32, V, ABCD
6–7Voltage L1–L2
float32, V, ABCD
8–9Voltage L2–L3
float32, V, ABCD
10–11Voltage L3–L1
float32, V, ABCD
12–13Current L1
float32, A, ABCD
14–15Current L2
float32, A, ABCD
16–17Current L3
float32, A, ABCD
18–19Current N
float32, A, ABCD
20–21Active power
float32, kW, ABCD
22–23Reactive power
float32, kvar, ABCD
24–25Apparent power
float32, kVA, ABCD
26–27Power factor
float32, ABCD
28–29Frequency
float32, Hz, ABCD
30–31Active energy import
float32, kWh, ABCD
Climbs for as long as the hub runs.
32–33Reactive energy
float32, kvarh, ABCD
34–35THD voltage
float32, %, ABCD
36–37THD current
float32, %, ABCD
38–39Maximum demand today
float32, kW, ABCD
100–109Serial number
ASCII
“SH-PM-000231”.
110Firmware
uint16, BCD-ish
0x0213: version 2.19.

Holding registers (4x)

AddressNameNotes
0–127The same measurements
as above
Function 3 reads the same map as function 4, as many meters allow; a write here is exception 2.
1000CT primaryread/write
uint16, A
Default 200; 5–9999. Set it wrong and every current, power and energy reading scales with it — as on a real meter.
1001VT ratioread/write
uint16
Default 1; 1–1000.
1002Demand intervalread/write
uint16, min
Default 15; 1–60.
1003Wiringread/write
uint16
0 three-phase four-wire, 1 three-wire, 2 single-phase.
1004–1009Freeread/write
uint16

Unit 3: Variable frequency drive VFD-03

Pump 1’s drive: a control word, a speed reference and ramps that take the time you set — and an overcurrent trip if you ask for the impossible. Functions 3, 4, 6, 16, 17, 22, 43.

Input registers (3x)

AddressNameNotes
0Status word
bits
Bit 0 ready, 1 running, 2 at speed, 3 fault, 4 reverse, 5 remote.
1Output frequency
uint16, Hz × 100
Ramps toward the reference at the times you set.
2Motor current
uint16, A × 10
3Motor speed
uint16, rpm
4DC bus voltage
uint16, V
5Output power
uint16, kW × 10
6Heatsink temperature
uint16, °C
7Fault code
uint16
0 none, 1 overcurrent. Clear it with bit 2 of the control word.
8Run hours
uint16, h

Holding registers (4x)

AddressNameNotes
0Control wordread/write
bits
Bit 0 run, bit 1 reverse, bit 2 fault reset. Starts running.
1Speed referenceread/write
uint16, Hz × 100
Default 4500; above the maximum frequency is exception 3.
2Acceleration timeread/write
uint16, s × 10
From 0 to the maximum. Start from standstill with 1.0 s or less and the drive trips on overcurrent.
3Deceleration timeread/write
uint16, s × 10
4Maximum frequencyread/write
uint16, Hz × 100
Default 5000; 1000–6000.
5–9Freeread/write
uint16

Unit 4: Room sensor on a radio link

A battery sensor behind a slow link: every answer takes 0.6 to 1.8 seconds, for testing timeouts and transactions that overlap. Functions 3, 4, 6, 16, 17, 43.

Input registers (3x)

AddressNameNotes
0Temperature
int16, °C × 10
1Relative humidity
uint16, % × 10
2CO₂
uint16, ppm
3Air pressure
uint16, hPa × 10
4Battery
uint16, %
5Radio signal
int16, dBm
Negative: signed.

Holding registers (4x)

AddressNameNotes
0Report intervalread/write
uint16, s
10–3600.
1–9Freeread/write
uint16

From a tool that only speaks TCP

Modbus Poll, pymodbus, libmodbus, a PLC’s own library — they open a TCP connection to port 502. The bridge is a small Node script that runs on your computer, listens on the usual TCP port and carries each message to the hub over WebSocket — nothing else happens in it, and nothing is installed:

Run the bridge
curl -O https://sondahub.com/industrial/bridge.mjs
node bridge.mjs modbus

# then point the tool at localhost, port 5020

Questions

Can I use Modbus Poll, pymodbus or a PLC library?

Through the bridge: it listens on localhost port 5020 and carries each Modbus TCP frame to the gateway over WebSocket. Point the tool at 127.0.0.1:5020 and use the unit ids below. Port 502 needs administrator rights on most systems, so the bridge uses 5020 unless you give it another port.

Why does unit 0 never answer?

Because a broadcast never does: the specification has no reply to unit 0. Writes to unit 0 reach every device behind the gateway; reads to it go unanswered, and a client should time out — which is the point of trying it.

How are 32-bit values laid out?

High word first (ABCD) everywhere on the devices. Input registers 30 to 49 of unit 1 hold the same reference values in every order a device might use — float32 in ABCD, CDAB, BADC and DCBA, int32, uint32, float64 and int64 — so a client’s word and byte order settings can be checked against known numbers.