Skip to content

io.toml

io.toml defines which I/O backend a project uses and which outputs must be driven to a safe value on fault/watchdog handling.

Unknown fields are rejected.

Two Supported Shapes

[io]

Key Type Required Notes
driver string single-driver form Use one built-in driver name plus [io.params].
params table single-driver form Driver-specific configuration for driver.
drivers array of tables multi-driver form Compose multiple named drivers in one runtime.

Single-driver form

[io]
driver = "loopback"
params = {}

Multi-driver form

[io]
drivers = [
  { name = "modbus-tcp", params = { address = "127.0.0.1:1502", unit_id = 1, input_start = 0, output_start = 0, timeout_ms = 500, on_error = "fault" } },
  { name = "mqtt", params = { broker = "127.0.0.1:1883", topic_in = "trust/examples/in", topic_out = "trust/examples/out", reconnect_ms = 500, keep_alive_s = 5, allow_insecure_remote = false } }
]

Selection rules:

Condition Requirement
single-driver file use io.driver plus io.params
multi-driver file use io.drivers only
mixed forms invalid
empty config invalid unless you intentionally choose driver = "none"

Removing the final project driver through communication authoring retains io.toml as an explicit no-driver project configuration:

[io]
driver = "none"
params = {}
safe_state = [{ address = "%QX0.0", value = "FALSE" }]

This preserves safe-state policy and prevents the project from implicitly falling back to machine-wide system I/O. Adding a driver later replaces the none sentinel while retaining safe-state entries and project comments outside rewritten driver/safe-state items.

Built-in Driver Names

Driver Purpose
analog analog inputs and outputs in millivolts (a chip's ADC, Linux IIO)
can CAN frames to and from the process image (a chip's CAN controller, Linux SocketCAN)
counter pulse and encoder counters (a chip's counters, the Linux counter subsystem)
ethercat EtherCAT master / mock adapter workflows
gpio direct GPIO-backed I/O
loopback feed outputs back into inputs locally
modbus-tcp Modbus TCP client I/O
mqtt MQTT topic-backed I/O
pwm PWM outputs with a duty in hundredths of a percent (a chip's PWM, Linux sysfs PWM)
simulated runtime without physical hardware

Accepted aliases also include:

Alias Canonical driver
sim, noop simulated
modbus_tcp modbus-tcp
mqtt-tcp mqtt
ether-cat, ecat ethercat

Driver Patterns

simulated

[io]
driver = "simulated"
params = {}

Choose this driver when you want runtime execution without physical hardware.

loopback

[io]
driver = "loopback"
params = {}

Use this for first-project validation when you want outputs reflected back locally.

gpio

[io]
driver = "gpio"

[io.params]
backend = "sysfs"
inputs = [{ address = "%IX0.0", line = 17, debounce_ms = 5 }]
outputs = [{ address = "%QX0.0", line = 27, initial = false }]

modbus-tcp

[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"

mqtt

[io]
driver = "mqtt"

[io.params]
broker = "127.0.0.1:1883"
topic_in = "trust/examples/mqtt/in"
topic_out = "trust/examples/mqtt/out"
reconnect_ms = 500
keep_alive_s = 5
allow_insecure_remote = false

Remote plaintext brokers are rejected unless allow_insecure_remote = true is set explicitly for a test or development exception. Production remote brokers should use TLS:

[io]
driver = "mqtt"

[io.params]
broker = "mqtts://mqtt.example.test:8883"
topic_in = "trust/site-a/in"
topic_out = "trust/site-a/out"
reconnect_ms = 500
keep_alive_s = 5
tls = true
tls_ca_path = "/etc/trust/certs/mqtt-ca.pem"
tls_client_cert_path = "/etc/trust/certs/runtime-client.pem"
tls_client_key_path = "/etc/trust/private/runtime-client-key.pem"
tls_alpn = ["mqtt"]

MQTT TLS uses the broker host name as the TLS server name/SNI value, so the broker's certificate must name that host: a DNS name, or an IP address among its IP addresses. The runtime trusts only the certificates in tls_ca_path, every one the file holds, not the computer's trusted certificates, and it speaks TLS 1.2 and 1.3, the same on Windows, macOS and Linux. Over TLS 1.2 the broker must offer an ECDHE cipher suite with AES-GCM or ChaCha20-Poly1305; CBC suites and RSA key exchange are not offered.

tls_client_cert_path and tls_client_key_path must be provided together when mutual TLS is required. The certificate file holds the client's certificate in PEM, then any intermediate certificates; the key file holds its private key in PEM, as PKCS#8, PKCS#1 (RSA) or SEC1 (EC). The runtime refuses the configuration when the key does not belong to the certificate, and keeps the key in its own memory.

ethercat

[io]
driver = "ethercat"

[io.params]
adapter = "mock"
timeout_ms = 250
cycle_warn_ms = 5
on_error = "fault"
mock_inputs = ["01", "00"]

[[io.params.modules]]
model = "EK1100"
slot = 0

[[io.params.modules]]
model = "EL2008"
slot = 1
channels = 8

Use adapter = "mock" for deterministic local validation. Real adapters need the appropriate wire feature and supported host platform.

Safe-state Outputs

Safe-state entries are optional but strongly recommended:

[[io.safe_state]]
address = "%QX0.0"
value = "FALSE"

[[io.safe_state]]

Key Type Required Notes
address IEC output address yes Uses %Q... addressing.
value string yes Parsed as a typed value for the target output.

Validation Rules

Condition Requirement
io.drivers[*].name must not be empty
driver params must be a TOML table
io.safe_state[*].value must match the output type

Numeric safe-state values must fit the addressed output width exactly: BYTE is 0..255, WORD is 0..65535, DWORD is 0..4294967295, and LWORD is the full unsigned 64-bit range. Out-of-range values are rejected rather than wrapped or clamped.