Introduction: OPC UA as the Lingua Franca of Industry 4.0
OPC UA (Open Platform Communications Unified Architecture) is the international standard for industrial data exchange. Defined in the IEC 62541 specification (14 parts), OPC UA decouples industrial communication from proprietary vendor ecosystems and OS-specific dependencies. Unlike its predecessor, OPC Classic (DCOM-bound to Windows), OPC UA runs cross-platform—Linux, Windows, real-time OS, embedded systems, cloud—using standardized message encoding and transport mechanisms.
The protocol has become the de facto interoperability layer for Industry 4.0, enabling:
- Secure, authenticated access to PLCs, sensors, and edge devices from enterprise systems (ERP, MES, analytics)
- Uniform data modeling through the Address Space (a semantic graph of nodes, types, and references)
- Real-time pub/sub messaging (OPC UA PubSub, UDP multicast, MQTT)
- Field-level deterministic control (OPC UA FX, TSN-aware scheduling)
- Certificate-based authentication and message-level encryption
This guide provides a first-principles technical foundation for OPC UA, from specification architecture through production-grade implementation patterns. We anchor every concept in real industrial constraints—latency, determinism, scalability, certificate management, subscription fan-out—that shape how engineers design and operate OPC UA deployments at the enterprise and field levels.
TL;DR
OPC UA is an IEC 62541-standardized, cross-platform protocol for industrial device communication. Key attributes:
- Decoupled from DCOM/Windows. Works on Linux, embedded, cloud. Binary encoding + secure channel provides compact, authenticated transport.
- Address Space as semantic graph. Nodes (Object, Variable, Method, DataType) and typed References (HasChild, HasComponent) form a navigable, self-describing data model.
- 35 core services: Read, Write, Browse, Call (methods), Subscribe (monitored items), RegisterNodes, TranslateBrowsePath, etc.
- Dual transport: Client/Server (request-response over TCP/HTTPS) and PubSub (pub/sub over UDP multicast, MQTT, TSN).
- Security-by-default: X.509 certificates, secure channel (DH key exchange), message signing (HMAC-SHA256), and encryption (AES-256).
- OPC UA FX: New field exchange model (2024+) for deterministic, TSN-aware, multi-publisher/subscriber scenarios.
- Compared to OPC Classic: No DCOM firewalls, cross-platform, scalable secure channel per client. Compared to MQTT/Sparkplug: semantic type safety, method invocation, hierarchical browsing.
Reference Implementation Libraries: open62541 (C), python-opcua (Python), .NET OPC UA Stack, Node-opcua (JS).
Table of Contents
- Key Concepts
- Why OPC UA Replaced OPC Classic
- Information Model and Address Space
- Services and Transport Stack
- PubSub and OPC UA FX
- Security Model
- OPC UA vs OPC Classic vs MQTT Sparkplug
- Edge Cases and Failure Modes
- Implementation Guide
- FAQ
- Where OPC UA Is Heading
- References
- Related Posts
Key Concepts
Before diving into the protocol mechanics, define the mental model:
Server & Client
A Server hosts an Address Space—a collection of Nodes that represent devices, sensors, alarms, and methods. A Client connects to a Server, establishes a Secure Channel, and then issues Requests (Read, Write, Browse, Subscribe) to which the Server responds.
Multiple clients can connect to a single server; the server maintains per-client Sessions that authenticate and track subscriptions.
Session & Secure Channel
A Session is a logical connection between client and server, tied to a user identity (username/password, X.509 cert, or anonymous). A Secure Channel is the encrypted, signed transport layer that wraps all messages between a given client and server. One session may span multiple secure channels (if the first channel is lost, the client can reconnect and resume the session).
Address Space
The Address Space is an in-memory, hierarchical graph of Nodes. Each node has a NodeId (e.g., ns=2;s=Pump_01), a NodeClass (Object, Variable, Method, DataType, ReferenceType), and Attributes (e.g., DisplayName, Description, DataType, Value for variables).
Nodes are connected by References, which are directional and typed. Example: A Node “Pump_01” (Object) has a Reference “HasComponent” to a Node “Pressure” (Variable).
Node & Reference
Nodes are first-class citizens. Each has:
- NodeId: Namespace prefix + identifier (numeric or string).
- NodeClass: Object, Variable, Method, DataType, ReferenceType, View.
- Attributes: QualifiedName, DisplayName, Description, WriteMask, Value, DataType, ValueRank, ArrayDimensions, AccessLevel, UserAccessLevel, MinimumSamplingInterval, Historizing (for variables); executable, UserExecutable (for methods).
References are semantic links: HasChild, HasComponent, HasProperty, Organizes, HasTypeDefinition. References are directional and can be browsed bidirectionally.
Information Model & Companion Specification
The Information Model is the structural blueprint for the Address Space. It defines Types (ObjectType, VariableType, DataType, ReferenceType) from which concrete nodes are instantiated.
A Companion Specification is an OPC Foundation profile for a specific domain (e.g., PLCopen for PLC variables, PackML for packaging machines, ISA 95 for manufacturing operations). Each companion spec extends the base information model with domain-specific types.
PubSub & Field Exchange (FX)
PubSub decouples publishers from subscribers. A publisher (sensor, edge device) emits data to a Publisher Endpoint (UDP multicast group, MQTT broker topic); subscribers listen. This is event-driven, not request-response.
OPC UA FX (Field Exchange) is the 2024+ successor to client/server for deterministic, time-synchronized field scenarios. FX controllers publish and subscribe over TSN (Time-Sensitive Networking) or deterministic Ethernet, eliminating the latency jitter of TCP-based polling.
Secure Channel & Message Security
A Secure Channel is negotiated via handshake (client hello → server hello → DH key exchange). Once established, all messages are:
- Sequenced (sequence number prevents replay).
- Optionally signed (HMAC-SHA256 over plaintext + sequence number).
- Optionally encrypted (AES-256-CBC over signed message).
The security level is negotiated and applies to all messages on that channel.
Why OPC UA Replaced OPC Classic
OPC Classic (OPC DA, AE, HDA) relied on DCOM (Distributed Component Object Model) and Windows-specific infrastructure. This created profound constraints:
OPC Classic Limitations
| Problem | Impact |
|---|---|
| DCOM firewalls | Impossible to cross DMZ; requires opening unpredictable port ranges. |
| Windows-only | Server must run Windows; embedded Linux devices cannot be OPC DA servers. |
| No encryption | Passwords sent in clear; no mutual authentication. |
| Scaling issues | Each client opens a separate TCP connection; DCOM overhead per connection. |
| Type system weakness | Data types are opaque (VARIANT); no self-describing semantic model. |
| No native pub/sub | Only request-response; polling creates latency and CPU overhead. |
OPC UA Solution

