Bridging OPC UA to MQTT
TL;DR — OPC UA is the standard PLC protocol in 2022 industry; MQTT is the standard for IT/cloud ingest. A bridge subscribes to OPC UA nodes, publishes changes to MQTT. Use commercial gateways (HighByte, Siemens) for fewer projects; open-source / custom for more control.
After Sparkplug, the question of how data actually leaves the PLC. OPC UA is the protocol PLCs (Siemens S7, Allen-Bradley, Beckhoff) speak. MQTT is what your cloud expects. A bridge translates.
What OPC UA is
OPC Unified Architecture. IEC 62541. Open standard. Successor to OPC Classic (Windows-only DCOM). Designed to be cross-platform, secure, and replace the patchwork of industrial protocols.
Key features:
- Address space model: hierarchical tree of “nodes” (variables, methods, objects)
- Subscriptions: client subscribes to a node; server pushes changes
- Security: certs, encryption, authentication
- Transport: typically TCP on port 4840 (binary) or HTTPS (less common)
PLCs running OPC UA Server expose their internal state as nodes:
Objects
└── Server
└── ProductionLine1
├── Press42
│ ├── Temperature (value, double)
│ ├── Pressure (value, double)
│ ├── State (value, string)
│ └── EmergencyStop (method)
└── Conveyor3
└── Speed (value, double)
Your bridge connects, finds relevant nodes, subscribes, gets value-change notifications.
Why the bridge
OPC UA is well-designed for OT but doesn’t fit cloud ingest well:
- TCP-based, persistent connections
- Cert-heavy security model
- Server-push, not topic-routed
- Limited tooling outside industrial vendors
MQTT is the cloud-friendly transport. Light, topic-based, decouples publishers from subscribers, runs anywhere.
The bridge: an OPC UA client that subscribes to nodes you care about, formats values, publishes to MQTT.
Bridge options in 2022
Commercial gateways:
- HighByte Intelligence Hub — modern, GUI-driven, good UX. Per-deployment licensing.
- Cirrus Link MQTT Distributor — Ignition-based, Sparkplug-native.
- Siemens IoT2050 — Siemens hardware + bridge software.
Pros: GUI, vendor support, supported by industrial integrators. Cons: per-license costs, vendor lock-in to some extent.
Open-source / build-your-own:
- Eclipse Milo (Java) — OPC UA SDK, write your own bridge
- open62541 (C) — most common embedded OPC UA stack
- Node-OPCUA (Node.js) — good for prototyping
- gopcua/opcua (Go) — Go SDK
- asyncua (Python) — Python SDK
For 5+ project deployments, commercial gateway might save engineering time. For 1-3 projects or custom needs, building one is straightforward.
A simple custom bridge in Go
package main
import (
"context"
"encoding/json"
"log"
"time"
"github.com/gopcua/opcua"
"github.com/gopcua/opcua/ua"
mqtt "github.com/eclipse/paho.mqtt.golang"
)
type NodeMapping struct {
OPCUANodeID string
MQTTTopic string
}
func main() {
ctx := context.Background()
opcClient, err := opcua.NewClient("opc.tcp://10.0.0.50:4840")
if err != nil { log.Fatal(err) }
if err := opcClient.Connect(ctx); err != nil { log.Fatal(err) }
defer opcClient.Close()
mqttClient := newMQTTClient(...)
mappings := []NodeMapping{
{"ns=2;s=ProductionLine1.Press42.Temperature", "factory/press42/temperature"},
{"ns=2;s=ProductionLine1.Press42.Pressure", "factory/press42/pressure"},
{"ns=2;s=ProductionLine1.Conveyor3.Speed", "factory/conveyor3/speed"},
}
sub, err := opcClient.SubscribeWithContext(ctx, &opcua.SubscriptionParameters{
Interval: 500 * time.Millisecond,
}, func(s *opcua.PublishNotificationData) {
switch v := s.Value.(type) {
case *ua.DataChangeNotification:
for _, item := range v.MonitoredItems {
handleChange(item, mappings, mqttClient)
}
}
})
if err != nil { log.Fatal(err) }
defer sub.Cancel(ctx)
for i, m := range mappings {
id, _ := ua.ParseNodeID(m.OPCUANodeID)
sub.Monitor(ctx, ua.TimestampsToReturnBoth, opcua.MonitoringParameters{
NodeID: id,
ClientHandle: uint32(i),
})
}
select {} // run forever
}
func handleChange(item *ua.MonitoredItemNotification, mappings []NodeMapping, mqtt mqtt.Client) {
idx := int(item.ClientHandle)
topic := mappings[idx].MQTTTopic
payload, _ := json.Marshal(map[string]any{
"ts": time.Now().UTC().Format(time.RFC3339),
"value": item.Value.Value.Value(),
})
mqtt.Publish(topic, 1, false, payload)
}
200 lines, runnable on an edge gateway. Reliable for 100s of nodes.
Node selection
Don’t subscribe to every node. PLCs expose hundreds; most are internal state nobody needs externally. Curate.
The pattern that works:
- Browse the address space once via UaExpert (free OPC UA browser) — visual tree of nodes
- Document the mapping in YAML/JSON config: which OPC UA NodeID → which MQTT topic
- Subscribe only to the mapped nodes at runtime
- Add new nodes via config, not code
# bridge-config.yaml
mappings:
- opcua_node: "ns=2;s=ProductionLine1.Press42.Temperature"
mqtt_topic: "factory/press42/temperature"
type: float
sample_rate_ms: 500
- opcua_node: "ns=2;s=ProductionLine1.Conveyor3.Speed"
mqtt_topic: "factory/conveyor3/speed"
type: float
sample_rate_ms: 1000
OT and IT teams can collaborate on the YAML. OT says “we’ll expose these 20 nodes”; IT consumes the MQTT topics.
Security
OPC UA security has multiple modes:
- None — no auth, no encryption. Lab/dev only.
- Sign — auth + integrity but no encryption.
- SignAndEncrypt — full TLS-equivalent.
For production, always SignAndEncrypt. The bridge needs a cert; the PLC needs to trust it. Coordinate with OT before deployment.
Auth options:
- Anonymous — for dev only
- Username/password — basic
- Certificate-based — mutual auth, production grade
Common Pitfalls
Subscribing to everything. Floods MQTT, exhausts PLC bandwidth. Curate.
Sample rate too low. Subscribing at 50ms intervals when 1s is fine = wasted everything. Tune to what the metric actually needs.
No reconnect logic. OPC UA connections drop; bridge must reconnect. Most SDKs have it; configure it.
Translating OPC UA semantics to MQTT badly. E.g., OPC UA “quality” field is rich (good/bad/uncertain with reason codes). Squashing to “value only” loses information. Decide what to keep.
No node-name versioning. OT renames a node; bridge breaks silently. Use stable OPC UA NodeIDs, not browse paths.
Bridge as single point of failure. If the bridge dies, all data stops. Run two or have monitoring + auto-restart.
Wrapping Up
OPC UA → MQTT bridge is the universal “factory data into cloud” pattern. Commercial for fast-time-to-value, custom for control. Monday: Grafana dashboards for IIoT.