Example of using OPC UA server with Raspberry PLC

Industrial applications with hardware based on Open Source for automation solutions
January 16, 2024 by
Example of using OPC UA server with Raspberry PLC
Boot & Work Corp. S.L., David Coronado


                                  Introduction

                                  OPC UA (OPC Unified Architecture) is an evolution of OPC that addresses the limitations of previous versions. It was developed to provide a unified architecture and open standards for communication in industrial automation and other environments.

                                  Among its features are:

                                  • Platform Independent: OPC UA is not tied to a specific platform and can run on various operating systems.
                                  • Security: Provides a robust set of security features, including authentication, authorization, integrity, and confidentiality.
                                  • Scalability: OPC UA is highly scalable and can be used in small and large systems, from embedded devices to enterprise environments.
                                  • Unified Information Model: Offers a unified information model that facilitates interoperability between different systems and devices.


                                  Requirements

                                  • Have a Raspberry Pi PLC
                                  • Having the Raspberry PLC dependencies installed, we strongly recommend seeing this post first:

                                  First steps using Raspberry Pi PLC

                                  • Install the Node Red libraries, which provide us with the additional necessary blocks for creating the server. 

                                  ​These libraries are: node-red-contrib-iiot-opcua and node-red-contrib-opcua

                                  • (Recommended) Install a client for OPC UA servers, this will help us see if the server structure is correct, and the values that each node has. We recommend the OTC client called UaExpert, which is free and cross-platform.

                                  Examples of use

                                  Below we are going to see a series of examples with which to understand the basic concepts of creating this type of OPC UA servers and their use, informing that it is possible to do the same using high-level languages such as python and programming languages. low level with C. We have decided to use Node-Red, basically, for its simplicity and easy understanding.

                                  Basic examples

                                  Create the OPC UA server

                                  The blocks seen below are responsible for generating the server, its system of folders and nodes, while initially giving them a default value. Keep in mind that when doing the "Deploy" it will take a few moments to be assembled and deployed.

                                  Block set to create an OTA server

                                  The blocks we have used are:

                                  • The OPCUA-IIoT-Inject block is used to inject a value, we specify that it is of type INJECT.

                                  Inject configuration

                                  • The OPCUA-IIoT-Server-ASO block is used to assign the value to a route to a Node, we generate the id of the node and the type in this case is FolderType, but in the case of variables we will see that it is BaseDataVariableType.

                                  Tag configuration

                                  • The OPCUA-IIoT-Server block, to mount and provide a URL to the server, we specify the port where the server will be listening (then the endpoint will be found at: opc.tcp://Device_IP:55488/ ) and other settings such as limit, security, etc.

                                  Server configuration

                                  Here is the code, which corresponds to the previous set of blocks:

                                  [ { "id": "526638e31bd911fe", "type": "tab", "label": "Flow 1", "disabled": false, "info": "", "env": [] }, { "id": "75df2f23680c144e", "type": "OPCUA-IIoT-Server", "z": "526638e31bd911fe", "port": "55488", "endpoint": "", "acceptExternalCommands": true, "maxAllowedSessionNumber": "", "maxConnectionsPerEndpoint": "", "maxAllowedSubscriptionNumber": "", "alternateHostname": "", "name": "", "showStatusActivities": false, "showErrors": false, "asoDemo": false, "allowAnonymous": true, "individualCerts": false, "isAuditing": false, "serverDiscovery": true, "users": [], "xmlsets": [], "publicCertificateFile": "", "privateCertificateFile": "", "registerServerMethod": "1", "discoveryServerEndpointUrl": "", "capabilitiesForMDNS": "", "maxNodesPerRead": "", "maxNodesPerBrowse": "", "delayToClose": "", "x": 1034.3000183105469, "y": 389.9999694824219, "wires": [ [ "1b7404249f5bac6e" ] ] }, { "id": "1b7404249f5bac6e", "type": "debug", "z": "526638e31bd911fe", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 1214.3000183105469, "y": 389.9999694824219, "wires": [] }, { "id": "a94182acacad6858", "type": "OPCUA-IIoT-Server-ASO", "z": "526638e31bd911fe", "nodeId": "ns=1;s=TestVariables", "browsename": "Variables", "displayname": "Variables", "objecttype": "FolderType", "datatype": "", "value": "", "referenceNodeId": "ns=0;i=85", "referencetype": "Organizes", "name": "Folder TestVariables", "x": 540, "y": 340, "wires": [ [ "2c72a02e95e2e53a" ] ] }, { "id": "57a7deb0b12064d7", "type": "OPCUA-IIoT-Inject", "z": "526638e31bd911fe", "injectType": "inject", "payload": "", "payloadType": "date", "topic": "", "repeat": "", "crontab": "", "once": true, "startDelay": "", "name": "", "addressSpaceItems": [], "x": 250, "y": 340, "wires": [ [ "a94182acacad6858" ] ] }, { "id": "09ed80c457c3501a", "type": "OPCUA-IIoT-Server-ASO", "z": "526638e31bd911fe", "nodeId": "ns=1;s=TestDateTime", "browsename": "TestDateTime", "displayname": "Test DateTime", "objecttype": "BaseDataVariableType", "datatype": "DateTime", "value": "", "referenceNodeId": "ns=1;s=TestVariables", "referencetype": "Organizes", "name": "DateTime", "x": 580, "y": 380, "wires": [ [ "2c72a02e95e2e53a" ] ] }, { "id": "f824a67fc665769f", "type": "OPCUA-IIoT-Inject", "z": "526638e31bd911fe", "injectType": "inject", "payload": "", "payloadType": "date", "topic": "", "repeat": "", "crontab": "", "once": true, "startDelay": "15", "name": "", "addressSpaceItems": [], "x": 250, "y": 380, "wires": [ [ "09ed80c457c3501a" ] ] }, { "id": "2c72a02e95e2e53a", "type": "function", "z": "526638e31bd911fe", "name": "thru", "func": "\nreturn msg;", "outputs": 1, "timeout": "", "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 854.3000183105469, "y": 389.9999694824219, "wires": [ [ "75df2f23680c144e" ] ] }, { "id": "9b7c6cf023d4b643", "type": "OPCUA-IIoT-Server-ASO", "z": "526638e31bd911fe", "nodeId": "ns=1;s=Variable1", "browsename": "Variable1", "displayname": "Variable1", "objecttype": "BaseDataVariableType", "datatype": "Int16", "value": "", "referenceNodeId": "ns=1;s=TestVariables", "referencetype": "Organizes", "name": "Variable1Int16", "x": 560, "y": 420, "wires": [ [ "2c72a02e95e2e53a" ] ] }, { "id": "9f0e92a8743be1eb", "type": "OPCUA-IIoT-Inject", "z": "526638e31bd911fe", "injectType": "inject", "payload": "125", "payloadType": "num", "topic": "", "repeat": "", "crontab": "", "once": true, "startDelay": "15", "name": "", "addressSpaceItems": [], "x": 270, "y": 420, "wires": [ [ "9b7c6cf023d4b643" ] ] }]


                                  Send data to the server

                                  What this sequence of blocks does is, every second it reads the value I0.10 from the Raspberry PLC, on the one hand it shows the result on the dashboard and, on the other hand, it injects the value into a specified node or variable, writing the read value.

                                  Set of blocks to inject data to an OTA server

                                  The blocks we have used are:

                                  • In the OPCUA-IIoT-Node block, or WRITE type node, we specify the veriable to which you want to send the data and what type of data it is.

                                  Node configuration

                                  • The OPC UA-IoT-Write block is responsible for connecting to the server that we specify.

                                  Write node configuration

                                  Here we can configure the endpoint of our server where to send the data to the specified variable or node.

                                  Connector node configuration

                                  Here is the code, which corresponds to the previous set of blocks:

                                  [ { "id": "63f010ebc6ec597c", "type": "tab", "label": "Flow 2", "disabled": false, "info": "", "env": [] }, { "id": "272bd91f019aaa4a", "type": "rpiplc-analog-read", "z": "63f010ebc6ec597c", "rpiplc": "9440adac8659dc1d", "pin": "I0.10", "name": "", "x": 540, "y": 340, "wires": [ [ "72bb01129893f1ae", "27654d48016fa1bf" ] ] }, { "id": "4591787bbefbd0b3", "type": "OPCUA-IIoT-Write", "z": "63f010ebc6ec597c", "connector": "875fa40fca5ccc47", "name": "", "justValue": true, "showStatusActivities": false, "showErrors": false, "x": 850, "y": 340, "wires": [ [ "e4d285975b908c13" ] ] }, { "id": "e4d285975b908c13", "type": "debug", "z": "63f010ebc6ec597c", "name": "debug-set", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "statusVal": "", "statusType": "auto", "x": 1040, "y": 340, "wires": [] }, { "id": "62aeb4828a504caa", "type": "inject", "z": "63f010ebc6ec597c", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "1", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 370, "y": 340, "wires": [ [ "272bd91f019aaa4a" ] ] }, { "id": "72bb01129893f1ae", "type": "OPCUA-IIoT-Node", "z": "63f010ebc6ec597c", "injectType": "write", "nodeId": "ns=1;s=Variable1", "datatype": "Int16", "value": "", "name": "", "topic": "", "showErrors": false, "x": 690, "y": 340, "wires": [ [ "4591787bbefbd0b3" ] ] }, { "id": "27654d48016fa1bf", "type": "ui_gauge", "z": "63f010ebc6ec597c", "name": "", "group": "0cfb93c85cf539c8", "order": 0, "width": 0, "height": 0, "gtype": "gage", "title": "IO.10", "label": "Volts", "format": "{{value}}", "min": 0, "max": "2000", "colors": [ "#00b500", "#e6e600", "#ca3838" ], "seg1": "", "seg2": "", "diff": false, "className": "", "x": 690, "y": 280, "wires": [] }, { "id": "9440adac8659dc1d", "type": "rpiplc-config", "model": "RPIPLC_21", "name": "" }, { "id": "875fa40fca5ccc47", "type": "OPCUA-IIoT-Connector", "discoveryUrl": "", "endpoint": "opc.tcp://localhost:55488/", "endpointMustExist": false, "keepSessionAlive": true, "loginEnabled": false, "name": "LOCAL SERVER", "showErrors": false, "securityPolicy": "None", "securityMode": "None", "individualCerts": false, "publicCertificateFile": "", "privateKeyFile": "", "defaultSecureTokenLifetime": "", "autoSelectRightEndpoint": false, "strategyMaxRetry": "", "strategyInitialDelay": "", "strategyMaxDelay": "", "strategyRandomisationFactor": "", "requestedSessionTimeout": "", "connectionStartDelay": "", "reconnectDelay": "", "maxBadSessionRequests": "10" }, { "id": "0cfb93c85cf539c8", "type": "ui_group", "name": "Default", "tab": "39450c9e599a8dde", "order": 1, "disp": true, "width": "6", "collapse": false, "className": "" }, { "id": "39450c9e599a8dde", "type": "ui_tab", "name": "Home", "icon": "dashboard", "disabled": false, "hidden": false }]


                                  Request data from the server

                                  In this case we want to read the value of a node, for that we must make a request to the OPC UA server, for this we will use these blocks, belonging to the other library. What this example does is simple, every second it makes the request to the variable that we specify and displays it, both as a message in the debug panel and in the dashboard.

                                  Set of blocks to get data to an OTA server

                                  The blocks we have used are:

                                  • The OpcUa-Item block, in this block the type of data to be collected is specified.

                                  Item node configuration

                                  • The OpcUa-Client block will be in charge of pointing to the endpoint, performing the action that we will specify as READ.

                                  Client node configuration


                                  Here is the code, which corresponds to the previous set of blocks:

                                  [ { "id": "e0635bf35b9bc5c7", "type": "tab", "label": "Flow 3", "disabled": false, "info": "", "env": [] }, { "id": "f7aab83e0a06b0ba", "type": "OpcUa-Item", "z": "e0635bf35b9bc5c7", "item": "ns=1;s=Variable1", "datatype": "Int16", "value": "", "name": "", "x": 480, "y": 280, "wires": [ [ "c9cbb2df0b559691" ] ], "icon": "font-awesome/fa-tag" }, { "id": "c9cbb2df0b559691", "type": "OpcUa-Client", "z": "e0635bf35b9bc5c7", "endpoint": "9911d4815d3e3bdd", "action": "read", "deadbandtype": "a", "deadbandvalue": 1, "time": 10, "timeUnit": "s", "certificate": "n", "localfile": "", "localkeyfile": "", "folderName4PKI": "", "useTransport": false, "maxChunkCount": "", "maxMessageSize": "", "receiveBufferSize": "", "sendBufferSize": "", "name": "", "x": 680, "y": 280, "wires": [ [ "95de452cad37e33f", "4faa4ea77b213280" ], [] ], "icon": "font-awesome/fa-tasks" }, { "id": "9e76fd28ede4da39", "type": "inject", "z": "e0635bf35b9bc5c7", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "1", "crontab": "", "once": true, "onceDelay": "15", "topic": "", "payload": "1", "payloadType": "num", "x": 270, "y": 280, "wires": [ [ "f7aab83e0a06b0ba" ] ] }, { "id": "95de452cad37e33f", "type": "debug", "z": "e0635bf35b9bc5c7", "name": "debug-get", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "statusVal": "", "statusType": "auto", "x": 900, "y": 300, "wires": [] }, { "id": "4faa4ea77b213280", "type": "ui_gauge", "z": "e0635bf35b9bc5c7", "name": "", "group": "0cfb93c85cf539c8", "order": 2, "width": 0, "height": 0, "gtype": "gage", "title": "Server Read", "label": "Volts", "format": "{{value}}", "min": 0, "max": "2000", "colors": [ "#00b500", "#e6e600", "#ca3838" ], "seg1": "", "seg2": "", "diff": false, "className": "", "x": 910, "y": 260, "wires": [] }, { "id": "9911d4815d3e3bdd", "type": "OpcUa-Endpoint", "endpoint": "opc.tcp://10.10.10.20:55488/", "secpol": "None", "secmode": "None", "none": true, "login": false, "usercert": false, "usercertificate": "", "userprivatekey": "" }, { "id": "0cfb93c85cf539c8", "type": "ui_group", "name": "Default", "tab": "39450c9e599a8dde", "order": 1, "disp": true, "width": "6", "collapse": false, "className": "" }, { "id": "39450c9e599a8dde", "type": "ui_tab", "name": "Home", "icon": "dashboard", "disabled": false, "hidden": false }]


                                  See also

                                  How to install and use Rapid SCADA on Touchberry Pi

                                  Read the Post >>>

                                  Here I leave the link to the libraries used in case you want to go even deeper into this technology.


                                  ​Search in our Blog

                                  Example of using OPC UA server with Raspberry PLC
                                  Boot & Work Corp. S.L., David Coronado January 16, 2024
                                  Share this post

                                  Looking for your ideal Programmable Logic Controller?

                                  Take a look at this product comparison with other industrial controllers Arduino-based. 

                                  We are comparing inputs, outputs, communications and other features with the ones of the relevant brands.


                                  Industrial PLC comparison >>>