Modbus TCP¶
Good fit vs bad fit¶
| Good fit | Bad fit |
|---|---|
| PLC-to-device register exchange | pub/sub event distribution |
| gateways, drives, legacy equipment | highly dynamic topic-style systems |
| explicit polling and deterministic offsets | loosely structured payloads |
First things to decide¶
- what server endpoint will the runtime talk to?
- what
unit_idis correct for the device or gateway? - where do inputs and outputs start in register space?
- should communication faults halt, warn, or degrade gracefully?
Example and commissioning guide¶
Communication Example: Modbus/TCP¶
This example shows how to wire a minimal PLC project to io.driver = "modbus-tcp".
What you learn¶
- how
%IX/%QXbits map through Modbus register space - why
unit_id,input_start, andoutput_startmust be explicit - how timeout and
on_errorpolicy affect runtime behavior
Files in this folder¶
src/main.st: simple input-to-output logic (DO0 := DI0)src/config.st: task binding plusVAR_CONFIGmapping (P1.DI0/P1.DO0)io.toml: Modbus/TCP backend profileruntime.toml: runtime defaultstrust-lsp.toml: project settings
Step 1: Build the project¶
Why: prove ST compile path is valid before protocol troubleshooting.
cd examples/communication/modbus_tcp
trust-runtime build --project . --sources src
Step 2: Inspect io.toml¶
Why: every field controls a concrete transport or mapping decision.
[io]
driver = "modbus-tcp"
[io.params]
address = "127.0.0.1:1502"
unit_id = 1
input_start = 0
output_start = 0
timeout_ms = 500
on_error = "fault"
Field intent:
address: Modbus server endpoint.unit_id: target slave/unit address.input_start: first register offset read into%Iimage.output_start: first register offset written from%Qimage.timeout_ms: upper bound per exchange.on_error = "fault": fail closed during commissioning.
Step 3: Validate configuration¶
Why: catches schema/mode errors before runtime boot.
trust-runtime validate --project .
Step 4: Run with a local test server¶
Why: isolate mapping/timeout behavior before connecting to plant hardware.
- Start a Modbus test endpoint on
127.0.0.1:1502. - Start runtime:
trust-runtime run --project .
- In another terminal, inspect image:
trust-runtime ctl --project . io-read
Step 5: Harden for production¶
Why: lab defaults often become hidden failure points in production.
- replace loopback endpoint with real server address
- keep
on_error = "fault"until commissioning sign-off - verify safe state outputs match actuator-safe values
Common mistakes¶
- using wrong
unit_idfor gateway/slave topology - shifting
input_start/output_startby one register block - setting
on_error = "warn"too early in bring-up - validating config but not testing real timeout behavior
Common Modbus gotchas¶
- wrong
unit_idbehind a gateway - off-by-one mental model around register blocks
- byte/word order mismatches on non-trivial payloads
- accepting a “validate passed” result as proof of runtime connectivity