← All Posts

Modbus to MQTT Gateway: Bridging PLC Data to a Broker

Every plant eventually hits the same wall: the PLC speaks Modbus TCP, the historian, dashboard, or cloud service speaks MQTT, and something has to stand between them. That something is a Modbus to MQTT gateway. This post covers why the bridge is worth building, how to design the topics and QoS so the data stays trustworthy, and a working pattern using Voltrus Gateway, a single binary that speaks both ends of the bridge over one API.

Why Not Let Every Application Poll Modbus Directly?

Modbus is a client-server protocol with no native event push. Every consumer must poll: send a read request, get a response, repeat. The pattern collapses in a predictable way:

  • Five applications polling the same registers means five independent poll loops against one device. Polling rate limits that were fine for one master fall apart with five.
  • Every new consumer needs network reachability into the OT segment. Each one is another firewall rule, another set of credentials floating around, another client that misbehaves when the PLC reboots.
  • IT-side tools cannot handle Modbus framing at all. A cloud dashboard expects a broker connection, not a TCP socket to port 502.

MQTT inverts the data flow. A single client polls the PLC once, publishes the values to a broker, and any number of subscribers receive them. The protocol is an OASIS standard built around a central broker, so consumers only ever need one thing: a broker connection. The PLC keeps exactly one Modbus client.

The Three Architecture Options

Option
How it works
Trade-off
DIY: two libraries, one script
A Python or Node script with a Modbus client library and an MQTT client library, glue in between
You own both protocol stacks: reconnect logic, timeouts, byte order, exception handling, plus two dependency trees to keep patched
One gateway binary, both protocols
A single process that natively speaks Modbus TCP and MQTT and exposes both over one API; your code only handles the mapping
License cost (one-time, per host); protocol bugs are fixed upstream, not by you
Full platform (OPC server + IoT module)
Commercial suites that model tags, then forward to MQTT from the same tag database
Heaviest footprint and cost, often Windows-bound, usually licensed per tag or per connection

The DIY route is genuinely fine for a one-off proof of concept. It stops being fine the second the bridge is load-bearing: reconnection after a PLC power cycle, stale-value handling, and float byte order are exactly the details a script forgets at 3 a.m. If the bridge matters, the middle option is the boring, defensible choice.

Design the Topics Before the Code

The topic scheme outlives every implementation detail. Two rules prevent most pain, covered in depth in our MQTT topic design guide:

  1. Encode the asset hierarchy, not the register map. plant1/line2/mixer3/temperature survives a PLC swap; device1/hr400 does not.
  2. One topic per value, not one giant JSON blob per poll. Per-value topics let subscribers pick only what they need and make broker-side access control possible.

QoS, then, per message class. The short version from our QoS explainer: polled process values are safely QoS 0, because the next poll supersedes the last. Counters, batch totals, and events deserve QoS 1. Set retained on the last published value of each topic, so a historian that reconnects after a broker restart sees current values immediately instead of waiting a full poll cycle. If your organization needs a full data model with birth/death certificates on top of MQTT, that is Sparkplug B, and we have a separate Sparkplug B walkthrough.

The Bridge Pattern with Voltrus Gateway

Voltrus Gateway is a single Rust binary that speaks Modbus TCP and MQTT natively (among seven protocols), all behind one typed gRPC API with read, write, publish, and subscribe support. The bridge stops being "glue two protocol stacks together" and becomes "map register addresses to topic names":

  1. Connect to the PLC. Open a Modbus session: IP, port, Unit ID.
  2. Connect to the broker. Open an MQTT session to your broker.
  3. Define the map. A list of tags: register address, register area, data type, byte order, and the MQTT topic each value publishes to.
  4. Run the loop. Read the tags, publish each to its topic, handle errors per tag instead of per connection.

The Python SDK (available to licensed customers on request) makes the loop a few lines long:

from voltrus_gateway import VoltrusGateway

with VoltrusGateway("http://127.0.0.1:50061") as gw:
    plc = gw.connect("modbus", ip="192.168.1.50", unit_id=1, endianness="big")

    tags = [
        {"name": "temperature", "address": 400, "reg_type": "float32", "register_area": "holding"},
        {"name": "motor_running", "address": 12, "reg_type": "bool", "register_area": "coil"},
    ]

    while True:
        for tv in gw.read_tags(plc, tags):
            topic = f"plant1/line2/mixer3/{tv.tag.modbus.name}"
            mqtt.publish(topic, tv.value, qos=1, retain=True)   # your broker session

The point is not that the script is short. It is that the failure modes are handled where they belong: a failed poll arrives as a per-tag error and quality stamp instead of killing the stream, and values return automatically when the device recovers. The gateway process holds the protocol state; your script holds only the mapping. The same binary also covers the other direction, writing an MQTT-received setpoint back to a Modbus register, and the rest of the gRPC surface works identically for OPC UA, EtherNet/IP, S7, DNP3, and BACnet, so the second bridge is configuration, not a new project. For how this compares to the traditional Windows OPC route, see Voltrus Gateway vs Kepware.

Operational Checklist

  • Poll rate: pick it from the process, not the network. A temperature that moves over minutes does not need 100 ms polls; each poll costs the PLC a request-response cycle.
  • Byte order: a float32 spans two Modbus registers. Confirm ABCD/DCBA/BADC/CDAB against the device manual before trusting a single published value.
  • Stale data: decide what a subscriber should do when polls fail. Publishing a quality flag alongside the value beats letting consumers assume freshness.
  • Last-will: register an MQTT last-will message for the bridge process itself, so a dead gateway is visible at the broker, not just at the PLC.
  • Security: the broker sits on the IT side; keep the Modbus connection confined to the OT segment and let only the gateway straddle the line.
Rule of thumb: if more than one application needs Modbus data, or if any consumer lives outside the OT network, put a gateway and a broker between them. Poll once, publish everywhere.

Frequently Asked Questions

What does a Modbus to MQTT gateway do?

It reads data from Modbus devices (holding registers, input registers, coils) and publishes the values as MQTT messages to a broker. Historians, dashboards, and cloud services subscribe to the broker instead of polling the PLC directly. This keeps the number of Modbus clients on the PLC small and decouples the OT network from IT consumers.

Why not let every application poll Modbus directly?

Modbus has no native event push: every consumer must poll, and each poll is a request-response round trip to the PLC. Five dashboards polling the same registers means five separate poll loops against one device. With MQTT in between, the PLC is polled once, and any number of subscribers receive the values from the broker.

Which QoS should I use for Modbus data published to MQTT?

QoS 0 is usually right for continuously polled process values, since the next poll supersedes the last message. Use QoS 1 for values that must arrive, such as counters, batch totals, and events. QoS 2 is rarely worth the overhead on telemetry. Retained messages let new subscribers see current values immediately without waiting for the next poll cycle.

Does Voltrus Gateway replace a dedicated Modbus MQTT bridge?

Voltrus Gateway is a single Rust binary that speaks Modbus TCP and MQTT with read, write, publish, and subscribe support over one typed gRPC API. Instead of configuring a black-box bridge, you connect the gateway to the PLC and to the broker and control exactly what is read and published with a short script. It is a one-time license per host, with no per-tag licensing.

Voltrus Gateway: One Binary, Both Ends of the Bridge

Modbus TCP, MQTT, OPC UA, EtherNet/IP, S7, DNP3, and BACnet behind one typed gRPC API. Self-hosted, single binary for Linux, macOS, and Windows. $49.99 one-time, one license per host.

Buy a License: $49.99 →

Further Reading