OPC UA decouples from OS and network protocol:
- Cross-platform. Servers run on Windows, Linux, ARM, RTOS. Clients on any OS.
- Binary + JSON encoding. Compact wire format (binary) or human-readable (JSON). No DCOM RPC overhead.
- Native encryption. X.509 certificates, AES-256, HMAC-SHA256. Secure-by-default.
- Scalable secure channel. One secure channel per client, not per RPC call.
- Rich type system. Address Space graph with typed nodes and references enables semantic navigation.
- Pub/Sub and Request-Response. Both communication patterns natively supported.
- Single port. Default TCP 4840; no dynamic port allocation.
Historical Fact: The first OPC UA spec (IEC 62541-1:2009) was released in 2009; widespread adoption began ~2015. By 2020, most modern SCADA servers and PLCs supported OPC UA.
Information Model and Address Space
The Address Space is OPC UA’s most powerful concept. It is a semantic graph where every data point, method, and relationship is explicitly modeled and navigable.
Node Anatomy
Every node is defined by:
NodeId: ns=2;s=Pump_01
NodeClass: Object
Attributes:
- DisplayName: "Pump 1"
- Description: "Main circulation pump"
- BrowseName: "Pump_01"
- QualifiedName: ns=2;Pump_01
For a Variable node:
NodeId: ns=2;s=Pump_01.Pressure
NodeClass: Variable
Attributes:
- DisplayName: "Pressure"
- DataType: ns=0;i=7 (Double)
- Value: 102.5 (Float64)
- AccessLevel: CurrentRead | CurrentWrite
- ValueRank: -1 (Scalar)
- MinimumSamplingInterval: 100 (milliseconds)
For a Method node:
NodeId: ns=2;s=Pump_01.Start
NodeClass: Method
Attributes:
- DisplayName: "Start"
- Executable: true
- UserExecutable: true (based on session user)
- InputArguments: (none)
- OutputArguments: (StatusCode)
Address Space Structure: References
Nodes are connected by typed References:

