Skip to content

Cookie preferences

We use cookies for our own analytics, to see how our campaigns perform and, if you allow it, to load content from other services such as the Google site search. We never sell your data. Necessary cookies keep the site working and cannot be switched off. See our Cookie Policy for details and our Privacy Policy for how we handle personal data.

Security, load balancing and remembering your cookie choice.

Show us which pages people read, how they find the site and where they get stuck, so we can improve it (Google Analytics, Microsoft Clarity).

Show us which of our ad campaigns bring visitors to the site and remember the campaign or partner link you arrived from (Google Ads, LinkedIn, Meta, partner program). We do not use these cookies to build advertising profiles.

Load the site search from Google when you use it. Google sets its own cookies and shows ads in the search results.

© 2026 The ThingsBoard Authors
22.6k Try for free

ThingsBoard Cloud

Choose your data region

Your data stays in the region you choose, for residency and compliance. No credit card required.

Rather run it yourself? Install on your own servers

OPC-UA Integration

OPC-UA Integration connects ThingsBoard to an OPC UA server as a client. It finds the device nodes that match your mapping, subscribes to their values, and passes every change to an uplink data converter, which turns it into telemetry and attributes. For downlink, ThingsBoard writes values to server nodes and calls server methods.

Use OPC-UA Integration when:

  • Your data lives on an OPC UA server, such as a PLC, a SCADA system, or an OPC UA gateway like KEPServerEX, and the server is reachable from ThingsBoard.
  • You want ThingsBoard to create a device for every node that matches a pattern, instead of registering devices one by one.
  • You need to write node values or call methods on the server from the Rule Engine.
  1. Connect and find devices. The integration connects to the server and browses its address space for nodes that match the Device Node Pattern. It repeats the scan every Scan period in seconds.
  2. Subscribe. For each device node, the integration subscribes to the child nodes listed in Subscription tags. The server reports value changes, sampled once per second.
  3. Decode the payload. On every change, the uplink converter gets the tag values and the device node details, and returns the device name, telemetry, and attributes. If Allow create devices or assets is on, ThingsBoard creates the device on its first message.
  4. Write or call (optional). The Rule Engine passes a message to the integration. The downlink converter returns the values to write and the methods to call, and the integration sends them to the server right away.
  • Access to ThingsBoard or ThingsBoard Cloud with integrations enabled for your tenant.
  • Permission to create integrations and data converters, and to edit rule chains for downlink.
  • An OPC UA server that ThingsBoard can reach over TCP.
  • Optional: an OPC UA client to browse the server and look up node names and NodeIds, for example the free Prosys OPC UA Browser.

Collect these details of your server. You enter them in the integration later.

  • Endpoint URL — opc.tcp://{host}:{port}/{path}.
  • Security policy and Identity that the server accepts.
  • Device node — the node that represents a device, with its full browse path from Objects, or its NodeId.
  • Tags — the browse names of the child nodes to read, and the NodeIds of the nodes to write or the methods to call.

This guide uses the public demo instance of the Prosys OPC UA Simulation Server as an example. It runs on the internet with values that change every second, so there is nothing to install. To follow the guide with your own server, replace the example values with yours. For a complete scenario with another server, a dashboard, and method calls, see the air conditioner monitoring recipe.

Setting Value
Endpoint URL opc.tcp://uademo.prosysopc.com:53530/OPCUA/SimulationServer
Host uademo.prosysopc.com
Port 53530
Endpoint /OPCUA/SimulationServer
Security None
Identity Anonymous

On the demo server, the guide reads the sample device MyDevice. Its fully qualified name is Objects.MyObjects.MyDevice, and its NodeId is ns=6;s=MyDevice. Among its child nodes are:

  • MyLevel — a Double variable, NodeId ns=6;s=MyLevel, with a simulated level that changes every second. The guide reads it as telemetry.
  • MySwitch — a Boolean variable, NodeId ns=6;s=MySwitch, that clients can write. The guide reads it and writes it.
  • MyLevelAlarm — an alarm object, NodeId ns=6;s=MyLevel.Alarm, not a variable.
  • MyMethod — a sample method.

To check the address space, open an OPC UA client such as the Prosys OPC UA Browser, connect to the endpoint URL, and expand Objects ⇾ MyObjects ⇾ MyDevice. Select a node to see its NodeId.

Create the uplink converter, then the integration with the mapping of your device nodes.

