Modbus TCP

Modbus registers and coils over TCP/IP, with typed addressing, byte-order control and a bounded network scan.

View as Markdown

The Modbus TCP driver reads and writes Modbus registers and coils over TCP/IP — PLCs, meters, gateways and any server speaking Modbus/TCP. Addressing is fully typed: you pick a register class and a 0-based register number, and the app assembles the wire request; there is no free-text address to get wrong.

Device

Field Meaning Default
Host DNS name, IPv4 or IPv6 address of the Modbus server or gateway. localhost when left empty
Port TCP port. 502 (a configured 0 falls back to 502)
Unit ID The Modbus unit (slave) id carried in every request — what a gateway uses to route to the device behind it. 0 to 255. 1
Minimum transaction interval (ms) Compatibility pacing: the minimum silence after one transaction completes before the next starts, 0 to 1000. Zero adds no artificial delay. 0
Poll interval (ms) Default polling rate for tags that do not override it. 1000

The Base URI shown in the panel header is modbus+tcp://host:port/unitID, plus ?minimumTransactionIntervalMs=n when the interval is set.

Devices that share a host and port also share one transaction gate: requests to the same endpoint never overlap, and the largest configured minimum transaction interval among the enabled devices on that endpoint paces all of them. The panel says when another device raises the effective interval above the one configured here. The interval governs only traffic this application emits — it is a compatibility aid for a slow server or gateway, not a repair for network faults.

Connecting waits at most 5 seconds; a response is waited on for at most 10 seconds; there are no protocol-level retries. The background retry loop owns reconnection, so a device that is off costs nothing but a bad-quality reading.

Tag addressing

Field Meaning Default
Register class Which Modbus entity the tag addresses: HoldingRegister, InputRegister, Coil or DiscreteInput. Derived from type and access: Boolean tags get DiscreteInput (read-only) or Coil (writable); every other type gets InputRegister (read-only) or HoldingRegister (writable).
Register number (0-based) The raw protocol address. This is the 0-based address on the wire, not the classic 4xxxx notation: holding register "40001" is register number 0. The register travels the wire as a 16-bit number, so the field takes 0 to 65535 and refuses anything wider where it is typed. 0
Quantity (registers, blank = auto) How many 16-bit registers the value occupies. The box is drawn on String tags only: every other type has a fixed width, so there is nothing to override. By type: Boolean 1, Int32 and Float 2, String 4
Most significant byte first Which byte of each 16-bit register comes first on the wire. Numeric tags only. on (what the Modbus specification asks for)
Most significant word first Which register of a two-register value carries the high half. Numeric tags only. on

The Source card shows the assembled wire address (for example 100?quantity=2) as a read-only preview, so what the panel shows is exactly what the request carries.

Value types and quantities

Four value types are offered, and the protocol width of each is fixed:

Data type Registers Wire form
Boolean 1 On a register class: any non-zero register reads true, and a write sends 1 or 0. On Coil/DiscreteInput: the bit itself.
Int32 2 32-bit signed integer across a register pair.
Float 2 IEEE 754 single precision across a register pair.
String 1 or more (default 4) UTF-8 bytes packed two per register. A read stops at the first NUL byte; a write must fit in the declared quantity (2 bytes per register) and is zero-padded.

A String tag is read and written in the order the device holds the bytes, and a write always sends UTF-8. Bytes that are not valid UTF-8, which is what a range the device never initialised or text written in another encoding looks like, read as the Unicode replacement character rather than failing: the tag shows something visibly wrong, and every other tag on that device keeps reading.

Quantity is asked on String tags only. Boolean, Int32 and Float widths are fixed, so the box is not drawn on those points, neither in the tag panel nor in the tag grid; the override exists to size String tags.

Byte and word order

The two order boxes are drawn on numeric tags. They are independent questions: which byte leads inside each register, and which register leads across a pair. Modbus defines neither for text, so a String tag is not offered them: its bytes travel in the order the device holds them. For the value 0x01020304, with A the most significant byte, the four layouts are:

Most significant byte first Most significant word first Wire bytes
on on AB CD (the specification default)
on off CD AB (the classic word swap)
off on BA DC
off off DC BA

Reads and writes apply the same two answers in the same order, so a write lays down exactly the bytes a read would have taken back.

Function codes

Register class Read Write
HoldingRegister 3 (Read Holding Registers) 6 (single register) or 16 (multiple registers)
InputRegister 4 (Read Input Registers) read-only; a write is refused
Coil 1 (Read Coils) 5 (Force Single Coil)
DiscreteInput 2 (Read Discrete Inputs) read-only; a write is refused

A write returns success only when the server's protocol response confirms acceptance; the cached value advances only then.

Discovery

The scan is explicit and bounded — the app never infers or sweeps your local subnet. The scope form takes:

Field Meaning Default
IPv4 host or CIDR One address (192.168.1.10) or a subnet no broader than /24 (192.168.1.0/24). Network and broadcast addresses are skipped. required
Port The port to probe. 502
Unit IDs Values, comma-separated lists or ascending ranges: 1, 1,5,9, 1, 3, 8-16 (0 to 255). 1
Minimum transaction interval (ms) Pacing between completed probes on the same host, 0 to 1000. 0

A single scan may send at most 256 probes (hosts × unit ids); a larger plan is refused before any socket opens. The only request sent is Read Basic Device Identification (function 43/14) — discovery never reads registers or guesses a register map. A fully valid Modbus exception response proves a unit is present without claiming its identity; TCP reachability, timeouts and malformed frames do not. Up to four hosts are scanned in parallel, sequentially within each host, with 750 ms connect and probe timeouts.

Results carry the complete endpoint and a suggested name built from the vendor and product code when the device reports them. Candidates have zero tags — registers are configured after Add. A unit that is already configured is marked as such and offers Open instead of creating a duplicate.

Limits worth knowing

  • Wide integer types (Int64, unsigned widths) and Double are not offered: the wire codec round-trips exactly the four types above.
  • One request is outstanding per endpoint at a time; many devices behind one gateway share that serialization by design.
  • Writes address one coil or one register run at a time; there is no multi-coil write.