Skip to main content

What is a OPCUA NodeId

· 7 min read
Etienne Rossignon
Maintainer of NodeOPCUA and @opcua/for-node-red

🔧 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.

alt text

🔧 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": alt text

  • 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. alt text

  • create a inject button and link it toward the "OPCUA ClientNode ` with "msg.topic' property

  • alt text

🔧 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. alt text

  • 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.

indexnamespace
0http://opcfoundation.org/UA/
1urn:opcuademo.sterfive.com:NodeOPCUA-Server-for-CTT
2http://opcfoundation.org/UA/DI/
3http://opcfoundation.org/UA/ADI/
4http://opcfoundation.org/UA/AutoID/
5http://opcfoundation.org/UA/MachineVision
6http://opcfoundation.org/UA/Robotics/
7http://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 the br= 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 the br= 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