Skip to main content

Protocol Bridging in IIoT: Translating Between Modbus, EtherNet/IP, and MQTT at the Edge [2026]

· 14 min read

Every manufacturing plant is a polyglot. Modbus RTU on the serial bus. Modbus TCP on the local network. EtherNet/IP talking to Allen-Bradley PLCs. And now someone wants all of that data in the cloud via MQTT.

Protocol bridging at the edge is the unglamorous but critical work that makes IIoT actually function. Get it right, and you have a seamless data pipeline from a 20-year-old Modbus RTU device to a modern cloud analytics platform. Get it wrong, and you have data gaps, crashed connections, and a plant floor that's lost trust in your "smart factory" initiative.

This guide covers the architecture, pitfalls, and hard-won lessons from building protocol bridges that run in production — not just in proof-of-concepts.

The Protocol Tower: What You're Actually Bridging

Understanding what needs translating requires seeing the full stack:

┌─────────────────────────────────────────────┐
│ CLOUD LAYER │
│ MQTT Broker → Time Series DB → Analytics │
├─────────────────────────────────────────────┤
│ EDGE GATEWAY │
│ Protocol Translation + Batching + Buffering │
├──────────┬──────────┬────────────────────────┤
│ Modbus │ Modbus │ EtherNet/IP │
│ RTU │ TCP │ (CIP) │
│ RS-485 │ TCP/502 │ TCP/44818 │
├──────────┴──────────┴────────────────────────┤
│ PHYSICAL DEVICES │
│ PLCs, VFDs, Sensors, Meters, Controllers │
└─────────────────────────────────────────────┘

The edge gateway must speak three very different languages simultaneously:

Modbus RTU — Serial protocol. Half-duplex. One master, multiple slaves on a bus. Data is register-based with strict addressing conventions. Timing-sensitive (baud rate, byte timeouts, response timeouts).

Modbus TCP — Same register model as RTU, but wrapped in TCP/IP. Eliminates serial timing constraints but introduces TCP connection management.

EtherNet/IP — Higher-level protocol using CIP objects and symbolic tag names. Supports both request/response (explicit) and scheduled I/O (implicit) messaging.

MQTT — Publish/subscribe transport. Topic-based routing. QoS levels (0, 1, 2). Designed for unreliable networks. The cloud-side lingua franca.

The gateway sits in the middle, continuously polling southbound protocols and publishing northbound to MQTT.

Modbus Bridging: The Details That Matter

Address Space Translation

Modbus organizes data into four register types, distinguished by their address ranges:

Address RangeRegister TypeAccessFunction Code
000001-065535Coils (discrete output)Read/WriteFC01 (read), FC05/15 (write)
100001-165535Discrete InputsRead OnlyFC02
300001-365535Input Registers (16-bit)Read OnlyFC04
400001-465535Holding Registers (16-bit)Read/WriteFC03 (read), FC06/16 (write)

The leading digit determines the function code used on the wire. A gateway must correctly parse the configured address, extract the register type from the leading digit, compute the actual wire address (subtract the base), and issue the right function code.

For example, address 300201:

  • Leading 3 → Input Register → Function Code 04
  • Wire address → 300201 - 300001 = 200
  • The gateway issues: FC04, start=200, count=1

And address 400006:

  • Leading 4 → Holding Register → Function Code 03
  • Wire address → 400006 - 400001 = 5
  • The gateway issues: FC03, start=5, count=1

Coils use a similar pattern but with single-bit addressing:

  • Address 5 (no prefix or 0 prefix) → Coil → Function Code 01
  • Address 100009 → Discrete Input → Function Code 02

Common pitfall: Off-by-one errors in address translation. Some PLCs and documentation use 0-based addressing, others use 1-based. Verify empirically by reading a known register value.

RTU vs TCP: More Than Just Transport

While Modbus RTU and TCP share the same register model, bridging them requires very different code paths.

Modbus RTU (Serial):

Configuration:
Port: /dev/rs485 (or /dev/ttyUSB0)
Slave address: 48
Baud rate: 9600
Parity: Even
Data bits: 8
Stop bits: 1
Byte timeout: 4ms
Response timeout: 100ms

RTU is unforgiving. The byte timeout (inter-character delay) determines when a frame ends — exceed 1.5 character times and the slave treats it as a frame boundary. The response timeout is how long you wait for the slave to respond before declaring a communication failure.

