A Revolution Pi MQTT gateway gives you the familiar Raspberry Pi software stack in a DIN-rail housing built for control cabinets, with 24 V supply, industrial I/O modules and a longer product lifecycle. This article explains where industrial Pi-based controllers fit, how to read field signals from a Revolution Pi in Python and publish them to MQTT, and what changes when a prototype becomes a production gateway. The same patterns apply to other Compute Module 4 and Compute Module 5 based industrial devices.
Why an Industrial Raspberry Pi
A standard Raspberry Pi is excellent for prototypes, but a control cabinet is a different environment: 24 V DC supply, electrical noise, wide temperature ranges, vibration and maintenance staff who expect DIN-rail mounting and screw terminals. Industrial controllers based on the Raspberry Pi Compute Module solve those problems while keeping Raspberry Pi OS (or a close derivative), Python, Node-RED and the rest of the ecosystem you already know.
The Revolution Pi family from KUNBUS is the best-known example. A base module such as the RevPi Connect contains the Compute Module, Ethernet ports and power conditioning. Expansion modules for digital I/O, analog I/O and fieldbus gateways clip on the side and communicate with the base over a backplane connector. Other vendors build CM4 and CM5-based controllers with their own I/O, and the integration approach in this article carries over with a different I/O library.
| Need | Standard Pi | Industrial Pi-based controller |
|---|---|---|
| Power | 5 V USB-C | 24 V DC, often with wide input range |
| Mounting | Case or DIN adapter | Native DIN rail |
| I/O | 3.3 V GPIO | 24 V digital inputs/outputs, 0-10 V and 4-20 mA analog |
| Storage | SD card | eMMC on the Compute Module |
| Environment | Office | Control cabinet temperature and EMC ratings |
| Lifecycle | Consumer cadence | Long-term availability is a design goal |
Typical Architecture
In most deployments the industrial Pi does not replace the PLC that runs the machine. It sits next to it as an edge gateway:
- Field signals reach the controller directly through I/O modules (a door contact, a flow meter pulse, a 4-20 mA pressure transmitter) or indirectly through Modbus TCP, Modbus RTU or another protocol from the existing PLC.
- The gateway application samples values, applies scaling and deadbands, detects state changes and timestamps everything.
- MQTT carries the data outbound to a broker in the cloud or the plant network, where dashboards, historians and alerting tools subscribe.
- Optional commands travel back over MQTT to outputs, but only for non-safety functions and with explicit validation.
This division is important. The PLC keeps control deterministic and safety-rated, while the gateway handles connectivity and data modelling, which are the things a PLC is often bad at. If your signals come from a PLC rather than direct I/O, the Raspberry Pi Modbus MQTT guide shows the polling side in detail.
Reading RevPi I/O in Python
The Revolution Pi exposes all module values through a process image managed by the piControl driver. You configure modules and give each I/O a name in the PiCtory web tool, then read and write those names from any language. The piTest command is the quickest sanity check:
piTest -d # list detected modules
piTest -r I_1 # read input I_1
piTest -w O_1,1 # set output O_1
In Python, the revpimodio2 library maps the process image to objects named after your PiCtory configuration. It supports both cyclic polling and event callbacks, which suits MQTT well: inputs change rarely, so publishing on change keeps traffic low.
import json
import os
import time
import paho.mqtt.client as mqtt
import revpimodio2
BROKER = os.getenv("MQTT_HOST", "broker.connectmq.com")
PORT = int(os.getenv("MQTT_PORT", "1883"))
USERNAME = os.getenv("MQTT_USERNAME")
PASSWORD = os.getenv("MQTT_PASSWORD")
CLIENT_ID = os.getenv("MQTT_CLIENT_ID", "revpi-gw-r2d7k1")
PREFIX = os.getenv("TOPIC_PREFIX", "connectmq/demo/r2d7k1/plant1/packaging/line2/revpi1")
DIGITAL_INPUTS = ["I_1", "I_2", "I_3"] # names from PiCtory
ANALOG_INPUTS = {"InputValue_1": 0.001} # name -> scale factor (e.g. mV to V)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=CLIENT_ID)
if USERNAME:
client.username_pw_set(USERNAME, PASSWORD)
if PORT == 8883:
client.tls_set()
client.will_set(f"{PREFIX}/status", "offline", qos=1, retain=True)
client.reconnect_delay_set(1, 60)
def on_connect(client, userdata, flags, reason_code, properties):
client.publish(f"{PREFIX}/status", "online", qos=1, retain=True)
client.subscribe(f"{PREFIX}/cmd/O_1", qos=1)
def on_message(client, userdata, msg):
value = msg.payload.decode().strip().lower()
if value in ("1", "true", "on"):
rpi.io.O_1.value = True
elif value in ("0", "false", "off"):
rpi.io.O_1.value = False
client.on_connect = on_connect
client.on_message = on_message
client.connect_async(BROKER, PORT, keepalive=30)
client.loop_start()
rpi = revpimodio2.RevPiModIO(autorefresh=True)
rpi.handlesignalend(rpi.cleanup)
def publish_digital(ioname, iovalue):
payload = {"value": bool(iovalue), "ts": round(time.time(), 3)}
client.publish(f"{PREFIX}/di/{ioname}", json.dumps(payload), qos=1, retain=True)
for name in DIGITAL_INPUTS:
rpi.io[name].reg_event(publish_digital)
publish_digital(name, rpi.io[name].value) # initial state
rpi.mainloop(blocking=False)
while not rpi.exitsignal.wait(5):
analog = {n: round(rpi.io[n].value * f, 3) for n, f in ANALOG_INPUTS.items()}
client.publish(f"{PREFIX}/ai", json.dumps({**analog, "ts": int(time.time())}), qos=1)
client.publish(f"{PREFIX}/status", "offline", qos=1, retain=True)
client.loop_stop()
client.disconnect()
Digital inputs are published immediately on change and retained, so a subscriber always knows the current state. Analog values are sampled every five seconds. The command handler accepts only explicit values for one named output and ignores everything else. Note that the command subscription uses a non-retained topic by convention: if a retained "on" command were sitting on the broker, the output would switch every time the gateway restarted.
Topic Design for Plant Data
Industrial gateways benefit from a topic hierarchy that mirrors the plant: enterprise, site, area, line, cell or machine. That ISA-95 style structure is the backbone of a Unified Namespace, and it makes subscriptions natural (plant1/packaging/# for everything in one area). ConnectMQ recognises ISA-95 style topics and shows connected devices by those levels in its portal, which makes a large fleet of gateways easier to navigate. The MQTT Sparkplug B and Unified Namespace guide explains when to add Sparkplug B on top of plain JSON.
A few rules keep the namespace useful over the years:
- Put the asset path in the topic and the measurements in the payload, not the other way round.
- Use stable signal names from PiCtory or a mapping table, never raw register addresses.
- Publish status and metadata (firmware version, I/O configuration hash) to retained topics.
- Keep command topics separate (
.../cmd/...) and document exactly who may publish to them.
From Pilot to Production
The code above is enough for a pilot on the public MQTT test broker, but plant data does not belong on a public broker. Production gateways need a private broker, TLS and a few operational habits:
- Private credentials over TLS. Create a broker on ConnectMQ, use the generated username, password and client ID from the portal, and connect on port 8883. Client IDs are bound to issued credentials, so one gateway cannot impersonate another.
- Outbound-only networking. The gateway opens a connection to the broker; no inbound firewall rules are needed on the OT network.
- Store and forward. Keep a local queue so network outages do not create data gaps. The offline MQTT buffering in Python pattern works unchanged on a RevPi.
- Service management. Run the script as a systemd unit with automatic restart, and use the hardware watchdog.
- Hardening. SSH keys, a firewall, regular updates and a dedicated service user, as covered in the secure Raspberry Pi IoT checklist.
- Change control. Version your PiCtory configuration and gateway code together, so the I/O names in the code always match the hardware.
A managed MQTT broker handles the server side: certificates, availability and quotas. For larger plants the Dedicated and BYOC plans keep traffic on an isolated cluster or in your own cloud account.
Where Industrial Pi Gateways Fit Well
Industrial Pi-based controllers are a strong choice for retrofitting older machines with sensors the PLC never had, monitoring utilities such as compressed air, water and energy meters, collecting machine states for OEE calculations, and protocol conversion where a vendor gateway would be expensive or closed. They are not a substitute for safety PLCs, and they should not close fast control loops that depend on deterministic timing. Keep those in the controller designed for them and let the gateway report what happens.
For a broader view of edge gateways on the Pi platform, see the Raspberry Pi MQTT tutorial and the official Compute Module documentation.
Connect Your Industrial Gateways to ConnectMQ
Start a pilot on the free Serverless plan at app.connectmq.com, no credit card required, and watch your RevPi signals appear in the live topic explorer within minutes. When the pilot proves its value, move the same gateways to a Dedicated or BYOC broker without changing the code.
Frequently Asked Questions
Can I use Node-RED instead of Python on a Revolution Pi?
Yes. Node-RED runs on the RevPi and has nodes for the process image, and its built-in MQTT nodes publish to any broker. Python gives you more control over buffering and testing.
Does the Revolution Pi replace a PLC?
It can run simple control logic, but most plants keep their existing PLCs for control and safety and use the RevPi as a data gateway alongside them.
How fast can a RevPi gateway publish data?
The I/O cycle is fast enough for monitoring, but the useful question is how often consumers need data. Publish on change for digital signals and at a fixed interval for analog values.
Is MQTT secure enough for plant data?
MQTT over TLS with unique credentials per gateway, outbound-only connections and a private broker is a sound design. Never publish plant data to a public broker.