The uplink converter receives the tag values as a JSON byte array and returns the device data. OPC-UA Integration uses the Generic converter: you write the decoder in TBEL or JavaScript. For converter types and the full decoder reference, see Uplink data converter.

  1. Go to Integrations ⇾ Data converters and click + Add data converter ⇾ Create new converter.

  2. Converter type — keep Uplink.

  3. Integration type — select OPC-UA.

  4. Name — enter a name, for example OPC-UA Uplink Converter.

  5. Main decoding configuration — the editor opens with the default OPC-UA decoder below. Keep it: it takes the device name from the node name in the metadata, stores the node details as attributes, and stores every tag value as telemetry. Use the TBEL / JS toggle to switch to JavaScript.

    /** Decoder **/
    // One string value per subscription tag key, for example {"level": "42.17", "switchState": "true"}.
    var data = decodeToJson(payload);
    var telemetry = {};
    foreach (element : data.entrySet()) {
    var value = element.value;
    // A tag without a value yet arrives as null.
    if (value != null) {
    // Boolean values arrive as "true" or "false".
    if (value == "true" || value == "false") {
    value = value == "true";
    }
    telemetry.put(element.key, value);
    }
    }
    return {
    // Device node name, for example MyDevice.
    deviceName: metadata['opcUaNode_name'],
    deviceType: "OPC-UA Device",
    attributes: {
    opcUaNodeFqn: metadata['opcUaNode_fqn'],
    opcUaNamespaceIndex: metadata['opcUaNode_namespaceIndex'],
    opcUaIdentifier: metadata['opcUaNode_identifier']
    },
    telemetry: telemetry
    };

    To adapt:

    • Device name — opcUaNode_name is the browse name of the device node. If nodes in different folders share a name, use metadata['opcUaNode_fqn'] instead.
    • Device profile — set deviceType to the name of the device profile you want.
    • Attributes instead of telemetry — move a key from telemetry to attributes, for example if (element.key == "switchState") { ... }.
    • Update-only keys — the node attributes are sent with every message. To store them only when they change, add them to the Update only keys list in Advanced decoding parameters.
    • Group or customer — add groupName or customerName to the result.
  6. Click Test decoder function and check the result (see Test the Decoder).

  7. Click Add.

Decoder Inputs

Variable Type Content
payload byte array A JSON object with one entry per subscription tag: the tag Key and the node value as a string, for example "42.17" or "true". A tag whose node has no value yet is null.
metadata {[key: string]: string} integrationName, the key–value pairs from the integration’s Metadata field, and the device node details: opcUaNode_name (browse name), opcUaNode_fqn (fully qualified name), opcUaNode_namespaceIndex, and opcUaNode_identifier (the identifier part of the NodeId).

For the example device, the first message after the device is found holds every tag. A tag whose value has not arrived yet is null:

{
"level": "17.0",
"switchState": null
}

with this metadata:

{
"integrationName": "OPC-UA integration",
"opcUaNode_name": "MyDevice",
"opcUaNode_fqn": "Objects.MyObjects.MyDevice",
"opcUaNode_namespaceIndex": "6",
"opcUaNode_identifier": "MyDevice"
}

After that, each message holds only the tag that changed, plus every tag marked Required. For example, a level change sends {"level": "18.0"}. Write the decoder so that every key is optional.

Test the Decoder

Click Test decoder function below the editor. Replace the payload with {"level": "42.17", "switchState": "true"}, and in Metadata add opcUaNode_name = MyDevice, opcUaNode_fqn = Objects.MyObjects.MyDevice, opcUaNode_namespaceIndex = 6, and opcUaNode_identifier = MyDevice. Click Test. The Output panel shows:

{
"deviceName": "MyDevice",
"deviceType": "OPC-UA Device",
"attributes": {
"opcUaNodeFqn": "Objects.MyObjects.MyDevice",
"opcUaNamespaceIndex": 6,
"opcUaIdentifier": "MyDevice"
},
"telemetry": {
"level": "42.17",
"switchState": true
}
}

level stays a string in the test output. When ThingsBoard saves the data, it stores numeric strings as numbers. If the output is empty or shows an error, fix the decoder and test again.

  1. Go to Integrations ⇾ Integrations and click + Add integration.
  2. Basic settings:
    • Integration type — select OPC-UA.
    • Name — keep the default OPC-UA integration or enter your own.
    • Enable integration and Allow create devices or assets — keep both on, so the device is created on its first message.
    • Click Next.
  3. Uplink data converter:
    • Click Select existing and choose OPC-UA Uplink Converter.
    • Alternatively, click Create new to write a decoder here.
    • Click Next.
  4. Downlink data converter:
  5. Connection:
    • Host — the host of your server, for example uademo.prosysopc.com.
    • Port — the port of your server. Replace the default 49320, for example with 53530.
    • Endpoint — the endpoint path, for example /OPCUA/SimulationServer.
    • Security — a policy that the server offers. The example keeps None.
    • Identity — Anonymous, or Username with the credentials of your server. The example keeps Anonymous.
    • Mapping, in the same step:
      • Mapping type — keep Fully Qualified Name.

      • Device Node Pattern — a regular expression for the full path of your device nodes. Replace the default Channel1\.Device\d+$, for example with Objects\.MyObjects\.MyDevice. Escape each dot as \..

      • Subscription tags — one tag per node to read: a Key for the value and the Path of the node. Fill in the first tag, then click Add subscription tag for the next one. Leave Required off. The example uses:

        Key Path
        level MyLevel
        switchState MySwitch
  6. Click Add.