These timings depend on baud rate:

  • At 9600 baud, one character ≈ 1.04ms (11 bits per character)
  • 1.5 character timeout ≈ 1.56ms
  • 3.5 character timeout (frame delimiter) ≈ 3.65ms

Set your byte timeout too low and you'll get fragmented frames. Set it too high and your polling loop crawls.

Modbus TCP:

Configuration:
IP address: 192.168.5.5
Port: 502
Connection: TCP (persistent, reusable)

TCP eliminates the timing sensitivity of RTU but introduces its own issues:

  • Connection management — TCP sockets can go stale. Implement keepalive or application-level heartbeats.
  • Multiple concurrent connections — Unlike RTU's single-master bus, TCP allows multiple clients. But PLCs have connection limits (often 4-8 concurrent TCP connections).
  • No slave address in header — Modbus TCP uses a Unit Identifier field (usually 0 or 1) instead of the RTU slave address.

Optimizing Register Reads

The biggest performance gain in Modbus bridging is read coalescing. Instead of reading registers one at a time, batch contiguous registers into a single request.

Naive approach (don't do this):

Read FC04, address 0, count 1    → 1 request
Read FC04, address 1, count 1 → 1 request
Read FC04, address 2, count 1 → 1 request
...
Read FC04, address 160, count 1 → 161 requests!

Optimized approach:

Read FC04, address 0, count 125  → 1 request (Modbus max per FC04)
Read FC04, address 125, count 36 → 1 request
Total: 2 requests instead of 161

Modbus limits reads to 125 registers (FC03/04) or 2000 coils (FC01/02) per request. A smart gateway groups tags by register type and address proximity, then issues the minimum number of requests to cover all configured tags.

But beware: if you have tags at addresses 1 and 5000, a single read of 5000 registers is wasteful — you'd read 4998 registers you don't need. The optimization must consider address gaps and split reads when the gap exceeds a threshold.

Data Type Handling

Modbus natively deals in 16-bit registers. But real-world values come in many sizes:

Data TypeRegistersByte Order Concern
bool1 coil or 1 register (bit extraction)None
int16 / uint161 registerNone
int32 / uint322 registersWord order matters!
float322 registersWord AND byte order matter!

The 32-bit types are where things get treacherous. Two consecutive 16-bit registers combine to form a 32-bit value, but which register is the high word?

Big-endian (AB CD):    Register N = high word, Register N+1 = low word
Little-endian (CD AB): Register N = low word, Register N+1 = high word
Mid-big (BA DC): Bytes swapped within each word
Mid-little (DC BA): Both byte and word order reversed

There is no standard. Different PLC manufacturers use different byte ordering. The only way to know is to read a known value and verify. A temperature of 72.5°F stored as a float32:

  • Big-endian: 0x4291 0x0000
  • Little-endian: 0x0000 0x4291
  • Mid-big: 0x9142 0x0000

Your edge gateway must support configurable byte ordering per device or per tag. Getting this wrong produces values that look plausible but are completely incorrect — a particularly dangerous failure mode.

EtherNet/IP Bridging

EtherNet/IP uses a fundamentally different paradigm from Modbus. Instead of numbered registers, you address data by symbolic tag names that map to memory in the PLC.

Key Differences from Modbus

AspectModbusEtherNet/IP
AddressingNumeric registers (30001, 40001)Symbolic tags ("Tank_Temperature")
Data types16-bit registers, manual assemblyNative types (BOOL, INT, DINT, REAL)
ArraysContiguous register blocksFirst-class array support with indices
DiscoveryNone (you need the register map)Tag browsing possible
ConnectionStateless (TCP) or bus (RTU)Stateful CIP connections

The gateway needs to construct CIP request paths that specify the protocol, gateway IP, CPU type, tag name, element size, and element count. Each parameter affects how the PLC processes the request.

CPU Type Matters

Different Allen-Bradley controller families require different CIP parameters:

  • Micro800 — Uses symbolic tags with cpu=micro800
  • CompactLogix / ControlLogix — Uses cpu=lgx with different session handling
  • MicroLogix — Uses cpu=mlgx with limited tag access

Using the wrong CPU type will result in connection timeouts or malformed responses. Auto-detection is possible by attempting a known read (like device type) with each CPU type and seeing which succeeds.

The MQTT Northbound: Getting Data to the Cloud

The southbound protocols (Modbus, EtherNet/IP) get data from machines. MQTT pushes it north to the cloud. But the bridge between them involves critical design decisions.

Topic Structure

A well-designed MQTT topic hierarchy enables both targeted subscriptions and broad analytics:

machinecdn/{serial_number}/telemetry    → Batched process data
machinecdn/{serial_number}/alarms → Immediate alarm notifications
machinecdn/{serial_number}/status → Gateway health and connectivity
machinecdn/{serial_number}/config → Configuration commands (subscribe)

The device serial number as a topic segment enables per-machine data routing and access control.

QoS Selection

MQTT QoS levels for industrial data:

  • QoS 0 (at most once): Fire and forget. Fine for high-frequency telemetry where missing one data point isn't critical. Lowest overhead.
  • QoS 1 (at least once): Guaranteed delivery, may duplicate. The standard choice for industrial data — you'd rather see a duplicate reading than lose one.
  • QoS 2 (exactly once): No duplicates, no losses. Highest overhead. Rarely needed for telemetry — the 4-packet handshake per message is excessive for time-series data.

For most IIoT deployments, QoS 1 is the sweet spot. Combine it with idempotent message processing on the server side (deduplication by timestamp + device serial + tag ID) and you get reliable delivery without the QoS 2 overhead.

Connection Management

MQTT connections over cellular or unreliable networks need careful handling:

  1. Clean session = false — Allows the broker to queue messages during disconnection
  2. Last Will and Testament (LWT) — Automatically publishes an offline status if the gateway disconnects unexpectedly
  3. Keep-alive interval — Typically 60-120 seconds for cellular connections
  4. Automatic reconnection — With exponential backoff (1s, 2s, 4s, 8s... capped at 60s)

The gateway must decouple MQTT publishing from PLC polling. If the MQTT connection drops, PLC polling continues and data accumulates in a local buffer.

Store-and-Forward: The Non-Negotiable Pattern

This is the single most important architectural pattern in protocol bridging. Without it, any network disruption means data loss.

How It Works

PLC Polling Loop:
Read tags → Batch data → Add to outgoing buffer

MQTT Publishing Loop (separate thread):
If connected:
Read from buffer → Publish → On ACK, advance read pointer
If disconnected:
Data accumulates in buffer → Reconnect with backoff
On reconnect:
Flush buffer (oldest data first) → Resume real-time publishing

The buffer sits between the polling loop and the MQTT client, acting as a shock absorber for network disruptions.

Buffer Design

A production-grade buffer needs:

  • Page-based allocation — Fixed-size pages prevent memory fragmentation on embedded systems
  • Thread-safe access — Polling and publishing run in separate threads
  • Overflow handling — When the buffer is full, either drop oldest data or stop polling (configurable)
  • Delivery confirmation — Only free buffer space after MQTT PUBACK received

A 500KB buffer with 4KB pages gives 125 pages of storage. With binary-format batches, each page can hold approximately 200-400 data points. That's 25,000-50,000 data points of buffer capacity — enough for several hours of typical machine monitoring during a network outage.

Buffer Page Lifecycle

[FREE] → [WRITING] → [QUEUED] → [SENDING] → [DELIVERED] → [FREE]

[SEND FAILED] → [QUEUED] (retry)

The key insight: a page moves from SENDING to DELIVERED only when the MQTT broker acknowledges receipt (PUBACK for QoS 1). If the connection drops during transmission, the page reverts to QUEUED and will be retransmitted after reconnection.

Multi-Protocol Gateway Architecture

A production gateway typically manages multiple devices simultaneously — some on Modbus RTU, some on Modbus TCP, some on EtherNet/IP. The architecture needs to handle this cleanly.

Device Abstraction

Each device gets its own configuration context:

Gateway
├── Device 1: Central Chiller
│ ├── Protocol: Modbus TCP
│ ├── IP: 192.168.5.5:502
│ ├── Tags: 230 (process vars + alarms)
│ ├── Batch size: 4000 bytes
│ └── Batch timeout: 60 seconds

├── Device 2: Batch Blender
│ ├── Protocol: EtherNet/IP
│ ├── IP: 192.168.5.10:44818
│ ├── Tags: 47 (with dependents)
│ ├── Batch size: 4000 bytes
│ └── Batch timeout: 60 seconds

└── Device 3: TCU (Temperature Control)
├── Protocol: Modbus RTU
├── Port: /dev/rs485
├── Slave address: 48
└── Tags: 25

Each device maintains its own:

  • Connection state (connected/disconnected)
  • Tag list and polling intervals
  • Data batch (accumulating readings)
  • Last-known values (for change detection)

But they share a single outgoing buffer and MQTT connection, reducing cloud-side connection count.

Auto-Detection

Some gateways can automatically detect what's connected:

  1. Try EtherNet/IP first — Attempt to read a device type tag via CIP
  2. Fall back to Modbus TCP — Try reading input register 800 (common device type register)
  3. Check Modbus RTU — If serial port is configured, scan slave addresses

Once the protocol and device type are identified, the gateway can load the appropriate tag configuration automatically.

Alarm Handling: Special Considerations

Alarms deserve special treatment in protocol bridging. A temperature reading arriving 5 seconds late is fine. An alarm arriving 5 seconds late could mean a missed emergency response.

Immediate Delivery Pattern

For alarm-critical tags, bypass the batching system entirely:

Tag configuration:
name: "Circuit 1 Alarm Word"
type: uint16
interval: 1 second
compare: true # Only on change
do_not_batch: true # Bypass batching, send immediately

When a tag is marked for immediate delivery, the gateway:

  1. Reads the register
  2. Compares against last known value
  3. If changed, publishes directly to MQTT (skipping the batch accumulator)
  4. Uses the alarm-specific MQTT topic for priority routing

This creates a dual-path architecture: bulk telemetry flows through batching (high throughput, higher latency), while alarms flow directly (lower throughput, minimal latency).

Bit-Level Alarm Decomposition

PLC alarm registers pack multiple fault conditions into single 16-bit words. The gateway should decompose these into individual alarm states:

Alarm Word (uint16): 0x0005 = 0000 0000 0000 0101

Bit 0 = 1 → High Pressure Fault ACTIVE
Bit 1 = 0 → Low Pressure: OK
Bit 2 = 1 → Sensor Failure ACTIVE
Bit 3-15 → OK

Each bit becomes an individual alarm that can trigger its own notification, be tracked independently, and show up as a distinct event in the alarm history.

Common Pitfalls and How to Avoid Them

1. Serial Bus Contention (RTU)

Problem: Multiple masters on the same RS-485 bus. Solution: Only one master per bus. If SCADA and the edge gateway both need access, use a Modbus TCP gateway to convert to TCP, then both can read via TCP.

2. Polling Too Fast

Problem: Polling a Modbus device every 100ms when data changes every 30 seconds. Solution: Match polling interval to the physics. Temperatures don't change in milliseconds. Motor speeds might. Profile your data before setting intervals.

3. Ignoring Connection Recovery

Problem: Gateway connects once at startup, never recovers from disconnection. Solution: Implement connection health monitoring with automatic recovery. Track connection state transitions (connected → disconnected → reconnecting → connected) and log them.

4. Not Validating Data Types

Problem: Reading a float32 as two uint16s because of a configuration error. Solution: During commissioning, compare gateway values against HMI/SCADA readings for every tag. Automated validation scripts help.

5. Buffer Overruns on Embedded Hardware

Problem: 64KB of RAM on an OpenWRT router, trying to buffer hours of data. Solution: Size your buffer for the outage duration you need to survive. Accept that embedded edge gateways have limits and design accordingly.

Where machineCDN Fits

machineCDN's edge gateway handles multi-protocol bridging natively — Modbus RTU, Modbus TCP, and EtherNet/IP on the southbound side, MQTT to the cloud on the northbound side. The platform handles address translation, byte ordering, data type conversion, batch accumulation, store-and-forward buffering, and connection recovery automatically.

The gateway runs on industrial hardware (including OpenWRT-based routers) with minimal resource footprint, making it suitable for retrofit installations where adding rack-mount servers isn't an option.

Key Takeaways

  1. Protocol bridging is more than format conversion — it's connection management, timing, buffering, and recovery
  2. Modbus address ranges determine function codes — get the leading digit right or you're reading wrong registers
  3. Byte ordering in 32-bit Modbus values has no standard — verify empirically, configure per device
  4. Store-and-forward is non-negotiable — any network disruption without buffering means permanent data loss
  5. Separate alarm paths from telemetry — alarms bypass batching for immediate delivery
  6. Size polling intervals to the physics — not everything needs sub-second reads
  7. EtherNet/IP and Modbus coexist — your gateway must handle both simultaneously with proper device isolation

Protocol bridging is infrastructure work. It's invisible when it's working and catastrophic when it's not. The investment in getting it right — proper address translation, robust connection handling, intelligent buffering — pays dividends in data reliability for years.