Modbus first appeared in 1979, developed by Modicon for communicating with their programmable logic controllers. By any reasonable measure of protocol longevity, it is ancient. It has no built-in security. It has no native data type beyond 16-bit integer registers. Its addressing scheme was designed for small device footprints, not enterprise data architectures. And yet, in 2025, if you walk into a continuous-process plant that was built or last significantly upgraded before 2010, you will find Modbus TCP everywhere — on remote I/O racks, standalone temperature transmitters, flow computers, gas analyzers, and peripheral control units that were never designed to speak anything else. If you want to connect a digital twin to that plant, you need to understand Modbus TCP well enough to do it right.
The Modbus Register Model (What the Standard Actually Defines)
Modbus defines four data tables per device: coils (single-bit read/write), discrete inputs (single-bit read-only), input registers (16-bit read-only, typically from sensors), and holding registers (16-bit read/write, typically setpoints and configuration). In process plant contexts, you'll mostly care about input registers (Function Code 4, Read Input Registers) and holding registers (Function Code 3, Read Holding Registers).
The addressing confusion that trips most people on first contact: Modbus has two addressing conventions that coexist in the wild. The "PLC addressing" convention uses 1-based addresses: register 1 is the first register in the table. The "protocol addressing" convention (what appears in the actual Modbus TCP packet) uses 0-based addresses: the first register in the table has protocol address 0. A device's documentation may say "read register 40001" (using PLC addressing for holding register 1) but the actual Modbus TCP request has to use address 0. When you get back garbage or zeros, this offset is the first thing to check.
Scaling and floating point: Modbus registers are 16-bit integers. Most process measurements — temperature, pressure, flow — are floating-point values. Device manufacturers handle this in three main ways: (1) scale the float to fit in a 16-bit integer with a documented scaling factor (e.g., temperature in 0.01°C, so 8860 means 88.60°C); (2) use two consecutive 16-bit registers to store a 32-bit IEEE 754 floating point value (big-endian or little-endian depending on the device); (3) use a proprietary encoding described in the device manual only. All three variants exist in the field. The only way to know which one a specific device uses is the device's Modbus register map documentation.
What a Modbus TCP Connection Looks Like in Practice
Modbus TCP wraps the classic Modbus RTU message in a TCP/IP packet. The device acts as a Modbus TCP server (historically called "slave") on TCP port 502. Your integration agent acts as the Modbus TCP client ("master"). Connection is straightforward: establish TCP connection to the device IP address on port 502, send a Modbus TCP ADU (Application Data Unit) with a transaction ID, protocol ID (always 0 for Modbus), length, unit ID (device address — usually 1 for single-device IP addresses), function code, and register address and count. Parse the response. Close connection or hold it open for subsequent polls.
Unlike OPC-UA, Modbus TCP has no subscription mechanism. You poll. The client initiates every read; the server never pushes data. This means your polling loop design determines your data freshness. For a digital twin that updates every 15 seconds, a reasonable polling approach for Modbus devices is to batch-read all relevant registers in a single Function Code 3 or 4 request per device per poll cycle (maximizing contiguous register reads to minimize transaction count), with a polling interval of 5–10 seconds for critical sensors.
The batch-read efficiency point is important: many devices have limits on the maximum number of registers returnable in a single request (often 125 or 250 registers per the Modbus specification). For a device with sensors spread across a non-contiguous register map, you may need multiple requests per poll cycle. Spreading these across the available poll window prevents any single slow device response from blocking the poll cycle for other devices.
The Integration Problems You Will Actually Encounter
Device Timeout and Connection Drop Handling
Modbus TCP devices in legacy installations were often not designed for persistent long-running connections from multiple clients. When a device drops a connection (due to a PLC scan time interrupt, a network glitch, or a device restart), your integration layer needs to reconnect gracefully without losing data continuity. A polling client that doesn't handle socket exceptions cleanly will either hang waiting for a response that never comes, or crash and lose all polling until restarted manually.
The pattern that works: per-device connection with explicit connection timeout (TCP connect timeout: 3 seconds; read timeout: 2 seconds). On any socket error, log the error with timestamp, mark the device as "connection failed," attempt reconnect on the next poll cycle with exponential backoff (retry after 5s, 15s, 60s). Forward the "connection failed" state to the twin's data quality layer so the model knows to flag affected tags as unreliable.
Stale Data Without Quality Indication
Modbus has no native data quality concept. When you successfully read a register, you get a value — but the protocol gives you no indication of whether that value is fresh (updated in the last scan cycle) or stale (frozen because the connected instrument has failed, gone into hold mode, or the PLC's scan task has hung). A flow transmitter in hold mode will return the last held value indefinitely; Modbus will report it as a clean successful read.
The mitigation requires additional engineering: for devices that support it, look for a dedicated status register or a bit in a holding register that indicates instrument health (many modern transmitters with Modbus interfaces include this). For devices that don't, implement a change-detection filter: if a measurement registers no change over a window significantly longer than its expected physical variation (a flow rate showing exactly 142.7 kg/hr for 45 consecutive minutes in a plant that's actively running — that's suspicious), flag it for investigation. This won't catch every frozen-in-nominal-range sensor, but it catches the common case of a transmitter in hold that's stuck on a round number.
Network Topology and Latency
Legacy process plants often have flat OT networks where all devices — DCS I/O modules, PLCs, remote I/O, HMIs — share the same network segment with no prioritization. A Modbus TCP poll from your integration agent competes for network bandwidth with DCS scan traffic, historian polling, operator HMI refreshes, and occasionally video surveillance or printer traffic that someone connected to the OT network in 2009 and never removed.
Network latency to Modbus devices on a well-managed OT LAN should be well under 10 milliseconds for a typical request-response cycle. If you're seeing poll response times of 100ms+ consistently, the network is congested or the device is slow to respond (some legacy PLCs have long scan cycles and respond to external Modbus requests only between PLC scan iterations). The solution to slow device response times is increasing the poll interval for that device, not tightening timeouts — you don't want to flood a slow-responding legacy PLC with retries that it can't service fast enough, which makes the problem worse.
When to Use a Modbus-to-OPC-UA Gateway
For plants with a significant number of Modbus TCP devices alongside a newer OPC-UA capable DCS, a Modbus-to-OPC-UA gateway device can simplify the integration architecture. The gateway runs Modbus TCP client polling internally, maps the register values into an OPC-UA node address space, and exposes them to OPC-UA clients including the twin's integration agent. This gives you the subscription and quality infrastructure of OPC-UA at the application layer while the gateway handles Modbus polling internally.
The tradeoff: the gateway adds a latency hop (gateway poll interval + OPC-UA subscription interval), and it requires correct configuration of the register map in the gateway's configuration (which has all the same register address and scaling challenges described above, just done once in the gateway). For a plant with 50+ Modbus devices all requiring integration, the gateway approach scales better than managing 50 direct Modbus polling connections in the twin agent. For a plant with 5 Modbus devices alongside an existing OPC-UA historian connection, direct polling from the agent may be simpler.
The right choice depends on the specific plant topology. We're not prescribing one architecture universally — the goal is clean, quality-flagged data flowing into the twin at sufficient frequency. Both approaches can achieve that if configured carefully. What never works is treating Modbus integration as a background task that can be done casually. Every assumption about register address, scaling factor, byte order, and data type needs to be verified against actual device documentation — not guessed, not inferred from another device of the same model number, and not taken on faith from a commissioning engineer's memory of what they configured ten years ago.