OPC UA Integration
The four decisions that decide whether an OPC UA integration holds: a published companion specification or a custom nodeset; a decoder that reads structures instead of storing opaque bytes; subscriptions and queues sized against a server that revises what you ask for; certificates and roles that are administered rather than coded. Around them the northbound leg, the historian mapping and the gateway question.
- A published companion model beats a private tag list Adopt or extend a cataloged model, and keep custom nodesets for domains nothing published covers.
- An unrecognized structure can be skipped as opaque data Read the DataTypeDefinition attribute and alarm on what the decoder cannot read rather than storing bytes.
- The server revises the intervals you ask for Log the revised values, size queues for a full cycle and alarm on the Overflow bit that marks dropped samples.
- Trust is administered, not coded Each client certificate is an administrator action on every server it will reach, which is what drives the topology choice.
- PubSub carries fan-out, client and server carry the model A publisher's cost does not grow with consumers, while every extra session adds load, certificates and firewall rules.
OPC UA integration is the point where an MES or an IIoT platform stops reading tags and starts reading a model. A server publishes an address space, and the platform decides which parts of it to subscribe to, how to decode what comes back, what happens when the session drops and whose certificate the server accepts. The OPC Foundation puts the purpose of the standard in one line in its Part 1 overview: "OPC UA is a platform-independent standard that enables various systems and devices to communicate securely and reliably." The services and the modeling rules are specified. Which of them your integration uses, and under whose identity, is work the standard leaves to you.
In short: four decisions carry most of the risk. Whether to adopt a published companion specification, extend one by subtyping or define a custom nodeset. Whether your client decodes the structured data types a server publishes or quietly stores them as bytes. How subscriptions and MonitoredItems are sized, since the server revises the intervals you request and drops notifications when a queue overflows. And how certificates, trust lists and roles are administered, which is an operational process before it is a code path. The decision table adds the three that follow from those four: the northbound leg, the historian mapping and the gateway question.
Where OPC UA sits in an MES or IIoT platform
OPC UA is the interoperability layer between control equipment and the systems above it. The OPC Foundation's Unified Architecture page describes a framework rather than a protocol on its own: "The OPC Unified Architecture (UA), released in 2008, is a platform independent service-oriented architecture that integrates all the functionality of the individual OPC Classic specifications into one extensible framework."
Two interaction models live in the same standard. Part 1, clause 4.2 records the second: "In addition to the ClientServer model, OPC UA also supports information transfer from Publishers to Subscribers using the PubSub model." In Pharos Production practice the plant-floor leg is client/server, because that is the model in which a server offers sets of Services for a client to call, browsing the address space and reading history among them. Our manufacturing software development guide covers MES scope and the shop-floor systems around it, so this page stays on the protocol decisions.
Integration decisions and their failure modes
This table is the short form of the whole guide, and each row is written to stand on its own. Its options column is sourced from the specification parts cited on this page, while the choose-when column is Pharos Production practice rather than a requirement of the standard. Every section after the table takes one row and works through it.
| Integration decision | Options (sourced) | Choose when (our practice) | Failure mode |
|---|---|---|---|
| Information model, the source of a value's meaning | Adopt a published companion specification, extend one by subtyping or define a custom nodeset | Adopt where a published model covers the asset class; extend where it is close; go custom only as a last resort | The server exposes base types only, so the client receives values without meaning |
| Type discovery, how a client learns what a node is | Browse type definitions at runtime, pin node identifiers from a NodeSet2 file or resolve instances by browse path | Browse for a mixed estate; pin identifiers where the model is fixed; resolve paths where instances repeat | Pinned identifiers break on a firmware update, and a NodeSet2 file pins the base model version it was built against |
| Structure handling, what a client does with a structured value | Read the DataTypeDefinition attribute and decode structures, or treat unknown structures as opaque bytes | Decode wherever the server publishes the definition; accept opaque only for storage nobody queries | An unrecognized encoding identifier may be skipped as opaque data, so the historian stores a blob and the field is lost |
| Sampling and publishing rates, how often a value is read and sent | Inherit the publishing interval or set a per-item sampling interval | Inherit for slow process values; override for fast interlocks or a fixed historian cadence | The server supports a limited set of intervals and revises the request, so the configured number is not the delivered one |
| Queue sizing and loss policy, what survives a burst | Queue size of one, or a queue sized for a full publishing cycle, discarding the oldest notification or replacing the last one added | Size one for a display value; size for the cycle where the consumer must not have gaps | Silent gaps. The Overflow bit is the only signal that samples were dropped, and a queue of one never sets it |
| Connection resilience, what happens after a session drops | Republish from the retransmission queue, transfer the subscription to a new session or rebuild it | Republish for a short gap; transfer after a session loss the subscription survived; rebuild otherwise | The subscription closes once the lifetime counter expires, and an empty availableSequenceNumbers array means no republish at all |
| Northbound leg, the path from the platform to analytics | OPC UA client/server to the platform, or PubSub over MQTT to a broker | Client/server where the platform must browse the address space and call the Services a server provides; PubSub where many consumers read one stream | A client/server fan-out grows the server's cost with every consumer, which the publisher model avoids |
| Identity, the application and the user behind a session | Self-signed or authority-issued application instance certificates, with permissions expressed through roles and the user token type the server's UserTokenPolicy offers, anonymous included | Self-signed for a handful of fixed endpoints; anonymous only on a closed cell network with no write path | A certificate must be marked as trusted before use, and an anonymous token type leaves nobody to authorize and nobody to audit |
Companion specification or custom information model
Does the meaning of your data come off the shelf? The OPC Foundation explains why published models exist on its companion specifications page: "OPC UA Companion Specifications are developed for various reasons: To publish specific information models (e.g., for specific industries, specific devices, specific use cases) To specify how to use OPC UA in specific environments." Without one, every machine is a private tag list that somebody maps by hand, and the mapping becomes the integration.
Models are never written from nothing. The same page states the derivation rule: "New Information Models can be created based on the OPC UA Data Model and eventually derived from OPC UA Base Information Models." It also states the payoff: "The synergy of the OPC UA infrastructure to exchange such industry information models enables interoperability at the semantic level." Two servers implementing one companion specification answer the same browse with the same type, so a single client works against both.
Published models are cataloged and versioned. The OPC UA Online Reference lists the core parts and the published companion specifications with their current versions. OPC UA for Machinery Part 1: Basic Building Blocks appears as OPC 40001-1 at release 1.04.1 under the namespace http://opcfoundation.org/UA/Machinery/, alongside OPC UA for PackML (OPC 30050) and the ISA-95 pair, OPC 10030 and OPC 10031-4. Read the catalog before commissioning a custom model.
Where a published model is close but incomplete, extend it instead of replacing it. Part 3, clause 4 states what subtyping buys the client: "In other words, subtypes reflect the structure defined by their supertype but may add additional characteristics." The mechanics for a vendor-specific addition are equally concrete: "The vendor would do this by creating a new VariableType which is a TargetNode for a HasSubtype reference from the original VariableType and adding the new Property to it." A client that knows only the base type keeps working; one that knows the extension gets more. What you may not invent is new machinery, since "No other NodeClasses shall be used to define Nodes", and clients and servers are not allowed to define NodeClasses or extend the ones that exist. The failure at the other end is a server that falls back on base types everywhere and hands you values without meaning.
Published models travel as NodeSet2 files, kept in the Foundation's UA-Nodeset repository. Each file declares its own model URI, version and publication date and names the base model version it was built against. The Machinery nodeset on the default branch declares Machinery 1.04.1 against base model 1.05.02, three releases behind the 1.05.06 core text on the reference site. That is a property of the file rather than a statement about the standard. A related trap: the core parts do not share one version number even though they share a URL path, with Part 4 and Part 6 at 1.05.07 while Part 1, Part 2 and Part 3 sit at 1.05.06 in the Online Reference catalog, so write the part number whenever you write a version.
Reading structures without losing fields
Scalars are solved. Structured types break naive clients, and they carry most of what a machine means: a batch record, a part-count block, a fault descriptor with a code and a timestamp. Part 3, clause 5.8 is careful about what a client can rely on: "Servers should attempt to expose the DataType Nodes and the information about the structure of those DataTypes for Clients to read", and the same sentence allows that this information might not always be available to the server.
Knowing where that description lives is the difference between writing a decoder once and reconstructing it from packet captures. Clause 5.8 states that "Structured DataTypes may have several encodings and the encodings are exposed in the AddressSpace", and that the metadata sits on one attribute: "The DataTypeDefinition Attribute is used to provide the meta data and encoding information for DataTypes". For a structure "the Attribute shall contain a structure of the DataType StructureDefinition", which is what your decoder needs.
On the wire a structure travels as an ExtensionObject, and decoder behavior when the type is unknown is normative. Part 6 gives the framing, "An ExtensionObject is encoded as sequence of bytes prefixed by the NodeId of its DataTypeEncoding", and the first step: "When a decoder encounters an ExtensionObject it shall check if it recognizes the DataTypeEncoding identifier." If it does not, then "it shall use the Encoding to determine if the body is a ByteString or an XmlElement and then decode the object body or treat it as opaque data and skip over it". That is the mechanism behind the silent loss we see most often in an OPC UA integration: the historian stores a byte string, every pipeline metric stays green and the field nobody checked was never there.
Our practice here is a rule rather than a preference. A client that cannot decode an ExtensionObject raises an alarm instead of storing the bytes, because a blob in a time-series store looks like a healthy row until an analyst needs the field. Reading DataTypeDefinition at connect time and caching the definitions per namespace and per server leaves a versioned artifact you can diff after a firmware update.
Subscriptions and MonitoredItems
Most of an integration's runtime cost sits here, and most of its data-quality bugs too. Part 4, clause 5.13 reduces a MonitoredItem to four knobs: "These parameters are the sampling interval, the monitoring mode, the filter and the queue parameter." Everything below is one of those four set carelessly, starting with the one that inherits: "Each MonitoredItem created by the Client is assigned a sampling interval that is either inherited from the publishing interval of the Subscription or that is defined specifically to override that rate." It also fixes the meaning: "The sampling interval indicates the fastest rate at which the Server should sample its underlying source for data changes." Fastest rate, not guaranteed rate. A signal that changes between two samples changes without you seeing the intermediate value.
Whatever you request, the server decides. Clause 5.13 is explicit: "It is expected that Servers will support only a limited set of sampling intervals to optimize their operation." The standard suggests reusing what comes back: "The Client may use the revised sampling interval values as a hint for setting the publishing interval as well as the keep-alive count". An integration that logs the requested interval and never the revised one documents a configuration it does not have.
Subscriptions are the clock those items run against. Part 4, clause 5.14 defines it: "The publishing interval of a Subscription defines the cyclic rate at which the Subscription executes." One number then governs two behaviors, because "The publishing interval also defines the default sampling interval for its MonitoredItems". Tune it for delivery latency and you have retuned every inheriting item, which is why one routine change to that number can multiply the sampling work a server does.
Queues decide whether data survives a burst, and loss is detectable only above a queue size of one. When there is no room left, "When the queue is full and a new Notification is received, the Server either discards the oldest Notification and queues the new one, or it replaces the last value added to the queue with the new one." The detection rule in clause 5.13 is precise: "If a Notification is discarded for a DataValue and the size of the queue is larger than one, then the Overflow bit (flag) in the InfoBits portion of the DataValue statusCode is set." A feed that ignores the status code cannot tell a quiet process from a dropped sample. Our default is to size the queue for a full publishing cycle at that signal's fastest plausible change rate, set discardOldest to false where the value means a state and to true where it means a measurement, then alarm on the Overflow bit.
Filters cut traffic and can cut information. Part 4, clause 7.22 defines the change filter: "The DataChangeFilter defines the conditions under which a DataChange Notification should be reported and, optionally, a range or band for value changes where no DataChange Notification is generated." The absolute deadband is plain: "For this type the deadbandValue contains the absolute change in a data value that shall cause a Notification to be generated." A deadband on a noisy analog measurement is good engineering. The same deadband on a discrete signal the MES waits for is a bug that presents as a quiet line.
Sessions end, and two counters decide how an integration survives that. Clause 5.14 defines the first: "Subscriptions have a keep-alive counter that counts the number of consecutive publishing cycles in which there have been no Notifications to report". The second is the dangerous one: "Subscriptions have a lifetime counter that counts the number of consecutive publishing cycles in which there have been no Publish requests available to send a Publish response", and "When this counter reaches the value calculated for the lifetime of a Subscription" the subscription is closed. A client that stops sending Publish requests during a rolling redeploy loses its subscriptions without anyone touching the network.
Recovery has mechanisms, and none of them is a policy. Republish resends from the retransmission queue, since "This Service requests the Subscription to republish a NotificationMessage from its retransmission queue". Whether it exists at all is visible in the response, because "An empty array in availableSequenceNumbers indicates that the Server does not support a retransmission queue". A subscription can also move, since the identifier is server-wide "in order to allow the Subscription to be transferred to another Session using the TransferSubscriptions service". All three sit in clause 5.14, and which path you take after a disconnect is your design decision.
Scale changes the arithmetic, and the question usually arrives as whether you can simply subscribe to everything at once, a subscription of, say, five thousand tags. The specification describes server limits as existing and as revised against what a client requests, and gives no figure for them in the parts cited here, so measure the target server rather than trusting a datasheet. Splitting one subscription into several by cadence rather than by machine is our default, since it keeps slow signals off a fast interval.
Security from certificates to roles