The integration connects to the server right away and starts the first scan. See Connection Settings for the other parameters.

Parameter Default Description
Host — Host name or IP address of the OPC UA server. Required. On ThingsBoard Cloud, it must be a public address.
Port 49320 Port of the server, from 1 to 65535. The default is the port of KEPServerEX.
Endpoint — Path of the endpoint. The integration connects to opc.tcp://{Host}:{Port}/{Endpoint}. The leading slash is optional. Leave it empty if the server endpoint has no path.
Security None Security policy: None, Basic128Rsa15, Basic256, or Basic256Sha256. The integration uses the first endpoint of the server with this policy. Any policy other than None shows the Keystore fields.
Type JKS In Keystore. JKS or PKCS12.
Location — In Keystore. The keystore file with the client certificate and private key. Upload it with Browse file, or click Use Secret storage to take it from Secrets storage. The server must trust this certificate.
Alias opc-ua-extension In Keystore. Alias of the key entry.
Password and Key password secret In Keystore. Passwords of the keystore and of the key entry.
Identity Anonymous Anonymous, or Username with the Username and Password fields.
Mapping type Fully Qualified Name In Mapping. Fully Qualified Name matches the Device Node Pattern against the full browse path of a node, for example Objects.MyObjects.MyDevice. ID matches it against the identifier part of the NodeId, for example Channel1.Device1 in ns=2;s=Channel1.Device1.
Device Node Pattern Channel1\.Device\d+$ In Mapping. Required. Regular expression that must match the whole name or identifier. For Fully Qualified Name, the path starts at Objects, and each dot must be escaped as \.: the integration splits the pattern at \. to decide which folders to browse.
Namespace — In Mapping, for ID only. Namespace index of the device nodes. If empty, nodes in every namespace can match.
Subscription tags — In Mapping. At least one is required. Key is the name of the value in the payload. Path is the browse name of a node anywhere under the device node, for example MyLevel. If the NodeId identifier of a child contains the device node name, Path is the rest of the identifier instead, for example Tag1 for Channel1.Device1.Tag1. Required adds the tag to every message, not only when it changes.
Add mapping element — Adds another mapping with its own pattern and tags, for a second kind of device.
Execute remotely Off Runs the integration as a separate process outside the ThingsBoard cluster, for a server in a private network. ThingsBoard generates an Integration key and Integration secret for it. See Remote Integration.
Application name — In Advanced settings. Client application name sent to the server.
Application uri — In Advanced settings. Client application URI sent to the server.
Scan period in seconds 10 In Advanced settings. How often the integration browses the server again to find new device nodes and tags. Devices not found in a scan are removed from the integration. It does not change how often values arrive.
Timeout in milliseconds 5000 In Advanced settings. Request timeout of the OPC UA client.
Description — In Advanced settings. Free-text description.
Metadata — In Advanced settings. Key–value pairs added to the metadata of every message from this integration.

The subscription settings are fixed: the integration asks the server to sample each tag every 1,000 ms and to report changes once per second. If the connection fails, the integration tries again after 10 seconds, adding 10 seconds to each next attempt, up to 10 minutes.

Data arrives as soon as the integration connects and whenever a tag value changes. On the demo server, MyLevel changes every second.

  • Integration events — go to Integrations ⇾ Integrations, open the integration, and click the Events tab. A CONNECT event with status SUCCESS shows that the integration connected. Then Uplink events with status OK follow, about once per second. Click ⋅⋅⋅ in the Message column to see the tag values, for example {"level":"18.0"}.
  • Converter events — open OPC-UA Uplink Converter in Integrations ⇾ Data converters and click the Events tab. In shows the tag values, Out shows the decoded result, and Metadata shows integrationName and the opcUaNode_* keys.
  • Device — go to Devices & assets ⇾ Devices. The integration has created the device MyDevice with the OPC-UA Device profile. Open it and check:
    • Latest telemetry — level, which updates as the server value changes, and switchState.
    • Attributes — opcUaNodeFqn, opcUaNamespaceIndex, and opcUaIdentifier in Client attributes.

