Getting started with OPC-UA on Azure IoT Edge

OPC-UA brings the promise of secure and platform independent M2M communication:


“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.”


Microsoft invests heavily in OPC-UA by providing several solutions, eg.:



And most of it is open-source!



But it’s hard to get started, what do you need to get data from an OPC-UA Server into the cloud using IoT Edge?


Here is a quick start by using the UPC UA Publisher module.



Update: The functionality below was written against an older version of the OPC Publisher (November 2018). Since then the OPC Publisher logic has changed quite a few times. Still the support for a local file for published nodes remains available. The module is smart enough to detect the availability of a local file.


Here is a working example for Windows 10 1809:


c:\iiotedge\pn.json (I elevated the access rights of this folder)

    "EndpointUrl": "opc.tcp://",
    "UseSecurity": false,
    "OpcNodes": [
        "Id": "ns=2;s=CalcTag"
        "Id": "ns=2;s=UserTag"
        "Id": "ns=2;s=Led1"
        "Id": "ns=2;s=Led2"
        "Id": "ns=2;s=Switch1"
        "Id": "ns=2;s=Switch2"
        "Id": "ns=2;s=Potentio1"
        "Id": "ns=2;s=Potentio2"

Container Create Options for :

  "Hostname": "opcpublisher",
  "Cmd": [
  "HostConfig": {
    "Binds": [


This is part one of a series of blogs about OPC-UA:


  1. Getting started with OPC-UA on Azure IoT Edge
  2. Managing nodes from the cloud in the OPC-UA Publisher Edge
  3. A new batch of OPCPublisher direct methods
  4. Using OPC-Twin for OPC-UA discovery and sending back commands


Note: This is just an introduction. We leave out security for convenience. Don’t try that in your factory!


Note 2: As seen above, please check the current documentation regarding logic support in the current versions.


I run this demonstration with Linux containers on a Windows 10 (Pro) OS.


OPC-UA Server


To get started, we need an OPC-UA server. If you are lucky and you have an OPC-UA device, you can start with that one.


But for those who do not have an actual device, there are several alternatives.


In this case, we use the free OPC-UA Simulation server from Prosys OPC.


If you install the simulator, it automatically starts a simulation with multiple types of values (randomly generated values, a counter, etc.)  which is available for us to consume:



Note: When starting the server for the first time, you have to accept some firewall changes.


Server settings

The only thing regarding the server you need to know are two values:


1. The address of the server


On the opening page of the simulator, you will see the OPC address of your simulation:



Copy this UA TCP address:


opc.tcp://[My machine points to DESKTOP-1ETOHD3:53530]/OPCUA/SimulationServer


Note: later on, I replace this machine name by the IP address of this device. The machine name corrupted my URI, the IP address was more reliable.


2. The names of the nodes


From this server, we need to read (simulation) values. Values are available as nodes:



The value of Counter1 has the node:




Note: An updated version of the simulation on another machine resulted in other node values.


Docker settings


The IoT Edge module which we are going to publish needs access to the Operating system.
In our case, we give Docker access to a folder named:




Create this folder if it does not exist yet.


To get access to this directory/drive, Docker needs access permissions to the C drive:



Only once, you have to provide credentials to apply this change.


Note: I encountered an issue on (corporate) laptops which had limited access. If I tried to set the shared drive, it was disabled again. I was able to fix this using a second user account. Just create a local account with administrator permissions. Then, add this account to the ‘Hyper-V administrators’ group. Use this account to give Share permissions.


Publisher configuration


In this folder, create this configuration file named ‘c:\iiotedge\pn.json’:


    "EndpointUrl": "opc.tcp://",
    "UseSecurity": false,
    "OpcNodes": [
        "Id": "ns=5;s=Counter1"
        "Id": "ns=5;s=Random1"

Note: In Linux, gave read and write rights to this file (sudo chmod 666 pn.json)!


Note: In Linux, I recommend using  a path like ‘/var/iotedge/pn.json’ (with all the read and write rights set to folder and file).


Here you fill in the OPC-UA endpoint URL and the Nodes you want to make available in the cloud.


Note: I inserted the IP address instead of the machine name. I have encountered errors because the URL was not recognized due to the machine name.


Note: I disabled the security by setting it to ‘None’. In production, please implement a secure connection!


IoT Edge module using the marketplace


As always, register an IoT Edge in the Azure Portal.


Once registered, go to the official marketplace for public IoT Edge modules.


This is a good starting point for using official modules from Microsoft or third-party modules.


Here you can find the OPC-UA module we want to deploy:



This marketplace saves us a lot of hassle (downloading the code from GitHub, recompiling, adding it into a container repository, etc.) so get it now!



Follow the dialogs and before you know it, you start adding the OPC-UA Publisher to your device:





This wizard has no knowledge about your actual device or OPC-UA server. The default container create options are insufficient.


Remove the container create options:



And replace it with:


  "Hostname": "publisher",
  "Cmd": [
  "HostConfig": {
    "PortBindings": {
      "62222/tcp": [
          "HostPort": "62222"
    "Binds": [
    "ExtraHosts": [


Note: The ExtraHosts seems to be ignored.


Note: The binding for Windows containers on a Windows host is in this example “c:/iiotedge:c:/appdata”. The “c:/appdata” is required. The “c:/iiotedge” is the folder on the host Operating System.


Note: Using Linux containers on Windows works for me with “c://iiotedge:/appdata” and “–pf=/appdata/pn.json” for a local file “C:\iiotedge\pn.json”.


Note: Using (Linux) containers on Linux works for me with “/var/iiotedge:/appdata” and “–pf=/appdata/pn.json” for a local file “/var/iiotedge/pn.json”.


It is recommended to make the name of the module a part of the path of this kind of external folders (like c:/[folder]/[modulename]/[subfolder]). This way, if multiple modules are using external folders, the same folder is not used by multiple modules.


Now, we can read the pn.json file. This configuration published a message every ten seconds (–si=10).


Also, repair the routes, the wizard added this line which we do not need in here:


Finally, deploy this configuration.


Checking the configuration


Now comes the interesting part, will everything work together?


I encountered several little issues during various deployments: IP address instead of the machine name, typos in configurations, node names, etc. Check out the logging of both the edgeAgent and the OPCPublisher modules.


So if everything works, then “docker logs -f OPCPublusher” will show:


IoTEdge detected.
[22:46:38 INF] Current directory is: /appdata
[22:46:38 INF] Log file is: publisher-publisher.log
[22:46:38 INF] Log level is: info
[22:46:38 INF] Publisher is starting up...
[22:46:38 INF] Application Certificate store type is: X509Store
[22:46:38 INF] Application Certificate store path is: CurrentUser\UA_MachineDefault
[22:46:38 INF] Application Certificate subject name is: publisher
[22:46:38 INF] Application certificate found in Application Certificate Store
[22:46:38 INF] Application certificate is for Application URI 'urn:publisher:publisher:microsoft:', Application 'publisher and has Subject 'publisher'
[22:46:38 INF] Trusted Issuer store type is: Directory
[22:46:38 INF] Trusted Issuer Certificate store path is: CertificateStores/issuers
[22:46:38 INF] Trusted Peer Certificate store type is: Directory
[22:46:38 INF] Trusted Peer Certificate store path is: CertificateStores/trusted
[22:46:38 INF] Rejected certificate store type is: Directory
[22:46:38 INF] Rejected Certificate store path is: CertificateStores/rejected
[22:46:38 INF] Rejection of SHA1 signed certificates is disabled
[22:46:38 INF] Minimum certificate key size set to 1024
[22:46:38 INF] Adding publisher certificate to trusted peer store. StorePath=CertificateStores/trusted
[22:46:38 INF] A certificate with the same thumbprint is already in the trusted store.
[22:46:38 INF] OperationTimeout set to 120000
[22:46:38 INF] Publisher server base address: opc.tcp://publisher:62222/UA/Publisher
[22:46:38 INF] Security policy with mode SignAndEncrypt added
[22:46:38 INF] LDS(-ME) registration intervall set to 0 ms (0 means no registration)
[22:46:38 INF] opcstacktracemask set to: 0x0
[22:46:38 INF] There is no site configured.
[22:46:38 INF] Publisher configured to auto trust server certificates of the servers it is connecting to.
[22:46:38 INF] Starting server on endpoint opc.tcp://publisher:62222/UA/Publisher ...
[22:46:40 INF] Server started.
[22:46:40 INF] Using default telemetry configuration.
[22:46:40 INF] The name of the configuration file for published nodes is: ./pn.json
[22:46:40 INF] Attemtping to load node configuration from: ./pn.json
[22:46:40 INF] Loaded 1 config file entry/entries.
[22:46:40 INF] There are 2 nodes to publish.
[22:46:40 INF] Create IoTEdgeHub module client using 'Amqp_Tcp_Only' for communication.
[22:46:41 INF] IoTCentral mode: False
[22:46:42 INF] Connection status changed to 'Connected', reason 'Connection_Ok'
[22:46:42 INF] Message processing and hub communication configured with a send interval of 10 sec and a message buffer size of 262144 bytes.
[22:46:42 INF] Creating task process and batch monitored item data updates...
[22:46:42 INF] Publisher is running. Press CTRL-C to quit.
[22:46:43 INF] Connect and monitor session and nodes on endpoint 'opc.tcp://'.
[22:46:44 INF] Create unsecured session for endpoint URI 'opc.tcp://' with timeout of 10000 ms.
[22:46:44 INF] Certificate 'DC=DESKTOP-1ETOHD3, O=Prosys OPC, CN=SimulationServer' will be trusted, since the autotrustservercerts options was specified.
[22:46:44 INF] Session successfully created with Id ns=1;g=c92c780c-e4f7-4b96-af34-717afbd283fe.
[22:46:44 INF] The session to endpoint 'opc.tcp://' has 7 entries in its namespace array:
[22:46:44 INF] Namespace index 0:
[22:46:44 INF] Namespace index 1: urn:DESKTOP-1ETOHD3:OPCUA:SimulationServer
[22:46:44 INF] Namespace index 2:
[22:46:44 INF] Namespace index 3:
[22:46:44 INF] Namespace index 4:
[22:46:44 INF] Namespace index 5:
[22:46:44 INF] Namespace index 6:
[22:46:44 INF] The server on endpoint 'opc.tcp://' supports a minimal sampling interval of 0 ms.
[22:46:45 INF] Created subscription with id 2 on endpoint 'opc.tcp://'
[22:46:45 INF] Create subscription on endpoint 'opc.tcp://' requested OPC publishing interval is 0 ms. (revised: 0 ms)
[22:46:45 INF] Start monitoring items on endpoint 'opc.tcp://'. Currently monitoring 0 items.
[22:46:45 INF] Done processing unmonitored items on endpoint 'opc.tcp://' took 37 msec. Now monitoring 2 items in subscription with id '2'.


You see the namespaces exposed by the OPC-UA server. Check out namespace 5. This is the simulation. And the node of the counter of this simulation is named “ns=5;s=Counter1”. Nice.


And later on, you will see that messages are sent:


[22:49:44 INF] ==========================================================================
[22:49:44 INF] OpcPublisher status @ 11/05/2018 22:49:44 (started @ 11/05/2018 22:46:38)
[22:49:44 INF] ---------------------------------
[22:49:44 INF] OPC sessions: 1
[22:49:44 INF] connected OPC sessions: 1
[22:49:44 INF] connected OPC subscriptions: 1
[22:49:44 INF] OPC monitored items: 2
[22:49:44 INF] ---------------------------------
[22:49:44 INF] monitored items queue bounded capacity: 8192
[22:49:44 INF] monitored items queue current items: 0
[22:49:44 INF] monitored item notifications enqueued: 360
[22:49:44 INF] monitored item notifications enqueue failure: 0
[22:49:44 INF] monitored item notifications dequeued: 360
[22:49:44 INF] ---------------------------------
[22:49:44 INF] messages sent to IoTHub: 18
[22:49:44 INF] last successful msg sent @: 11/05/2018 22:49:42
[22:49:44 INF] bytes sent to IoTHub: 71353
[22:49:44 INF] avg msg size: 3964
[22:49:44 INF] msg send failures: 0
[22:49:44 INF] messages too large to sent to IoTHub: 0
[22:49:44 INF] times we missed send interval: 0
[22:49:44 INF] ---------------------------------
[22:49:44 INF] current working set in MB: 90
[22:49:44 INF] --si setting: 10
[22:49:44 INF] --ms setting: 262144
[22:49:44 INF] --ih setting: Amqp_Tcp_Only
[22:49:44 INF] ==========================================================================


Here, eighteen messages were sent.


I see the arrival of the messages using the Device Explorer. Here is one message:


11/5/2018 11:53:43 PM> Device: [opc-ua-edge], Data:[

Note: The device explorer is deprecated. I recommend using the IoT Edge extension in Visual Studio code.


I removed a few lines, only the first and last are shown. We see the counter values and the random values, ten in total because we are listening with a ten-second interval.


Help, I am missing every X message at high speed


The OPC Publisher is using the publisher-subscribe pattern for getting data from the master. But I ran into an issue where I was missing data. I should get telemetry four times a second but I only received every fourth message!

I was quite disappointed at first.

Then I double checked all settings of the OPC Publisher, I found this interesting setting…

Take a good look at:

--oi, --opcsamplinginterval=VALUE
the publisher is using this as default value in
milliseconds to request the servers to sample
the nodes with this interval
this value might be revised by the OPC UA
servers to a supported sampling interval.
please check the OPC UA specification for
details how this is handled by the OPC UA stack.
a negative value will set the sampling interval
to the publishing interval of the subscription
this node is on.
0 will configure the OPC UA server to sample in
the highest possible resolution and should be
taken with care.
Default: 1000

Although we are subscribed to receive for every message, it can be ‘throttled’.


And look at the default interval, 1 second.


When I changed it to 200 (200 milliseconds), I received all messages.


I’m missing values in my telemetry messages…


If you are reading values from several nodes but the values do not differ that much over time check out this section.


Eg. you are reading two nodes: one is changing every minute, the other is changing every second. In that case you will receive a partial telemetry message with only the second node every second. Only once a minute you receive both values!


The OPC-UA publisher only exposes changes op the OPC-UA server. There are no events sent for values which do not change.


So think about some aggregation module which fixes this if this is an issue for you.




This is a quickstart regarding OPC-UA. There is a lot more to discover. But this will help you on your journey.


Do not forget to add actual security, the code shown here is not what you want to deliver in a production environment.


Een gedachte over “Getting started with OPC-UA on Azure IoT Edge

Reacties zijn gesloten.