Industrial network communications
OPC UA on a Raspberry Pi
Use Python and Node-RED to create an industrial client-server test system.
Industrial operations such as chemical refineries, power plants, and mineral processing operations have different network communications requirements than most IT installations. Some of the key industrial communication requirements include security, multivendor connectivity, time stamping of data, and alarm or quality indicators.
To meet industrial requirements, a communications standard called OLE for Process Control (OPC) [1] was created. The original OPC design was based on Microsoft's Object Linking and Embedding (OLE) technology, and it quickly became the standard for communications between control system consoles, historians (data storage devices), and third-party applications (Figure 1).
The original OPC standard worked well, but it had major limitations in the areas of Linux and embedded systems, routing across wide-area networks (WANs), and new security concerns. To better address new industrial requirements, the Open Platform Communications Unified Architecture (OPC UA) standard [2] was created.
In this article, I look at three small projects that introduce you to OPC UA. The first project uses Python to create an OPC UA server with a client and a graphical user interface (GUI). The second project builds a Node-RED OPC UA server and client app. The final project uses Node-RED web dashboards for read-write access to a Python OPC UA server.
Getting Started
For my test system, I used a Raspberry Pi 4 and an Ubuntu laptop, but the test could also be done on a single Raspberry Pi or laptop. You have a number of OPC UA open source servers from which to choose. For C development applications, the open62541 project [3] offers a C99 architecture that runs on Windows, Linux, VxWorks, QNX (BlackBerry), Android, and a number of embedded systems.
For lightweight, quick testing, OPC UA servers are available in Python and Node-RED. The Free OPC-UA Library project [4] has a great selection of open source tools for people who want to learn and play with OPC UA.
To keep things simple, I am using the python-opcua library, which is a pure Python OPC UA server and client. (Note that a more complete Python OPC UA library, opcua-asyncio, is available for more detailed work.)
The Free OPC UA project also includes a number of useful tools, such as an OPC UA modeler and client GUI. The commands
# Python OPC UA server/client install sudo apt install python-opcua # OPC UA client/QT dependencies sudo apt install PyQT* pip3 install opcua-client # OPC UA modeler tool $ pip3 install opcua-modeler
load these items in Ubuntu/Debian/Raspian environments.
Simple Python OPC UA Server
As a first project, I create a simple OPC UA server with some simulated OPC UA tags (Listing 1) [5]. The first steps in setting up an OPC UA server are to define a server name, set a network location endpoint, and register the namespace (lines 7-12). A little later you will see that the namespace index is very important in the definition of OPC UA tags. To simplify my code, I hard-coded the IP address (lines 9 and 12). For a more flexible setup, the Python socket library could be used to determine this value for the endpoint and namespace.
Listing 1
Python OPC UA Simple Server
01 # opcua_server1.py - Create an OPC UA server and simulate 2 tags 02 # 03 import opcua 04 import random 05 import time 06 07 s = opcua.Server() 08 s.set_server_name("OpcUa Test Server") 09 s.set_endpoint("opc.tcp://192.168.0.120:4841") 10 11 # Register the OPC UA namespace 12 idx = s.register_namespace("http://192.168.0.120:4841") 13 # start the OPC UA server (no tags at this point) 14 s.start() 15 16 objects = s.get_objects_node() 17 # Define a Weather Station object with some tags 18 myobject = objects.add_object(idx, "Station") 19 20 # Add a Temperature tag with a value and range 21 myvar1 = myobject.add_variable(idx, "Temperature", 25) 22 myvar1.set_writable(writable=True) 23 24 # Add a Windspeed tag with a value and range 25 myvar2 = myobject.add_variable(idx, "Windspeed", 11,4,4) 26 myvar2.set_writable(writable=True) 27 28 # Cycle every 5 seconds with simulated data 29 while True: 30 myvar1.set_value(random.randrange(25, 29)) 31 myvar2.set_value(random.randrange(10, 20)) 32 time.sleep(5)
The OPC UA structure is based on objects and files, under which tags are configured. For this example a Station
object is created (line 18), with Temperature
and Windspeed
tags (lines 21-26). The final step is to use the set_value
method to update the tags with random values (lines 30-31).
The Free OPC UA project's opcua-client GUI tool lets you view the OPC UA server and its tags. Figure 2 shows the server with two simulated tags and their NodeIDs.
OPC UA items are accessed by their NodeIDs. The OPC UA server will autocreate a NodeID if one isn't defined when an object or tag is added. In this example, Temperature
is given the NodeID ns=2,i=2
, which means it has an index of 2 in node space 2. In the next sections, I show how NodeIDs are used and how they can be created.
In OPC UA, the terms "tags" and "variables" are often used interchangeably. In an industrial plant, hardware signals such as pumps and sensors are usually referred to as "tags" in the control systems; however, within the OPC UA server, the term "variable" is used. The key difference is that a variable can also be an internal or software-generated point, such as a counter.
Python OPC UA Client App
The opcua library is used for both server and client applications. An OPC UA client app only requires five lines to connect to a server and define a NodeID object and get its value. Listing 2 shows how to get the previously defined Temperature
tag.
Listing 2
Getting a Tag Value
>>> import opcua >>> >>> # Connect as an OPC UA client and get a tag value >>> client = opcua.Client("opc.tcp://192.168.0.120:4841") >>> client.connect() >>> temptag = client.get_node("ns=2;i=2") >>> temptag.get_value() 26
The Client
method defines the OPC UA server's endpoint location, and the connect
method enables the client connection. An OPC UA NodeID object is defined by a namespace number (ns
) and an index (i
), and the get_value
method returns the current value. For my Python client application, I use this basic code to show tag values on a gauge chart (Figure 3).
The Python tk_tools [6] library contains some useful graphic widgets (e.g., charts, gauges, LEDs, seven-segment displays). To install, enter:
pip install tk_tools
The client app (Listing 3) creates a Tkinter object (line 11) and then adds two gauge components (lines 15-20). The Tkinter after(<msec>,<function_name>)
method calls an update function (line 26) that refreshes the gauge components with OPC UA tag updates (lines 26 and 28).
Listing 3
Python OPC UA Client App
01 # station1.py - Put OPC UA data into gauges 02 # 03 import tkinter as tk 04 import tk_tools 05 import opcua 06 07 # Connect to the OPC UA server as a client 08 client = opcua.Client("opc.tcp://192.168.0.120:4841") 09 client.connect() 10 11 root = tk.Tk() 12 root.title("OPC-UA Weather Station 1") 13 14 # Create 2 gauge objects 15 gtemp = tk_tools.Gauge(root, height = 200, width = 400, 16 max_value=50, label='Temperature', unit='infinityC') 17 gtemp.pack() 18 gwind = tk_tools.Gauge(root, height = 200, width = 400, 19 max_value=100, label='Windspeed', unit='kph') 20 gwind.pack() 21 22 def update_gauge(): 23 # update the gauges with the OPC UA values every 1 second 24 gtemp.set_value(client.get_node("ns=2;i=2").get_value()) 25 gwind.set_value(client.get_node("ns=2;i=5").get_value()) 26 root.after(1000, update_gauge) 27 28 root.after(500, update_gauge) 29 30 root.mainloop()
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
News
-
Latest Cinnamon Desktop Releases with a Bold New Look
Just in time for the holidays, the developer of the Cinnamon desktop has shipped a new release to help spice up your eggnog with new features and a new look.
-
Armbian 24.11 Released with Expanded Hardware Support
If you've been waiting for Armbian to support OrangePi 5 Max and Radxa ROCK 5B+, the wait is over.
-
SUSE Renames Several Products for Better Name Recognition
SUSE has been a very powerful player in the European market, but it knows it must branch out to gain serious traction. Will a name change do the trick?
-
ESET Discovers New Linux Malware
WolfsBane is an all-in-one malware that has hit the Linux operating system and includes a dropper, a launcher, and a backdoor.
-
New Linux Kernel Patch Allows Forcing a CPU Mitigation
Even when CPU mitigations can consume precious CPU cycles, it might not be a bad idea to allow users to enable them, even if your machine isn't vulnerable.
-
Red Hat Enterprise Linux 9.5 Released
Notify your friends, loved ones, and colleagues that the latest version of RHEL is available with plenty of enhancements.
-
Linux Sees Massive Performance Increase from a Single Line of Code
With one line of code, Intel was able to increase the performance of the Linux kernel by 4,000 percent.
-
Fedora KDE Approved as an Official Spin
If you prefer the Plasma desktop environment and the Fedora distribution, you're in luck because there's now an official spin that is listed on the same level as the Fedora Workstation edition.
-
New Steam Client Ups the Ante for Linux
The latest release from Steam has some pretty cool tricks up its sleeve.
-
Gnome OS Transitioning Toward a General-Purpose Distro
If you're looking for the perfectly vanilla take on the Gnome desktop, Gnome OS might be for you.