The integration sends each downlink right away as OPC UA Write and Call requests. Nothing is queued: if the integration is not connected, it records a Downlink event with status FAILURE and drops the message.

The downlink converter turns a Rule Engine message into a JSON object with two arrays: writeValues and callMethods. In this guide, a change of the shared attribute switchState writes MySwitch on the server. For the full encoder reference, see Downlink data converter.

  1. Open the integration in Integrations ⇾ Integrations and click Toggle edit mode (pencil icon).
  2. In the Downlink data converter field, click Create new.
  3. Enter a name, for example OPC-UA Downlink Converter.
  4. Main encoding configuration opens in TBEL with the default OPC-UA encoder below. Keep it: on a switchState shared attribute update, it writes the value to the MySwitch node of the device. Use the TBEL / JS toggle to switch to JavaScript.
  5. To record downlink events, click the debug button next to the name, select All messages, and click Apply.
  6. Click Add.
  7. Click Apply changes.

The default encoder writes the switchState value to the MySwitch node of the device. The rule chain adds the namespace index of the device to the metadata as cs_opcUaNamespaceIndex.

/** Encoder **/
var data = {
writeValues: [],
callMethods: []
};
// A shared attribute update, for example {"switchState": true}.
if (msgType == 'ATTRIBUTES_UPDATED' && msg.switchState != null) {
data.writeValues.push({
// For the example device: ns=6;s=MySwitch.
nodeId: 'ns=' + metadata['cs_opcUaNamespaceIndex'] + ';s=MySwitch',
value: msg.switchState,
dataType: 'Boolean'
});
}
return {
contentType: 'JSON',
data: JSON.stringify(data),
metadata: {}
};

To check the encoder, click Test encoder function. Set Message type to ATTRIBUTES_UPDATED, replace the Message with {"switchState": true}, add cs_opcUaNamespaceIndex = 6 to Metadata, and click Test. The Output panel shows:

{
"contentType": "JSON",
"data": "{\"writeValues\":[{\"nodeId\":\"ns=6;s=MySwitch\",\"value\":true,\"dataType\":\"Boolean\"}],\"callMethods\":[]}",
"metadata": {}
}

The integration reads these fields from the encoder result. It ignores metadata.

Field Description
contentType Must be JSON. Results with other content types are skipped.
data JSON string with the writeValues and callMethods arrays. Both are optional. If both are empty, nothing is sent.
writeValues[].nodeId NodeId of the variable to write, for example ns=6;s=MySwitch or ns=3;i=1009. Entries with an invalid NodeId are skipped.
writeValues[].value Value to write: a number, string, or boolean.
writeValues[].dataType Optional OPC UA type of the value: SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, Float, Double, Boolean, or String. UInt64 is not supported: the write fails with failed to convert value to OPC UA type UInt64. An unknown name is treated as String. Without it, whole numbers are sent as Int32 (or Int64 if too large), decimals as Double. The server rejects a value whose type does not match the node, so set it for numeric nodes.
callMethods[].objectId NodeId of the object that owns the method, for example ns=6;s=MyDevice.
callMethods[].methodId NodeId of the method.
callMethods[].args Optional array of input arguments, in order. Each one is a value, or an object with value and dataType, for example {"value": 2.5, "dataType": "Double"}.

To call a method, push an entry to callMethods. Look up the NodeIds and the input arguments of the method in an OPC UA client: select the method and check its InputArguments property.

The encoder needs the namespace index of the device. Add it to the metadata with an originator attributes node, then pass the message to the integration.

  1. Go to Data processing ⇾ Rule chains and open the Root Rule Chain.
  2. From the Enrichment section, drag an originator attributes node onto the canvas:
    • Name — for example Add Namespace Index.
    • Client attributes — opcUaNamespaceIndex. The node adds it to the metadata as cs_opcUaNamespaceIndex.
    • Add originator attributes to — keep Metadata.
    • Click Add.
  3. From the Action section, drag an integration downlink node onto the canvas:
    • Name — for example Downlink to OPC-UA.
    • Integration — select OPC-UA integration.
    • Click Add.
  4. Connect the message type switch node to Add Namespace Index with the Attributes Updated relation.
  5. Connect Add Namespace Index to Downlink to OPC-UA with the Success relation.
  6. Click Apply changes.
  1. Go to Devices & assets ⇾ Devices and open MyDevice.
  2. On the Attributes tab, select Shared attributes and click +.
  3. Enter Key switchState, select the Boolean type, set the value to the opposite of the current switchState telemetry, and click Add.
  • Converter events — open OPC-UA Downlink Converter in Integrations ⇾ Data converters and click the Events tab. In shows {"switchState": true} with cs_opcUaNamespaceIndex in its metadata. Out shows the writeValues entry with ns=6;s=MySwitch.
  • Integration events — open the integration’s Events tab. Two events appear: Downlink: ATTRIBUTES_UPDATED for the message from the Rule Engine, and Downlink with status OK for the write, with the written values in its message. A rejected write appears as an error with the OPC UA status, for example Write NodeId{ns=6, id=MySwitch}, status = Bad_TypeMismatch.
  • Device — the server reports the new MySwitch value back, so switchState on the Latest telemetry tab changes to the value you set.
  • Server — in an OPC UA client, such as the Prosys OPC UA Browser, select MySwitch. Its Value shows the value you set.
