Modbus¶
The panel is a Modbus master: it cyclically reads — and writes — the registers of connected devices into the tag table. Two transports are supported, and both can run at once.
| Transport | Physical layer | When to use it |
|---|---|---|
| RTU | The board's RS485 terminals | I/O modules, inverters, sensors on a cable |
| TCP | Ethernet or Wi-Fi | Meters with a LAN module, RTU↔TCP gateways, anything in the cabinet with a network port |
The panel can also be a slave, so a supervisory system can read from it. That is further down the page.
A tag with a Modbus source¶
Every Modbus tag carries: unit (the device address), fc (coil, discrete,
holding or input), address (0-based — not the 3xxxx/4xxxx notation),
word_order (the order of the two words in a 32-bit value) and optionally
transport: tcp with ip and port.
- Without
transport— or withtransport: rtu— the tag goes over RS485. f32andu32tags are read from two registers. The word order differs from one manufacturer to the next and is the most common source of nonsense values. Importing from the catalogue sets it correctly for you.- Scaling is
value = raw × gain + offset. Analogue tags need a deadband: without one, a noisy value redraws the screen for no reason.
tags:
- name: voltage_l1
datatype: f32
source: { modbus: { transport: tcp, ip: 192.168.0.50, port: 502,
unit: 1, fc: input, address: 0, word_order: big } }
Limits and behaviour¶
| Limit | Value | Why |
|---|---|---|
| Distinct TCP targets (IP:port) per project | 4 | roughly 6–8 kB of RAM per connection on an ESP32-S3 |
| Tags in total | 512 | product limit |
- Tags pointing at the same IP:port share one connection — twenty tags from
one meter is still one target. Different
unitvalues on the same IP (an RTU↔TCP gateway with several devices behind it) are also one target. - The editor enforces the limit both on import and at compile time. A project over the limit is rejected on your desk, not discovered as a stuttering panel on a machine.
- When a target drops out, tags hold their last value, one event goes into the diagnostic buffer on the way down and one on the way back. The panel reconnects by itself with a one-second backoff, and polling of the other targets continues.
- The poller runs outside the PLC cycle, so a slow or unreachable Modbus device can never extend the logic cycle.
The device catalogue¶
Think of it as an EDS file for Modbus: the directory devices/modbus/ holds one
file per device — manufacturer, model, datasheet, word_order — plus the
register map: address, function code, data type, scale, unit and description.
The catalogue is curated by inovaea and signed like every other catalogue.
In the editor: open the Modbus RTU master or Modbus TCP master page →
Add Modbus device… → pick from the catalogue → tick the registers you want →
choose the transport (RS485, or TCP with an IP address) → Import tags. The
tags arrive prefixed with the model name (sdm630_voltage_l1), with the right
data type, word order and scaling.
The first entries are the Eastron SDM120 (single-phase meter) and SDM630 (three-phase meter).
A device that is not in the catalogue¶
You do not have to write the YAML by hand:
- Add Modbus device… → in the right-hand pane, with nothing selected, fill in
vendor and model → New device. This creates
devices/modbus/<vendor>_<model>.yamlwith one example register. - In the device window → the Registers tab → turn on Edit register map.
The row under the table adds registers (name, address, class, data type,
unit); the
xon a row deletes one. - Save to catalog writes the map back to the
.yamlfile.
The file is the single source of truth — the project only stores a path to it, so the entry exists on disk immediately rather than when the project is saved.
| Rule | Why |
|---|---|
File named <vendor>_<model>.yaml, lower-case ASCII |
the catalogue is version-controlled and has to be readable on every operating system |
uid: 0x0000 on customer entries |
the numbered space belongs to the curator, otherwise identifiers collide |
| One register per line | the parser reads line by line; a flow map wrapped onto two lines loses everything after the first |
| Renaming a model creates a new file | the old one may be in use in another project, so it is never deleted |
The manual route is still there: copy devices/modbus/_template.yaml and fill in
the register map from the device manual.
Modbus slave — the panel answers¶
The panel is not only a master. It can be a slave, so a supervisory SCADA system, a PLC or Home Assistant can read its state and send setpoints. The panel still runs completely without them — the supervisory system is a window out, not a condition of running.
modbus_slave:
rtu: false # answer on the RS485 line
tcp: true # answer over TCP
unit: 9 # the panel's unit ID
port: 502
word_order: big # word order of 32-bit values
tags:
- { tag: boiler_temp } # 4x 0-1, read only
- { tag: setpoint_temp, writable: true } # 4x 2-3
- { tag: pump } # 0x 0, bit
In the editor: RS485 → Modbus RTU slave, or Ethernet → Modbus TCP slave. The page shows exactly the map the integrator will read — both the protocol address and the number as it appears in a manual.
How addresses are assigned¶
Automatically, in list order: bool tags become coils (0x) from 0, everything
else becomes holding registers (4x) from 0, where i32, u32 and f32 occupy
two registers each. The address is written into the package explicitly,
so reordering the tags in the editor does not shift the map. The order can be
changed with the arrow buttons — it is part of the configuration, not cosmetics.
A write is a command¶
A tag without writable answers a write with exception 0x02; it never
silently succeeds. The same rule as for WebSocket and MQTT: what may be written
is decided by the panel, not by whoever is asking.
Other deliberate decisions:
FC6(write single register) against a 32-bit value is refused — half a value written is worse than a refusal.FC16validates the whole range before writing anything. A half-executed write would leave the machine in an undefined state.- Reading a range that lies even partly outside the map returns an exception. Zeros would send the integrator looking for a fault at their end.
- A broadcast (address 0) on RTU is executed but not answered, as the standard requires; otherwise two slaves would transmit at once.
Traps¶
| Trap | What to do |
|---|---|
| Master and slave on the same RS485 line | a line has one master — the build refuses it and the editor shows it in red on the page |
| Wrong word order | word_order has to match what the SCADA system expects — the same trap as on the master side |
| Several SCADA clients at once | the TCP slave serves one connection; each additional one would cost 6–8 kB of RAM the S3 does not have spare |
Diagnostics: the serial command VUI! prints mbslv=<requests>,<exceptions>.
A rising exception count means the SCADA system is reading outside the map or
trying to write a tag it has no right to.
Common traps¶
| Trap | What to do |
|---|---|
Nonsense f32 values |
swap word_order (big ↔ little) |
| Address off by one | the manufacturer's documentation counts from 1, or uses the 3xxxx notation — here the address is always the 0-based protocol address |
| Device does not answer over RTU | check baud rate, parity, the A/B wires and the line termination |
| TCP target behind NAT or on another segment | the panel and the device belong on the same isolated machine network |