A Modbus-to-MQTT gateway polls data from a Modbus device on a schedule and republishes it as MQTT messages, letting decades-old PLCs and instruments join the same modern, pub-sub-based dashboard and alerting stack used everywhere else on this site, without replacing the underlying equipment. Node-RED, already covered elsewhere on this site, is the simplest way to build this for most setups.
The problem this solves
Modbus is a point-to-point, poll-and-response protocol: one master asks one slave for a specific register value, gets one answer, and that’s the entire interaction. MQTT is publish-subscribe: a device publishes once, and any number of subscribers (a dashboard, an alert system, a data logger) receive it without the publisher needing to know who’s listening. Most modern IoT tooling, including everything else covered on this site, is built around the MQTT pattern. A gateway translating between the two means a 15-year-old PLC speaking only Modbus can still feed into a modern Grafana dashboard or trigger a Home Assistant automation, without anyone touching the PLC itself.
The architecture in plain terms
A small process, running continuously on the VPS, does three things on a loop: connect to the Modbus device, read specific registers, publish the values to MQTT topics. Everything downstream (dashboards, alerts, logging) then just subscribes to MQTT like any other device on the network, with no knowledge that the original data source is Modbus at all.
Building it in Node-RED
If Node-RED is already running on your VPS, this is the simplest path: install the Modbus nodes package via Node-RED’s palette manager (Menu > Manage Palette > search “node-red-contrib-modbus”), then build a flow with three parts:
- An inject node on a timer (every 10-30 seconds is typical for most monitoring use cases) to trigger each poll cycle.
- A Modbus Read node, configured with the PLC’s IP address, port 502, and the specific register range you want.
- An MQTT out node, publishing the result to a topic like
site1/plc/temperature, connected to your Mosquitto broker.
A function node between the Modbus read and the MQTT publish is usually where you convert the raw register value into something meaningful, register values are typically raw integers that need scaling (dividing by 10 or 100 for a decimal temperature reading, for example), which is specific to each device’s documentation and worth confirming against the PLC manufacturer’s register map rather than guessing.
Alternative: a dedicated gateway tool
For larger setups with many devices and registers to map, a dedicated tool like modbus2mqtt (an open-source utility built specifically for this bridging job) can be a cleaner fit than building dozens of individual Node-RED flows by hand, since it’s designed around configuration files mapping many registers at once rather than visual flows per data point. The trade-off: less visual, more configuration-file-driven, which suits some teams better than others depending on whether you’d rather see the logic or write it.
| Approach | Best for |
|---|---|
| Node-RED with Modbus nodes | A handful of devices, visual logic, teams already using Node-RED for other automation |
| Dedicated gateway tool (modbus2mqtt or similar) | Many devices, many registers, configuration-driven setups managed as code |
Polling frequency: getting it right
This is worth deliberate thought rather than a default guess. Polling too frequently adds unnecessary load to older PLCs that weren’t designed for high request rates, and can in some cases affect their primary control performance. Polling too infrequently means your dashboard shows stale data and alerts fire late. A reasonable starting point for most monitoring (not safety-critical control) use cases is every 10-30 seconds, adjusted based on how quickly the underlying value actually changes and how the specific PLC handles request load.
Security: this doesn’t change the Modbus risk picture
It’s worth being explicit: a Modbus-to-MQTT gateway doesn’t add security to the Modbus side of the connection, Modbus still has no native authentication, covered in detail in Remote Access to PLCs and Modbus Devices via VPS. The gateway should sit on the same protected network as the PLC itself (reachable via the VPN pattern from VPS as a SCADA VPN Concentrator), not bridge a Modbus connection across an untrusted network on its own.
What this unlocks
Once Modbus data is flowing into MQTT, everything else on this site applies directly: Grafana dashboards, Home Assistant automations, ThingsBoard rule chains, all consume MQTT the same way regardless of whether the original source was a smart plug or a 20-year-old PLC. This is, in a real sense, the entire point of the gateway pattern: it makes old industrial equipment a first-class citizen in a modern stack, rather than something permanently siloed off.
Troubleshooting common problems
Gateway flow runs but no MQTT messages appear. Check the Modbus Read node’s status indicator in Node-RED first; a red or yellow status usually means the connection to the PLC itself is failing, in which case the problem is upstream of MQTT entirely. A green/connected status with still no MQTT messages points to the publish side instead, check the broker credentials and topic in the MQTT out node.
Values look plausible but are scaled incorrectly. The single most common gateway-building mistake: forgetting that raw Modbus register values are almost always integers requiring a scale factor (divide by 10, 100, or a manufacturer-specific value) to become a meaningful decimal reading. Always check the specific PLC or instrument’s register map documentation for the correct scaling rather than assuming a standard.
Gateway works for a while, then silently stops updating. Often a Node-RED flow that doesn’t handle a Modbus timeout gracefully, leaving the flow in a stuck state rather than retrying on the next scheduled poll. Adding explicit error handling (a catch node, or checking the Modbus Read node’s own error output) prevents one failed poll from quietly breaking all future ones.
High latency between a real-world change and it appearing on the dashboard. Usually just the polling interval doing exactly what it’s configured to do; if 10-30 seconds isn’t responsive enough for a specific use case, shortening the interval is the direct fix, balanced against the device-load consideration covered above.
Scaling beyond a handful of devices
A few individual Node-RED flows, each polling one PLC, works fine for a small number of devices. Past roughly ten to fifteen devices, the visual-flow-per-device approach starts becoming unwieldy to maintain, and this is usually the point where either a single, more generic flow driven by a configuration list (rather than one hardcoded flow per device) or a dedicated tool like modbus2mqtt starts paying for the slightly steeper initial setup. There’s no fixed threshold, it’s worth watching for the maintenance burden becoming the bottleneck rather than waiting for a specific device count.
Naming MQTT topics consistently
As a gateway grows to cover multiple sites and device types, a consistent topic naming structure pays off significantly, for example site/device-type/device-id/measurement (such as site1/plc/pump-3/temperature). This makes wildcard subscriptions in downstream tools (Grafana, ThingsBoard, a simple mosquitto_sub -t 'site1/#' for debugging) genuinely useful rather than requiring a separate subscription per device, and is far easier to establish early than to retrofit once dozens of devices are already publishing under an inconsistent scheme.
Frequently asked questions
Can this work with Modbus RTU (serial) devices, not just Modbus TCP?
Yes, with a serial-to-IP converter (a small, inexpensive piece of hardware) connecting the RTU device to the network, after which the gateway treats it the same as any Modbus TCP device. Node-RED’s Modbus nodes support both serial and TCP connections directly.
What happens if the PLC stops responding to polls?
A well-built gateway flow should handle this explicitly: a timeout on the Modbus read, and ideally a “device offline” status published to MQTT so downstream dashboards and alerts know the difference between “value is zero” and “we haven’t heard from this device”.
Is this the same as what an industrial IoT gateway appliance does?
Conceptually yes, dedicated hardware gateways do the same Modbus-to-MQTT (or similar) translation. Running it as software on a VPS instead is cheaper and more flexible for most setups on this site, at the cost of needing the VPS itself to be reliably reachable from the PLC’s network, which is exactly what the VPN concentrator pattern provides.
How much VPS capacity does a gateway like this need?
Very little; polling a modest number of registers every few seconds is a trivial workload. This is one of the lightest things covered on this site, easily shared on the same small VPS already running MQTT and Node-RED.
Data retention: deciding how much history to keep
Once Modbus data is flowing into MQTT and onward into a time-series database, it’s worth a deliberate decision about retention rather than letting it grow unbounded. High-frequency polling (every few seconds) generates meaningfully more data over months than people initially expect, and most monitoring use cases don’t need full-resolution data forever, recent history at full detail, older history downsampled to hourly or daily averages, is a common and sensible pattern. InfluxDB and similar time-series databases support this kind of automatic downsampling and retention policy natively, worth configuring early rather than discovering a full disk later.
A complete example: from PLC to phone notification
Pulling everything in this guide and its linked pages together, a realistic end-to-end setup looks like this: a PLC at a remote site, reachable through a WireGuard tunnel to a central VPS; a Node-RED flow on that VPS polling Modbus registers every 15 seconds and publishing to MQTT; a second Node-RED flow subscribed to that data, checking it against a threshold with debounce logic to avoid false positives; and on a genuine alert condition, a notification sent via email or a messaging service’s API. Every individual piece in that chain is covered by a dedicated guide elsewhere on this site, and none of them require anything beyond a single small VPS to run reliably.
Can I write to a PLC through this same gateway, not just read from it?
Yes, Node-RED’s Modbus nodes support writes the same way as reads, subscribing to an MQTT command topic and translating incoming messages into Modbus write operations. Worth applying the read/write access distinction covered in the PLC remote access guide deliberately here too, rather than wiring write access in by default.
Should each PLC have its own dedicated gateway flow, or can one flow handle multiple devices?
Both patterns are common. One flow per device is simpler to understand and debug individually; one generic, configuration-driven flow scales better once device counts grow, as covered above. Starting with one-per-device and consolidating later, once the pattern across devices becomes clear, is a reasonable default for most projects.
Does the gateway need to run on the same VPS as the MQTT broker?
No, though it’s the simplest setup for most projects on this site. They can run on separate servers as long as the gateway can reach both the PLC’s network (via the VPN tunnel) and the MQTT broker, which itself just needs network connectivity, not physical co-location.
How do I handle a PLC with hundreds of registers without building hundreds of individual flow nodes?
Modbus’s “Read Multiple Registers” function reads a contiguous block of registers in a single request, which a single Modbus Read node can handle, followed by one function node splitting the block into individually named values. This is significantly more efficient than one read request per register, both in Node-RED flow complexity and in load placed on the PLC itself.
What’s a reasonable next step once a single-site gateway like this is working well?
Extending the same pattern across additional sites through the VPN hub covered in VPS as a SCADA VPN Concentrator, and consolidating multiple sites’ data centrally, which is exactly what Multi-Site Industrial IoT Data Aggregation covers.
Is it worth alerting on the gateway itself going down, not just the PLC it’s polling?
Yes, worth treating as a separate failure mode: a gateway flow that’s crashed produces the same symptom from a dashboard’s perspective as a PLC going offline, no new data, but the fix is completely different. Monitoring that the gateway process itself is alive (a simple periodic heartbeat message published regardless of PLC state) helps distinguish the two quickly when something stops updating.