Two identities are in play on every connection: the application and the user. Part 4, clause 6.1 starts with the application: "In addition, each ApplicationInstanceCertificate has a private key which should be stored in a location that can only be accessed by the application." A key baked into a container image is a key shared by every replica of it.
Trust is a decision rather than a property of a certificate. Clause 6.1 states the rule bluntly: "Applications shall never communicate with another application that they do not trust." It also fixes the default posture: "OPC UA Applications shall be configured to reject connections with applications that do not have a trusted Certificate".
Commissioning day surprises teams with a manual step. Part 4 puts it directly: "ApplicationInstanceCertificates shall not be used in a Client or Server until they have been evaluated and marked as trusted." Connecting a client is an event on every server it will talk to, not a deployment event.
Part 2, clause 4 names the actor: "TrustLists are implemented as a CertificateStore designated by an administrator", and "An administrator determines if the Certificate is signed, validated and trustworthy before placing it in a TrustList". Mechanisms are selected as a set, since "A SecurityPolicy specifies which security mechanisms are to be used and are derived from a Security Profile (see 4.7 for details)." Record the policy and the message security mode beside the endpoint URL, because neither is visible from the URL itself.
User identity is separate, and it is the part most projects skip. Part 4, clause 7.41 describes what skipping it means: "A tokenType of ANONYMOUS indicates that the Server does not require any user identification." An anonymous session gives the server nothing to authorize and gives your auditors nothing to read.
What that costs is stated in Part 18, clause 4, where the role model exists to keep the two apart: "Roles are used to separate authentication (determining who a Client is with a user token and Client application identity)" from authorization, which is expressed as permissions on nodes. Our practice for an MES client is a named application identity plus a named user identity on a read-oriented role, with write permissions only on the nodes a use case touches.
Client server or PubSub for the northbound leg
The plant-floor leg and the analytics leg have different shapes, and forcing one mechanism onto both is how a server ends up holding one session per consumer, say forty of them. Client/server suits the leg where the platform calls the Services a server provides, browsing the address space and reading history among them, since that needs a request and a response. PubSub suits fan-out, where one stream feeds consumers that never ask the server anything.
Part 14 makes the cloud argument directly: "The use of established standard messaging protocols like MQTT with JSON data encoding supports the cloud integration path and readily allows handling of the information in modern stream and batch analytics systems." That is the northbound case in one sentence, and the overview is clause 4 of Part 14.
Scaling is why the choice exists at all. Part 1, clause 5 records that a publisher's effort and resource requirements do not depend on how many subscribers there are. A client/server fan-out inverts that: every additional consumer is another session, another set of subscriptions and another certificate in the server's trust list, and the cost lands on the machine least able to absorb it.
Broker choice, topic design and backpressure belong to the messaging layer rather than to OPC UA, and our IoT development guide covers MQTT and IIoT platform architecture. Our own default is client/server to each server for the model-aware work and PubSub over MQTT only where a stream has many consumers, with one internal event model behind both legs.
Mapping OPC UA data into a historian
Turning a notification into a row of a time-series store is Pharos Production practice rather than anything the parts cited here specify, so read this section as ours. The Foundation does claim the reach: "OPC UA provides the necessary infrastructure for interoperability across the enterprise, from machine-to-machine, machine-to-enterprise and everything in-between", as its Unified Architecture page puts it.
Key on identity that survives a firmware update. We treat a node identifier as valid only for the server that issued it and as something a reflash can move, so we key a series on the namespace URI plus the browse path plus a stable asset identifier from the plant model, and keep the node identifier as a resolved cache rather than as the key. Resolving browse paths at connect time avoids the failure where a maintenance visit repoints half the tags. Each value also arrives with a status code, and it belongs in the row next to whatever timestamps your client records: a bad or uncertain value written as a plain number is worse than a gap, because a gap is visible on a chart.
We carry a small fixed set of context tags for site, area, line and machine, resolved once from the plant model rather than parsed out of browse names. Before inventing a parallel hierarchy, check whether a companion specification in the catalog already models the asset class. Backfill belongs in the same component, so we treat the reconnect handler and the historical read as one thing with one watermark.
Gateway aggregation or direct connections
Certificate administration decides this question, earlier than most architectures expect. Direct connections mean the trust work grows with clients multiplied by servers, and each cell of that matrix is an administrator action on a machine that may sit behind a different network and a different owner. One aggregating component collapses the matrix into one certificate per server plus one endpoint for everything above.
Aggregation costs something real in exchange. The component becomes a single point of failure, it adds a hop that must preserve the source timestamp and the status code rather than re-stamping values with its own clock, and it is where somebody will eventually flatten the information model into a tag list.
Our rule is to stay direct while the server count is small, and to aggregate once the certificate matrix or the firewall rules need a spreadsheet. An aggregating component re-exposes the model rather than a tag list and keeps a per-source health view.
How Pharos Production helps
An OPC UA integration is four pieces of software and one operational process: the client and its model cache, the subscription manager that survives reconnects, the decoder that refuses to store what it cannot read, the mapping into the historian and the certificate administration on every server.
Our manufacturing software development practice covers that layer: the client and the decoder built against the servers an estate already runs, published companion models and machine-specific extensions kept behind one internal event model and the certificate workflow treated as part of the build rather than as a commissioning-day surprise.
Sources: the OPC UA Online Reference on reference.opcfoundation.org (Parts 1, 2, 3, 4, 6, 14 and 18, the specification catalog and the OPC 40001-1 Machinery page); the OPC Foundation pages on Unified Architecture and UA Companion Specifications; the OPCFoundation UA-Nodeset repository. Read on 16 September 2026. Engineering guidance, not legal advice.
FAQ
Quick answers to common questions about custom software development, pricing, process and technology.
Type to filter questions and answers. Use Topic to narrow the list.
Showing all 6
No matches
Try a different keyword, change the topic or clear filters
-
What if a machine has no OPC UA server at all?
Then the integration question moves one layer down, and what follows is our reading rather than anything the specification settles. Somebody has to publish an address space for that asset: the control system may offer one as an option nobody enabled, the machine may predate the protocol entirely, or its data may exist only on a serial line or in a file drop.
Decide early whether that machine gets a server of its own or stays outside the model, because a half-mapped asset becomes a permanent exception in every query written afterwards, and the exception outlives the project that created it.
-
Who approves an OPC UA client before it can connect?
An administrator does, on every server the client will reach. The specification puts trust lists in an administrator's hands and requires a certificate to be evaluated and marked as trusted before use, so the sequence around that is ours.
The integration team issues the client certificate and sends its thumbprint, the plant or OT owner reviews it against a change request, the entry goes into that server's trust list, and both sides verify by reading one known node. When a connection is refused, the rollback is to withdraw the trust-list entry and re-issue, never to relax the endpoint's security settings to get through the day.
-
How many MonitoredItems can one subscription handle?
No published figure answers that, because the limit belongs to the server rather than to the standard. The specification says such limits exist and that a server supports only a limited set of sampling intervals, revising what a client requests.
It also says the server returns a result code once it has reached its maximum number of subscriptions. So measure the target server: raise the item count in steps, record the revised intervals and watch for that result code. Splitting one large subscription into several grouped by cadence rather than by machine keeps slow signals off a fast publishing interval.
-
When do these integration rules not apply?
Our defaults have counter-conditions worth naming. Sizing a queue for a full publishing cycle wastes memory where the consumer only ever paints the latest value on a screen, and a queue of one is the right answer there.
Alarming instead of storing an undecodable structure is wrong where the feed is a raw archive nobody queries by field, so store the bytes and mark the row. Staying direct stops paying once the certificate matrix needs a spreadsheet, and aggregating stops paying where it would put one component between the plant and every consumer that depends on it.
-
When should an OPC UA integration use PubSub over MQTT?
When the same stream feeds several consumers or the leg reaches into cloud analytics. A publisher's effort does not depend on how many subscribers read it, while a client/server fan-out adds a session, a subscription set and a trust-list entry per consumer on the machine least able to absorb it.
MQTT with JSON encoding is the path the specification names for cloud integration and for stream and batch analytics. Keep client/server for the leg where the platform has to browse the address space, read history or otherwise call the Services a server provides, since those need a request and a response.
-
What proves an OPC UA integration is working?
A short acceptance checklist, run against the target servers rather than a bench rig. Every subscribed item delivers with its revised interval recorded beside the requested one.
Structures arrive decoded, with an alarm rather than a stored blob wherever a definition is missing. The Overflow bit is monitored and has been seen to fire under a deliberate burst. A forced session drop is followed by recovery without an operator, and the historian shows no gap across it. Each series still resolves after a simulated reflash. The checklist is ours rather than a conformance test; passing it is not certification.
I work with startup founders who need a dedicated software development team but don’t want to gamble on hiring, random outsourcing, or opaque delivery.
Most founders face the same problem sooner or later.
Early technical and team decisions lock the product into tech debt, slow delivery, missed milestones and constant re-hiring. By the time this becomes visible, fixing it is already expensive.As a CTO and software architect, I help founders design, build and run dedicated development teams that work as a true extension of the startup. Not as a black-box vendor.
My focus is on complex products where mistakes are costly:
- Web3 and blockchain platforms
- FinTech and regulated products
- High-load startup systems
- MVP → scale transitions
We don’t do body-shopping.
We don’t sell generic outsourcing.Instead, we help founders:
- build the right team structure from day one
- keep technical ownership and transparency
- scale delivery without losing control
- avoid vendor lock-in and hidden risks
Teams are aligned with the product roadmap, business goals and long-term architecture. Not just short-term velocity.