Core Reference Types:
| Reference | Direction | Meaning |
|---|---|---|
| HasChild | Parent → Child | Containment (parent owns child). |
| HasComponent | Object → Variable/Method | Composition (component is part of object). |
| HasProperty | Any → Variable | Metadata property. |
| Organizes | Container → Element | Logical grouping (folder). |
| HasTypeDefinition | Instance → Type | Specifies the type of an instance node. |
| HasSubtype | Subtype → Supertype | Type hierarchy. |
Example hierarchy:
Root (ns=0;i=84)
├─ Objects (ns=0;i=85)
│ ├─ Server (ns=0;i=2253)
│ └─ MyFactory (ns=2;i=1001, custom)
│ ├─ Pump_01 (Object, ns=2;i=1002)
│ │ ├─ Pressure (Variable, ns=2;i=1003) [HasComponent]
│ │ ├─ Flow (Variable, ns=2;i=1004) [HasComponent]
│ │ ├─ Status (Variable, ns=2;i=1005) [HasComponent]
│ │ ├─ Start (Method, ns=2;i=1006) [HasComponent]
│ │ └─ LastError (Variable, ns=2;i=1007) [HasProperty]
│ └─ Pump_02 (Object, ns=2;i=1008)
Information Model: Types
The Type System defines the blueprint for instances. Every instance has a HasTypeDefinition reference to its type.
Built-in Types (ns=0, OPC UA core):
- BaseDataType → String, Int32, Float, Boolean, DateTime, Guid, etc.
- ObjectType → BaseObjectType → FolderType, ServerType, etc.
- VariableType → BaseVariableType → PropertyType, etc.
- ReferenceType → BaseReferenceType → HasChild, HasComponent, etc.
Custom Types (ns > 0, domain-specific):
A manufacturer might define:
ns=2;PumpType (ObjectType)
├─ Pressure (VariableType, DataType=Double)
├─ Flow (VariableType, DataType=Double)
├─ Status (VariableType, DataType=PumpStatus enum)
└─ Start (MethodType)
Then instantiate: Pump_01 as an instance of PumpType.
Companion Specifications
A Companion Specification is a standardized extension for a domain. Examples:
- PLCopen: Variables and function blocks for PLC programming.
- PackML: Packaging machine states and diagnostics.
- ISA 95: Manufacturing operations (recipes, production schedules).
- IEC 61850: Power systems (circuit breakers, transformers).
- PROFINET: Integration of PROFINET devices into OPC UA.
Each companion spec defines new ObjectTypes, VariableTypes, and ReferenceTypes, ensuring semantic interoperability within the domain.
Services and Transport Stack
OPC UA defines 35 core services organized into groups:
Discovery & Session Management
| Service | Semantics |
|---|---|
| GetEndpoints | Client discovers server endpoints and security policies. |
| CreateSession | Client authenticates and creates a session. |
| CloseSession | Terminate session. |
| ActivateSession | Resume a disconnected session. |
Address Space & Browsing
| Service | Semantics |
|---|---|
| Browse | Retrieve child nodes and references from a starting node. |
| TranslateBrowsePath | Navigate by path string (e.g., “/Pump_01/Pressure”). |
| BrowseNext | Pagination for large result sets. |
| RegisterNodes | Cache node IDs locally for faster future access. |
Data Access
| Service | Semantics |
|---|---|
| Read | Fetch variable values, timestamps, and metadata. |
| Write | Update variable values. |
| ReadHistory | Retrieve historical data (if server stores it). |
| UpdateAggregates | Compute aggregates (min, max, avg) over historical data. |
Subscriptions & Monitored Items
| Service | Semantics |
|---|---|
| CreateSubscription | Client creates a subscription; server begins monitoring. |
| ModifySubscription | Adjust publishing interval, deadband, etc. |
| DeleteSubscription | Stop monitoring. |
| CreateMonitoredItem | Add a variable to a subscription’s watch list. |
| ModifyMonitoredItem | Change threshold, sampling interval. |
| DeleteMonitoredItem | Stop monitoring a specific variable. |
| Publish | Server pushes monitored item changes to client (async). |
Method Invocation
| Service | Semantics |
|---|---|
| Call | Invoke a method with input arguments; receive output. |
Query & Attributes
| Service | Semantics |
|---|---|
| QueryFirst, QueryNext | Execute a query against the Address Space. |
| ReadAttributes | Fetch node attributes (DisplayName, Description, etc.). |
| WriteAttributes | Modify attributes (rarely used; most attributes are read-only). |
Transport Stack

Encoding Layers:
- Application Layer: OPC UA services (Read, Write, Browse, etc.).
- Secure Channel Layer: Message security (Sign/Encrypt), sequence numbers, certificate validation.
- Transport Encoding: UA-Binary (compact), UA-JSON (readable), UA-WebSocket (browser-native).
- Network Layer: TCP/IP (port 4840), HTTPS (port 443), MQTT (broker-defined), UDP multicast (PubSub).
UA-Binary Encoding:
Binary format is the default, wire-efficient. A Read service request (~20 bytes) encodes as:
[Request Header] [Service Body]
TxnID (4) NodeId (4) | ReadValueId (12+)
Sequence (4)
RequestHandle (4)
UA-JSON Encoding:
Human-readable alternative (useful for proxies, logging). Same service contract; just JSON serialization instead of binary:
{
"RequestHeader": {
"RequestHandle": 1,
"Timestamp": "2026-04-18T10:00:00.000Z"
},
"NodesToRead": [
{
"NodeId": "ns=2;s=Pump_01.Pressure",
"AttributeId": 13
}
]
}
UA-WebSocket Transport:
Wraps UA-Binary or UA-JSON in WebSocket frames; enables client-side JavaScript to communicate directly with OPC UA servers.
Transport Patterns:
| Pattern | Use Case | Latency | Determinism |
|---|---|---|---|
| TCP Client/Server | Request-response polling, dashboards, legacy SCADA. | 10–100 ms | Non-deterministic (TCP retransmit). |
| HTTPS Client/Server | Firewalled environments, cloud gateways. | 50–500 ms | Non-deterministic (TLS overhead). |
| UDP PubSub | Field-level pub/sub, edge devices, low-latency streams. | 1–10 ms | Non-deterministic (UDP loss, in-order not guaranteed). |
| MQTT PubSub | Cloud-connected devices, MQTT brokers as message hub. | 10–100 ms | Non-deterministic (depends on broker). |
| TSN (Time-Sensitive Networking) | Deterministic, real-time, industrial Ethernet. | <1 ms jitter | Deterministic (scheduled slots). |
PubSub and OPC UA FX
OPC UA originally mandated Client/Server (request-response). Modern deployments increasingly adopt PubSub for event-driven, low-latency scenarios.
OPC UA PubSub Model
Publisher emits data on a Publisher Endpoint. Subscribers listen. No server polls; no client request. This is asynchronous, event-driven.

