Building a fully compliant OPC UA server — from Companion Spec to brownfield
There are many OPC UA servers on the market. Most of them expose a flat list of tags. You get a value, a timestamp, and a quality code. That's useful — it's certainly better than nothing — but it misses the entire point of OPC UA.
A compliant OPC UA server doesn't just serve data. It serves an information model: typed objects, structured variables, engineering units, alarm conditions, methods with defined arguments, and relationships between assets. It implements industry-standard Companion Specifications so that any client — from any vendor — can understand the data without custom configuration.
Building that server is hard. Connecting it to a real factory — to PLCs that speak Modbus, S7, EtherNet/IP, or proprietary protocols — is harder still.
This article explains how to do both, and how modern tooling makes it configurable rather than codeable.
The three layers of a real OPC UA server
Every production OPC UA server has three layers. Most projects get the first one right and fail on the other two.
Layer 1: the information model
This is the semantic description of your equipment. Not a tag list — a model. It defines:
- Object types. What kinds of assets exist (pumps, conveyors, reactors, robots).
- Variable types. What data each asset exposes (temperature, speed, state, cycle count), with data types and engineering units.
- Method types. What operations can be performed (start, stop, reset, acknowledge alarm).
- Event types. What notifications the asset emits (alarm raised, state changed, batch completed).
- References. How assets relate to each other (Pump_3 is part of CoolingLoop_1, which is part of ProductionLine_A).
A Companion Specification provides the template. PackML tells you how to model a packaging machine's state machine. Euromap 77 tells you how to model an injection molding machine. PADIM tells you how to model a pressure transmitter.
Your job is to instantiate the Companion Spec for your specific equipment — your specific pumps, your specific conveyors, your specific line layout.
Layer 2: the driver layer (brownfield connectivity)
The information model describes what the data means. The driver layer describes where the data comes from.
In a greenfield deployment, you might have modern PLCs that already speak OPC UA. But in reality, most factories are brownfield environments with a mix of:
- Siemens S7 (S7-300, S7-400, S7-1200, S7-1500) — using the S7 protocol or PROFINET.
- Modbus TCP/RTU — the lingua franca of process instruments.
- EtherNet/IP (Allen-Bradley, Rockwell) — using CIP over Ethernet.
- BACnet — in building automation.
- MQTT — from IoT sensors and edge devices.
- CSV/REST/databases — from historians, SCADA, and legacy systems.
- Proprietary protocols — every vendor has one.
The driver layer connects to these heterogeneous sources and maps their raw data points to the semantic model. Register 40001 on a Modbus device becomes CoolingPump.DischargeTemperature with a type of Double, a unit of °C, and an engineering range of 20.0 – 65.0.
Layer 3: the OPC UA services layer
This is the OPC UA stack itself — the services that clients use to interact with the server:
- Browse. Clients discover the information model at runtime.
- Read/Write. Clients read process values and write setpoints.
- Subscribe. Clients receive real-time updates via monitored items.
- Call. Clients invoke methods on the server.
- Events. Clients receive structured alarm and event notifications.
- HistoryRead. Clients query historical data.
- Security. X.509 certificates, encryption, role-based access control.
A compliant server implements all of these correctly — not just Read and Subscribe.
Why most OPC UA servers fail
Flat tag lists
The most common failure: the server exposes a flat namespace of tags — Tag_001, Tag_002, Temperature_Pump3 — with no type information, no object hierarchy, no units, no relationships. This is essentially an OPC DA server wrapped in an OPC UA envelope. Clients can read values, but they can't understand the data.
Hardcoded models
Many OPC UA servers hardcode the information model in C++ or C#. Adding a new variable means recompiling and redeploying the server. Changing the model structure means months of development. This kills agility — factories need to adapt their models as equipment changes, new lines are added, and Companion Specs evolve.
No brownfield connectivity
Some servers have excellent OPC UA stacks but no way to connect to legacy equipment. They work great with other OPC UA servers, but they can't read a Modbus register or an S7 data block. In a brownfield factory, this makes them useless as an edge aggregation point.
Security as an afterthought
The server supports OPC UA security in theory, but in practice it's never enabled because certificate management is too complex. See our article on GDS for why this matters and how to solve it.
The configurable approach
The traditional way to build an OPC UA server is to write code: define types in C++, instantiate objects, implement data callbacks, handle subscriptions. This is powerful but expensive. Every change requires a developer.
The modern approach separates the three layers into configuration:
1. Model definition (YAML / NodeSet)
The information model is defined in a structured file — YAML, XML NodeSet, or a visual designer — not in application code. The model references Companion Specifications by importing their NodeSet files. Your custom types extend the Companion Spec types.
This means:
- A domain expert (not a programmer) can define the model.
- The model is version-controlled in Git.
- Changes are reviewed, diffed, and rolled back like code.
- The same model can be deployed to multiple servers.
2. Driver mapping (configuration)
The mapping between the information model and the brownfield data sources is defined in a configuration file. For each variable in the model, you specify:
- Source protocol (Modbus, S7, MQTT, OPC UA, CSV, …)
- Connection details (IP address, port, rack/slot for S7, unit ID for Modbus)
- Address (register number for Modbus, DB/offset for S7, topic for MQTT)
- Data type and transformation (scaling, byte swapping, bit extraction)
- Polling interval or event trigger
No code. A configuration change. When the factory adds a new PLC or replaces a transmitter, you update the mapping — not the software.
3. Server deployment (containerized)
The server runs as a container (Docker, Kubernetes, or a native service) with the model and driver configuration mounted as files. Updating the model is a configuration reload or container restart — not a recompilation.
A practical example
Consider a pharmaceutical production line with:
- 3 reactor vessels (Siemens S7-1500 PLCs, S7 protocol)
- 12 process instruments (Endress+Hauser, Modbus TCP)
- 1 packaging machine (Beckhoff, EtherNet/IP)
- 1 clean-in-place (CIP) skid (legacy Modbus RTU via serial gateway)
Step 1: Define the model.
Import the PADIM Companion Specification for process instruments, the ISA-88 BatchML model for reactor sequences, and PackML for the packaging machine. Define your specific reactor type by extending the BatchML reactor with your custom variables (agitator speed, jacket temperature, pH). Instantiate three reactors, twelve instruments, one packager, and one CIP skid.
Step 2: Configure the drivers.
Map Reactor_1's temperature setpoint to S7-1500 at IP 10.0.1.10, DB100, offset 0, type REAL. Map FlowMeter_7's measured value to Modbus TCP at IP 10.0.2.7, register 30001, type Float32, byte order BigEndian. Map Packager_1's state to EtherNet/IP at IP 10.0.3.1, CIP path 1/0/Assembly/100. Map CIP_Skid's step number to Modbus RTU via serial gateway at IP 10.0.4.1, unit ID 1, register 40010.
Step 3: Deploy.
Start the OPC UA server. Clients connecting to it see a fully browsable address space with typed objects, engineering units, alarm conditions, and methods — all backed by live data from four different protocols. The client doesn't know or care that the data comes from Modbus, S7, or EtherNet/IP. It sees OPC UA.
What compliance actually means
A "compliant" OPC UA server isn't just one that passes a certification test. In practice, compliance means:
| Aspect | What it means |
|---|---|
| Companion Spec conformance | The server's information model matches the Companion Specification — correct types, correct structure, correct NodeIds |
| Profile support | The server implements the required OPC UA profiles (e.g., Embedded DataChange Subscription, Standard UA Server) |
| Security | X.509 certificates, signed/encrypted communication, user authentication |
| Alarms & Events | Structured alarm conditions with acknowledge/confirm, not just boolean flags |
| Historical access | HistoryRead support for trending and compliance data |
| Browse | Full address space browsing — clients can discover the model at runtime |
| Methods | Server-side operations with defined input/output arguments |
| Diagnostics | Server diagnostics node, session diagnostics, subscription diagnostics |
Key takeaways
-
A flat tag list is not an OPC UA server. A real OPC UA server exposes an information model — typed objects with structure, units, and relationships.
-
Companion Specifications provide the templates. Your job is to instantiate them for your specific equipment.
-
Brownfield connectivity is the hard part. Your server must map semantic variables to Modbus registers, S7 data blocks, EtherNet/IP tags, and MQTT topics.
-
The modern approach is configuration, not code. Model in YAML, map drivers in config, deploy as containers.
-
Compliance is more than tags. It includes security, alarms, history, browse, methods, and diagnostics.
References
- OPC Foundation — OPC UA Part 5: Information Model. opcfoundation.org
- OPC Foundation — OPC UA Companion Specifications. opcfoundation.org/developer-tools/documents
- ISA — ISA-88 / PackML. isa.org
- FieldComm Group — PA-DIM. fieldcommgroup.org
- Euromap — Euromap 77/83. euromap.org
This is what we build at Sterfive. Omni-Edge is a fully configurable OPC UA server that implements Companion Specifications, connects to brownfield equipment via built-in drivers (S7, Modbus, MQTT, EtherNet/IP, and more), and deploys as a lightweight container. OPC UA Modeler lets you define the information model visually and export it as a NodeSet file that Omni-Edge consumes directly. No code required. Let's talk about your deployment.