Symptom Cause Fix
No events on the integration Events tab Debug mode is off, or the integration is disabled. Debug mode records all events only for a limited time after you turn it on. Turn on Enable integration and debug mode. Click Toggle edit mode and Apply changes to reconnect.
Host must be in the public network., or Usage of local network host for OPC-UA server connection is not allowed! The host is a private or local address, and the platform allows only public hosts. Use a public address, or run the integration as a Remote Integration in the server’s network.
CONNECT with status FAILURE: Failed to connect to provided endpoint: opc.tcp://... The host, port, or endpoint is wrong, or a firewall blocks the connection. Check the URL in the error against the endpoint URL of your server. Allow outbound TCP to the port.
CONNECT with status FAILURE: Failed to connect to OPC-UA server. Reason: UaServiceFaultException: status=Bad_TooManySessions The server has reached its maximum number of sessions. The public demo server often does. Every client connected to it, such as an OPC UA browser, holds a session. Disconnect other clients you do not need. The integration reconnects on its own, with a delay that grows by 10 seconds after each failure, up to 10 minutes. To retry right away, turn Enable integration off and on.
CONNECT with status FAILURE: no desired endpoints returned The server has no endpoint with the selected Security policy. Select a policy that the server offers. Check the endpoints in an OPC UA client.
CONNECT with status FAILURE: Failed to load certificate using provided configuration! The keystore file, type, alias, or a password is wrong. Check the Keystore fields. The alias must point to an entry with a private key.
CONNECT with SUCCESS, but no Uplink events The Device Node Pattern matches no node. Check the pattern against the full path from Objects, for example Objects\.MyObjects\.MyDevice. Escape every dot as \.: with plain dots, the integration does not browse into the folders. For ID, check Namespace.
A key is always null in the payload The tag Path matches no node under the device node, for example because of a typo or an extra character such as a trailing dot or space. Use the exact browse name of the node, for example MySwitch. Check it in an OPC UA client.
Each Uplink event holds only one key Expected: a message holds only the changed tag and the tags marked Required. Mark the tags you need in every message as Required.
Device named Unknown Device The converter has a decoder that reads deviceName from the payload, for example a decoder copied from another integration. OPC UA payloads have no deviceName. Use the default OPC-UA decoder: it reads the device name from opcUaNode_name in the metadata.
switchState is stored as the string "true" The decoder does not convert boolean strings. Use the default OPC-UA decoder, or convert "true" and "false" to booleans in your decoder.
Downlink FAILURE: Cannot process downlink message because of connection was lost. The integration was not connected. Downlinks are not queued. Check the CONNECT events, fix the connection, and send the downlink again.
No downlink event, and the converter Out has ns=null (TBEL) or ns=undefined (JavaScript) in nodeId The rule chain did not add cs_opcUaNamespaceIndex, so the NodeId is invalid and the write is skipped. Connect Add Namespace Index between the message type switch and the integration downlink node, and check that the device has the opcUaNamespaceIndex client attribute.
No downlink and no error No downlink converter is assigned, the rule chain does not route attribute updates, or the encoder returned empty arrays. Assign the converter, check the Attributes Updated relation, and check Out in the downlink converter events.
Downlink error Bad_TypeMismatch The value type does not match the node type, for example a string for a Boolean node. Set dataType in the encoder. Add the shared attribute with the matching type.
Downlink error Bad_NodeIdUnknown, Bad_NotWritable, or Bad_UserAccessDenied The NodeId is wrong, or the server does not allow the write. Check the NodeId and the AccessLevel of the node in an OPC UA client.

To read debug events, open the integration or converter, click Events, and select an event: In is the received data, Out is the converter result, and Error holds the error text.