PubSub Transports:
| Transport | Mechanism | Typical Use |
|---|---|---|
| UDP Multicast | Publisher → UDP group (224.0.0.1:4840) | Local LAN, multiple subscribers, no broker dependency. |
| MQTT | Publisher → MQTT broker → Subscribers | Cloud, remote sites, firewall-friendly. |
| UADP | OPC UA Data Protocol (binary, optimized for efficiency) | Field devices, bandwidth-constrained. |
| MQTT Sparkplug B | MQTT payload structure for IIoT (topic namespace, death certificates, type hints) | Industry 4.0 unified namespace. |
Subscription Model in PubSub:
Publisher
├─ DataSetWriter_1 (e.g., Pump_01 pressure, flow, status)
└─ DataSetWriter_2 (e.g., Motor_01 speed, torque)
Subscriber
├─ DataSetReader_1 (subscribes to Publisher's DataSetWriter_1)
└─ DataSetReader_2 (subscribes to Publisher's DataSetWriter_2)
A DataSetWriter batches multiple variables (e.g., Pump_01 attributes) into a single message. A DataSetReader consumes those batches. This batching reduces network traffic and CPU overhead.
OPC UA FX (Field Exchange)
OPC UA FX (2024 spec update) targets deterministic, time-synchronized field scenarios. Key features:
- Multi-Publisher/Multi-Subscriber: Unlike traditional client/server, multiple devices can publish and subscribe to the same data.
- TSN Integration: Schedules messages in deterministic time slots (IEEE 802.1Qbv).
- No Request-Response Overhead: Pure pub/sub, no polling latency.
- FX Controllers: Lightweight devices that handle FX messaging natively.
Example FX Deployment:
PLC_1 (FX Controller)
├─ Publishes: Motor_1 speed, position
└─ Subscribes to: Sensor_1 temperature
PLC_2 (FX Controller)
├─ Publishes: Sensor_1 temperature
└─ Subscribes to: Motor_1 speed
Gateway (TSN Switch)
├─ Schedules Motor_1 publishes → Slot 1–10 µs
├─ Schedules Sensor_1 publishes → Slot 11–20 µs
└─ Ensures <100 µs latency, zero jitter
FX vs Traditional OPC UA Client/Server:
| Aspect | Client/Server (TCP) | FX (TSN) |
|---|---|---|
| Initiator | Client pulls (polls) | Data-driven (publish happens at fixed time) |
| Latency | 10–100 ms (polling overhead) | 100–1000 µs (deterministic slot) |
| Scaling | Single server → 500+ clients | N publishers, N subscribers (no server bottleneck) |
| Synchronization | Loose (clock skew tolerated) | Tight (PTP/IEEE 1588 enforced) |
FX is not a replacement for traditional OPC UA; it is a complement for high-frequency, real-time field scenarios (motion control, disturbance rejection, synchronous manufacturing).
Security Model
OPC UA security operates at two levels: Secure Channel (transport-level) and Message Security (per-message integrity and confidentiality).
Secure Channel Establishment

Step 1: Client Hello
Client sends supported security policies (e.g., Basic256Sha256, Aes256_Sha256_RsaOaep), supported transports, certificate requirements.
Step 2: Server Hello
Server selects a policy (typically the strongest both support), returns server certificate and nonce.
Step 3: Handshake & Key Exchange
Client and server perform Diffie-Hellman (DH) key exchange to establish a shared secret. This secret is used to derive encryption and signing keys for all subsequent messages on the channel.
Step 4: Secure Channel Activated
All messages now apply:
- Sequence Number: Prevents replay attacks.
- Signing: HMAC-SHA256 over plaintext + sequence number (non-repudiation).
- Encryption: AES-256-CBC (confidentiality).
Security Policies
OPC UA defines predefined security policies (combinations of signing + encryption algorithms):
| Policy | Signing | Encryption | Key Size | Use Case |
|---|---|---|---|---|
| None | None | None | N/A | Testing only; no security. |
| Basic128Rsa15 | HMAC-SHA1 | AES-128 | RSA 1024 | Legacy; deprecated. |
| Basic256 | HMAC-SHA256 | AES-256 | RSA 2048 | Modern standard (2009–2020). |
| Basic256Sha256 | HMAC-SHA256 | AES-256 | RSA 2048 | Current best practice (2020+). |
| Aes256_Sha256_RsaOaep | HMAC-SHA256 | AES-256 | RSA 2048 | Latest (2023+). |
Recommendation: Use Basic256Sha256 or Aes256_Sha256_RsaOaep for new deployments; deprecate None and Basic128Rsa15.
User Authentication
Once a secure channel is established, the client authenticates the user (not just the channel) via:
| Method | Mechanism | Use Case |
|---|---|---|
| None | Anonymous access. | Internal networks, no sensitive data. |
| Username/Password | User sends username + password (hashed with session nonce). | Basic user accounts. |
| X.509 Certificate | Client proves identity via certificate (mutual TLS-style). | Service-to-service, high-security environments. |
The server issues a UserToken after successful authentication. This token is attached to all subsequent requests and operations are ACL’d based on the user’s role.
Certificate Management
Every OPC UA endpoint needs an X.509 certificate (self-signed or CA-signed). Best practices:
- Self-signed for dev/test: Quick to generate, but clients must explicitly trust.
- CA-signed for production: Clients trust the CA; no per-server manual approval.
- Renewal: Certificates expire; plan renewal before expiry (e.g., 2-year validity, renew at 1 year 11 months).
- Key Storage: Private keys must be protected (encrypted at rest, restricted file permissions).
OPC UA servers typically store certificates in:
/etc/opc-ua/certs/server.crt (public certificate)
/etc/opc-ua/private/server.key (private key, mode 0600)
/etc/opc-ua/trusted/ca.crt (trusted CAs)
/etc/opc-ua/rejected/untrusted.crt (rejected certificates, audit)
OPC UA vs OPC Classic vs MQTT Sparkplug
A practical comparison:
| Aspect | OPC Classic (DCOM) | OPC UA | MQTT Sparkplug B |
|---|---|---|---|
| Transport | DCOM RPC (Windows only) | TCP/HTTPS/UDP/MQTT | MQTT broker |
| Cross-Platform | Windows only | Windows, Linux, embedded, cloud | Any OS (MQTT client) |
| Security | None (clear text, no auth) | X.509 certs, AES-256, HMAC-SHA256 | TLS/SSL (broker-enforced) + username/password |
| Data Model | Opaque (VARIANT) | Semantic graph (Address Space, typed nodes) | Flat topic namespace + type hints (JSON) |
| Services | Read, Write, Async Notifications | 35 services (Browse, Call, Subscribe, etc.) | Publish/Subscribe only |
| Method Invocation | Limited | Full method call with input/output args | Not natively supported |
| Scalability | ~50 clients per server | 500+ clients per server + PubSub | Unlimited (broker scales) |
| Latency | 100–500 ms (DCOM RPC overhead) | 10–100 ms (TCP), 1–10 ms (UDP PubSub) | 50–200 ms (broker latency) |
| Real-Time Capability | Poor (TCP ACK overhead) | Good (TCP), Excellent (UDP/TSN) | Fair (MQTT jitter) |
| Industry Adoption | Legacy (pre-2015) | Standard (2015+); most new deployments | Rising (unified namespace, IIoT) |
| Learning Curve | Steep (DCOM, COM objects) | Steep (Address Space, services, security) | Shallow (MQTT pub/sub simplicity) |
| Interoperability | Vendor-locked | OPC Foundation standardized | MQTT standardized; Sparkplug B newer |
Verdict:
- OPC Classic: Avoid for new projects; legacy only.
- OPC UA: Gold standard for enterprise-field integration; required for PLCs, SCADA, brownfield modernization.
- MQTT Sparkplug B: Excellent for cloud-connected, loosely-coupled IoT; inferior for real-time field control and semantic navigation.
Edge Cases & Failure Modes
Certificate Trust & Validation Failures
Scenario: Client connects to OPC UA server with self-signed certificate. Client has not pre-trusted the certificate.
Outcome: Secure channel creation fails; client receives BadCertificateUntrusted.
Mitigation:
- Pre-share certificates: During commissioning, distribute server certificates to all clients.
- Trust on first use (TOFU): Client prompts operator to manually approve unknown certs (weak, but practical).
- CA-signed certs: Use a private CA trusted by all clients.
- Certificate pinning: Client stores server certificate fingerprint; rejects any cert not matching.
Information Model Bloat
Scenario: A manufacturer publishes a companion spec with 500+ node types. An integrator instantiates 10,000 objects in a single server.
Outcome: Address Space becomes unwieldy; Browse operations slow down (O(n) traversal); client discovery/caching becomes expensive.
Mitigation:
- Lazy loading: Server materializes nodes on-demand (not all at once).
- View filters: Server exposes a “filtered view” (e.g., only active pumps) instead of all nodes.
- Pagination: Browse service returns results in pages; client calls BrowseNext for subsequent pages.
- RegisterNodes: Client pre-caches node IDs, avoiding repeated Browse.
Subscription Fan-Out & CPU Overhead
Scenario: A SCADA server has 10,000 clients, each subscribing to 100 variables. On variable change, server must push notifications to all 10,000 subscribers.
Outcome: Server CPU saturates; publish messages pile up in queues; subscribers receive stale data.
Mitigation:
- Subscription deadbands: Variables only notify if change exceeds threshold (e.g., ±0.1 PSI for pressure).
- Sampling intervals: Subscribers specify minimum update interval (e.g., 100 ms); server batches updates.
- PubSub instead of subscriptions: Use UDP multicast or MQTT; server publishes once, OS/broker handles N-way delivery.
- Hierarchical architecture: Distribute subscribers across multiple edge servers (fan-out at edge, not at central server).
Legacy OPC DA → OPC UA Gateway Conflicts
Scenario: A facility has legacy OPC DA servers. An integrator deploys an OPC UA gateway that translates OPC DA → OPC UA address space.
Outcome: Gateway’s semantic model conflicts with facility’s expectations (e.g., node names differ, types unmapped).
Mitigation:
- Custom mapping: Configure gateway to translate OPC DA tag names to OPC UA node IDs explicitly.
- Companion spec alignment: Ensure gateway follows the facility’s chosen companion spec (PLCopen, ISA 95, etc.).
- Testing: Validate gateway’s Address Space against facility’s OPC UA client expectations before production.
Companion Specification Conflicts
Scenario: Facility uses both PLCopen and PackML companion specs. A single device (e.g., filling machine) should conform to both.
Outcome: Type inheritance conflicts; a PLC variable is both a PLCopen.INT and a PackML.RecipeIngredient.
Mitigation:
- Multiple inheritance: OPC UA supports multiple HasTypeDefinition references (one node, multiple type parents).
- Wrapper types: Create a facility-specific ObjectType that inherits from both specs’ base types.
- Documentation: Clearly document which nodes map to which specs; provide mapping guides for integrators.
Implementation Guide
Scenario: Build a Simple OPC UA Server & Client
We’ll implement:
- Server: Exposes a Pump object with Pressure, Flow variables and a Start method.
- Client: Connects, browses the address space, reads pressure, subscribes to changes, calls Start.
Using open62541 (C)
open62541 is the most mature, production-grade library. It’s used in industrial gateways, embedded devices, and SCADA servers.
Server (C):
#include <open62541/server.h>
#include <open62541/server_config_default.h>
#include <signal.h>
#include <stdlib.h>
volatile sig_atomic_t running = 1;
void signalHandler(int sig) {
running = 0;
}
int main(void) {
signal(SIGINT, signalHandler);
signal(SIGTERM, signalHandler);
UA_Server *server = UA_Server_new();
UA_ServerConfig_setDefault(UA_ServerConfig_new_default());
UA_Server_setServerConfig(server, UA_ServerConfig_new_default());
// Create a Pump object in the Objects folder
UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
oAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Pump_01");
UA_NodeId pumpNodeId = UA_NODEID_STRING(1, "Pump_01");
UA_Server_addObjectNode(
server,
pumpNodeId,
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
UA_QUALIFIEDNAME(1, "Pump_01"),
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
oAttr,
NULL,
NULL
);
// Add Pressure variable
UA_VariableAttributes vAttr = UA_VariableAttributes_default;
vAttr.description = UA_LOCALIZEDTEXT("en-US", "Pump pressure in PSI");
vAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Pressure");
vAttr.dataType = UA_TYPES[UA_TYPES_DOUBLE].typeId;
vAttr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
UA_Double pressureValue = 102.5;
UA_Variant_setScalar(&vAttr.value, &pressureValue, &UA_TYPES[UA_TYPES_DOUBLE]);
UA_NodeId pressureNodeId = UA_NODEID_STRING(1, "Pump_01.Pressure");
UA_Server_addVariableNode(
server,
pressureNodeId,
pumpNodeId,
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
UA_QUALIFIEDNAME(1, "Pressure"),
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
vAttr,
NULL,
NULL
);
// Start server
UA_StatusCode retval = UA_Server_run(server, &running);
UA_Server_delete(server);
return (int) retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}
Client (C):
#include <open62541/client.h>
#include <open62541/client_config_default.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
UA_Client *client = UA_Client_new();
UA_ClientConfig_setDefault(UA_ClientConfig_new_default());
UA_Client_setStateCallback(client, NULL, NULL);
// Connect to server
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
if (retval != UA_STATUSCODE_GOOD) {
UA_Client_delete(client);
return (int)retval;
}
// Browse ObjectsFolder
UA_BrowseRequest bReq;
UA_BrowseRequest_init(&bReq);
bReq.requestedMaxReferencesPerNode = 0;
bReq.nodesToBrowse = UA_BrowseDescription_new();
bReq.nodesToBrowseSize = 1;
bReq.nodesToBrowse[0].nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
bReq.nodesToBrowse[0].direction = UA_BROWSEDIRECTION_FORWARD;
UA_BrowseResponse bResp = UA_Client_Service_browse(client, bReq);
printf("Browse ObjectsFolder:\n");
for (size_t i = 0; i < bResp.resultsSize; ++i) {
for (size_t j = 0; j < bResp.results[i].referencesSize; ++j) {
UA_ReferenceDescription *ref = &bResp.results[i].references[j];
printf(" %s\n", (char *)ref->displayName.text.data);
}
}
UA_BrowseResponse_deleteMembers(&bResp);
// Read Pressure variable
UA_ReadRequest rReq;
UA_ReadRequest_init(&rReq);
rReq.nodesToRead = UA_ReadValueId_new();
rReq.nodesToReadSize = 1;
rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "Pump_01.Pressure");
rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;
UA_ReadResponse rResp = UA_Client_Service_read(client, rReq);
if (rResp.resultsSize > 0 && rResp.results[0].hasValue) {
UA_Double *pressureValue = (UA_Double *)rResp.results[0].value.data;
printf("Pressure: %.2f PSI\n", *pressureValue);
}
UA_ReadResponse_deleteMembers(&rResp);
// Disconnect
UA_Client_disconnect(client);
UA_Client_delete(client);
return EXIT_SUCCESS;
}
Compile & Run:
# Install open62541 (Ubuntu)
sudo apt-get install libopen62541-0 libopen62541-dev
# Compile server
gcc -o server server.c -lopen62541 -lmbedcrypto
# Compile client
gcc -o client client.c -lopen62541 -lmbedcrypto
# Terminal 1
./server
# Terminal 2
./client
Using python-opcua (Python)
python-opcua is easier to learn; great for prototyping and scripts.
Server (Python):
from opcua import Server
from opcua.common import objtypes
import time
from threading import Thread
server = Server()
server.set_endpoint("opc.tcp://localhost:4840/")
# Define namespace
ns = server.register_namespace("http://mycompany.com/pump")
# Create Pump object
pump = server.nodes.objects.add_object(ns, "Pump_01")
# Add Pressure variable
pressure = pump.add_variable(ns, "Pressure", 102.5)
pressure.set_writable()
pressure.set_attr_bit(0, True) # Historizing
# Add Flow variable
flow = pump.add_variable(ns, "Flow", 45.3)
flow.set_writable()
# Add Start method
def start_pump(parent, *args):
print("Pump started!")
return [True]
start_method = pump.add_method(ns, "Start", start_pump)
# Start server in background thread
def run_server():
with server:
server.start()
try:
while True:
time.sleep(0.1)
except KeyboardInterrupt:
pass
server_thread = Thread(target=run_server, daemon=True)
server_thread.start()
# Simulate pressure changes
try:
while True:
import random
new_pressure = 100.0 + random.uniform(-5, 5)
pressure.set_value(new_pressure)
time.sleep(2)
except KeyboardInterrupt:
print("Server stopped")
Client (Python):
from opcua import Client
from opcua.ua import VariantType
import time
client = Client("opc.tcp://localhost:4840/")
client.connect()
print("Connected to OPC UA server")
# Browse Objects folder
root = client.nodes.root
print(f"Root node: {root}")
objects = client.nodes.objects
print(f"Objects: {objects}")
# Browse Pump_01
pump = objects.get_child(["0:Pump_01"])
print(f"Pump_01 children:")
for child in pump.get_children():
print(f" {child.get_display_name()}")
# Read Pressure
pressure_node = pump.get_child(["0:Pressure"])
pressure_value = pressure_node.get_value()
print(f"Pressure: {pressure_value} PSI")
# Subscribe to Pressure changes
class PressureHandler:
def datachange_notification(self, node, val, data):
print(f"Pressure changed: {val}")
handler = PressureHandler()
sub = client.create_subscription(100, handler) # 100 ms publish interval
sub.subscribe_data_change(pressure_node)
# Let subscription run for 10 seconds
print("Listening for pressure changes (10 seconds)...")
time.sleep(10)
# Call Start method
print("Calling Start method...")
result = pump.call_method("0:Start")
print(f"Start result: {result}")
# Cleanup
sub.unsubscribe_data_change(pressure_node)
client.disconnect()
Run:
pip install opcua
# Terminal 1
python server.py
# Terminal 2
python client.py
Output:
Connected to OPC UA server
Root node: Node(ns=0;i=84)
Objects: Node(ns=0;i=85)
Pump_01 children:
Pressure
Flow
Start
Pressure: 102.5 PSI
Listening for pressure changes (10 seconds)...
Pressure changed: 103.2
Pressure changed: 98.1
Pressure changed: 101.5
Calling Start method...
Start result: [True]
FAQ
Q1: What’s the difference between OPC UA and OPC DA?
A: OPC Classic (OPC DA / AE / HDA) is Windows-only, DCOM-based, and has no security. OPC UA is cross-platform, transport-agnostic, and has built-in X.509 certificates and AES-256 encryption. OPC Classic is deprecated; OPC UA is the standard.
Q2: Is OPC UA free?
A: Yes. OPC UA specifications are open and free. Libraries like open62541 and python-opcua are open-source. Commercial stacks (Siemens TIA, BECKHOFF, OSIsoft) charge licensing fees, but the protocol itself is royalty-free.
Q3: What’s a Companion Specification?
A: A Companion Specification extends the base OPC UA information model with domain-specific types. Examples: PLCopen (PLC variables), PackML (packaging), ISA 95 (manufacturing operations). Companion specs ensure semantic interoperability within a domain.
Q4: Can OPC UA run on PLCs?
A: Yes. Modern PLCs (Siemens S7-1200+, BECKHOFF CX, B&R X20) support OPC UA natively. Older PLCs require a gateway. Embedded OPC UA stacks (open62541) are available for microcontrollers (ARM, x86 embedded).
Q5: Is OPC UA slow?
A: No. OPC UA over TCP achieves 10–100 ms latency (typical for SCADA polling). OPC UA PubSub over UDP or TSN achieves 1–10 ms latency (competitive with Modbus RTU over RS-485 and faster than MQTT). The “slowness” myth stems from OPC Classic (DCOM overhead); OPC UA is efficient.
Where OPC UA Is Heading
OPC UA FX (Field Exchange, 2024+)
FX is the next evolution, targeting deterministic, real-time field scenarios. Multi-publisher/subscriber over TSN (IEEE 802.1Qbv) eliminates client/server polling overhead.
Status: Ratified 2024; early adoption by automotive (real-time motion control) and discrete manufacturing.
Cloud-Native OPC UA
Trend: OPC UA servers and gateways moving to Kubernetes, containers. Open62541 runs in Docker; Siemens MindConnect, OSIsoft PI System have cloud editions.
Future: OPC UA brokers (Kafka-like) for Enterprise-to-cloud data pipelines; standardized schema for OPC UA → Parquet/Arrow; OPC UA MQTT bridge as first-class citizen.
TSN Convergence
Trend: Time-Sensitive Networking (IEEE 802.1Qbv) adoption in industrial Ethernet switches. OPC UA FX leverages TSN for deterministic scheduling.
Future: TSN-enabled PLCs and edge devices communicating directly; OPC UA as the semantic layer, TSN as the scheduling layer. Unified architecture for real-time + IT convergence.
References
OPC Foundation Specifications (IEC 62541)
- IEC 62541-1:2020 — Overview and Concepts
- IEC 62541-2:2020 — Security Model
- IEC 62541-3:2020 — Address Space Model
- IEC 62541-4:2020 — Services
- IEC 62541-5:2020 — Information Model
- IEC 62541-6:2020 — Mappings (UA-Binary, UA-JSON, UA-WebSocket)
- IEC 62541-7:2020 — Profiles (Profiles define which services are mandatory/optional)
- IEC 62541-10:2020 — Client/Server (Traditional request-response)
- IEC 62541-14:2024 — PubSub (Publish/Subscribe)
- OPC UA FX Specification (Part 15) — Field Exchange (2024, TSN integration)
Implementation Libraries
- open62541 (C): https://github.com/open62541/open62541 — Production-grade, embeddable, used in gateways and PLCs.
- python-opcua (Python): https://github.com/FreeOpcUa/python-opcua — Easy prototyping, testing.
- node-opcua (JavaScript): https://github.com/node-opcua/node-opcua — Browser and Node.js clients.
- .NET OPC UA Stack (C#): Microsoft/OPC Foundation official library.
Companion Specifications
- PLCopen OPC UA: PLC variable standardization (IEC 61131-3 integration).
- PackML OPC UA: Packaging machine states and diagnostics.
- ISA 95 OPC UA: Manufacturing operations (recipes, production).
- IEC 61850 OPC UA: Power systems (circuit breakers, transformers, protection relays).
Further Reading
- “OPC UA – A Practical Introduction to the Unified Architecture” — Technische Universitaet Graz (free course materials).
- OPC Foundation GitHub: https://github.com/OPCFoundation — Specs, test suites, reference implementations.
- “Real-Time Communication for Industrial IoT” — IEEE IES Magazine (TSN + OPC UA integration patterns).
Related Posts
- OPC UA FX: Field Exchange Reference Architecture for Deterministic Industrial Control (2026)
- OPC UA vs MQTT Sparkplug B: Semantic Models Meet Unified Namespace
- Unified Namespace Architecture for Industrial IoT: Hub-and-Spoke to Event-Driven
- MQTT: Message Queuing Telemetry Transport Protocol Complete Guide
Last Updated: April 18, 2026
