What is a OPCUA NodeId
🔧 Mastering NodeIds in OPC UA: Power, Pitfalls, and Practical Alternatives
NodeIds are at the heart of OPC UA – they’re the backbone for identifying and addressing nodes such as Variables or Objects in the OPC UA server’s address space. Whether you’re reading values, writing data, calling methods or subscribing to events, NodeIds are the key that unlocks access.
But while powerful, they aren’t without limitations.
🧩 What is a NodeId?
In OPC UA, NodeId is a unique identifier assigned to every node (variables, methods, objects) in the address space. This ID allows clients to directly access the node, making operations like reading, writing, and calling methods possible.
A NodeId is composed of two parts:
-
Namespace Index (ns) – This is an integer representing the namespace in which the node resides. Namespaces help organize nodes and avoid conflicts between different vendors or models.
-
Identifier (i, s, g, b) – This part uniquely identifies the node within the namespace. The identifier can be numeric (i), string (s), GUID (g), or opaque (b).
For example, a NodeId like ns=2;i=2022
means:
-
Namespace index = 2 (pointing to the second entry in the NamespaceArray)
-
Identifier = 2022 (a numeric identifier)
Namespaces play a crucial role in separating different parts of the address space. OPC UA servers expose a special variable called NamespaceArray, which lists all available namespaces. This array is dynamic and can change between server restarts or upgrades.
you can read the NamespaceArray using the wellknown NodeId ns=0;i=2255
to get the list of namespaces.
🔧 Using NodeIds in Node-OPCUA and NodeRED
Here’s how you can read a variable using a NodeId in Node-OPCUA:
import { OPCUAClient, AttributeIds } from "node-opcua";
const endpoint = "opc.tcp://localhost:4840";
// the NodeId of the ServedCoffeeCount variable of a CoffeMachine
const nodeId = "ns=1;i=1072";
const client = OPCUAClient.create();
const dataValue await client.withSessionAsync(async(session) => {
return await session.read({
nodeId,
attributeId: AttributeIds.Value
});
});
console.log("Served Coffee Count:", dataValue.value.toString());
🔧 Using node-red-contrib-opcua
:
Here is how you would do with node-red-contrib-opcua
:
-
insert a general purpose
OPCUA Client Node
with ACTION="READ": -
Use a OPCUA Client such as UAExpert to navigate to the variable and find its node Id (here ns=1i=1072) and copy the NodeId to the clipboard.
-
create a inject button and link it toward the "OPCUA ClientNode ` with "msg.topic' property
🔧 Using @opcua/for-node-red
:
Things are a little bit easier if you use the @opcua/for-node-red
module instead , as it provides an embeded browser to help you find the NodeId you are looking for.
-
simply add a
OPCUA Read Node
node and click on the button to open the node browser. -
use the browse button to open the address space and find the node you are looking for.
-
That's it !
🔍 Limitations of NodeIds
However, NodeIds are not self-descriptive. You need to know the exact NodeId to access a node, which can be cumbersome for large address spaces. While NodeIds are powerful, they come with pitfalls:
-
Discovering NodeId - NodeIds are not self-descriptive. You need to know the exact NodeId to access a node, which can be cumbersome for large address spaces.
-
Namespace Volatility – The NamespaceArray can change between server reboots or upgrades. For example, the same model that was in ns=2 might shift to ns=3 after an update. This can break existing client configurations.
-
Identifier Instability – In some cases, not only the namespace index but also the node identifier might change if the server’s address space is regenerated. This requires manual updates to client configurations, leading to maintenance overhead.
-
Opaque and Unreadable NodeIds – NodeIds with GUIDs or opaque identifiers can be difficult to manage or interpret.
🛠️ An Alternative – translateBrowsePathToNodeIds
Fortunatly, OPC UA provides an alternative: dynamically resolving NodeIds by traversing the address space following a given browse path. This is where translateBrowsePathToNodeIds shines.
Most of the time the location of the node you are looking for is fixed and stable and obey the rule of the Object Model, but the NodeId of the node itself can change.
Imaging you have a CoffeeMachine named MyCoffeeMachine that follow the Model definition provided by the "Commercial Kitchen Equipment Companion specification". You know as a fact that the ServerCoffeeCount will be accessible under the following browse path: Objects/DeviceSet/MyCoffeeMachine/Parameters/ServedCoffeeCount
.
By starting at a well-known root node, you can walk the hierarchy to find nodes by name, ensuring your application is resilient to address space changes.
Here’s how it works in Node-OPCUA:
const browsePath = makeBrowsePath("ObjecsFolder","/2:DeviceSet/1:MyCoffeeMachine/7:Parameters/7:ServedCoffeeCount");
const results = await session.translateBrowsePath(browsePath);
const nodeId = results[0].targets[0].targetId;
const dataValue = await session.readVariableValue(nodeId);
console.log("ServerdCoffeeCount (Dynamic):", dataValue.value.value);
But, wait a minute! What are the 2: 1: 7: ?
These are the namespace index of the node browseName.
In OPC UA, any concept defined in a namespace is identified by its browseName. The browseName is a string that identifies the node in the namespace. The namespace index is used to identify the namespace in which the browseName is defined. This ensure that the browseName doesn't conflict with other browseName in a different namespace that could have a slightly different meaning.
index | namespace |
---|---|
0 | http://opcfoundation.org/UA/ |
1 | urn:opcuademo.sterfive.com:NodeOPCUA-Server-for-CTT |
2 | http://opcfoundation.org/UA/DI/ |
3 | http://opcfoundation.org/UA/ADI/ |
4 | http://opcfoundation.org/UA/AutoID/ |
5 | http://opcfoundation.org/UA/MachineVision |
6 | http://opcfoundation.org/UA/Robotics/ |
7 | http://opcfoundation.org/UA/CommercialKitchenEquipment/ |
- therefore, "/2:DeviceSet" means the node whose BrowseName text is DeviceSet as defined in the namespace DI ( namespace index 2).
- "/1:MyCoffeeMachine" means the node whose BrowseName text is MyCoffeeMachine as defined in the namespace instance namespace (namespace index 1).
- "/7:Parameters" means the node whose BrowseName text is Parameters as defined in the namespace CommercialKitchenEquipment (namespace index 7).
Assuming that the namespace array is stable, you can use a browepath to indirectly find the NodeId of the node you are looking for, without having to worry about the NodeId changing.
🔧 Using node-red-contrib-opcua
:
- you can inject
br=/2:DeviceSet/1:MyCoffeeMachine/7:Parameters/7:ServedCoffeeCount
to the OPCUA Client node to get the browsepath converted to a nodeid and get the value of thre readd node. (don't foret to add thebr=
prefix before the browsepath) - you need to craft the browse path manually though and this may requires some back and forth with a external OPCUA client to find all the element of the browse path.
🔧 Using @opcua/for-node-red
:
-
just inject
/2:DeviceSet/1:MyCoffeeMachine/7:Parameters/7:ServedCoffeeCount
in the OPCUA Read node to refer to the node you are looking for. (no need to add thebr=
prefix before the browsepath). -
In addition
@opcua/for-node-red
provides nice functionality with the embedded browser that allow you to automatically construct the browse path without manually crafting.
⚙️ Why BrowsePath Matters
-
Resilience – Applications are less fragile to server changes.
-
Maintainability – Code becomes clearer and easier to manage.
-
Scalability – New nodes can be added without manually updating NodeIds.
NodeIds are essential, but smart use of browse paths can make your OPC UA applications more robust. In the next part of this series, we’ll explore a novel method for generating stable NodeIds – stay tuned! 🚀
#OPCUA #IIoT #Automation #NodeJS #NodeOPCUA