Advertisement

IIoT edge development - Using OPC UA protocols

Alena Traukina, Jayant Thomas, Prashant Tyagi, Kishore Reddipalli

February 11, 2019

Alena Traukina, Jayant Thomas, Prashant Tyagi, Kishore ReddipalliFebruary 11, 2019

Editor's Note: The Industrial Internet of Things (IIoT) promises to provide deep insight into industrial operations and enhance efficiency of connected machines and systems. Large-scale IIoT applications rely on layered architectures to collect data from a broad range of sensors, move data reliably and securely to the cloud, and perform analysis required to deliver that insight and efficiency. In Industrial Internet Application Development, the authors provide a detailed examination of the IIoT architecture and discuss approaches for meeting the broad requirements associated with these systems. 

 

Adapted from Industrial Internet Application Development, by Alena Traukina, Jayant Thomas, Prashant Tyagi, Kishore Reddipalli.


Chapter 3. IIoT Edge Development (Continued)
By Alena Traukina, Jayant Thomas, Prashant Tyagi, Kishore Reddipalli

Industrial M2M protocols – OPC UA

In this section, we will try to build a simple IoT app for sending data from a sensor simulator module to a receiver device (a PC or a cloud), using a Raspberry Pi hub and the OPC UA protocol:


Data flow from a sensor simulator to a receiver device

The OPC UA protocol is similar to Modbus, but works with more data types, and has no serious limitations, while providing for security, compression, and low latency.

The protocol was developed by the OPC Foundation as an industrial machine-to-machine communication protocol. OPC UA (Unified Architecture) is an improved version of the Open Platform Communications (OPC) protocol, with one of the major changes being that the new protocol is available free of charge without any restrictions.

In the following table, you can find a more detailed description of the protocol to understand whether it is suitable for your needs:

Key Value
Open source Yes
The OSI layer Transport or application
Data types Integer, float, string, Boolean, date, time, and so on
Limitations Not suitable for a complex architecture
Possible operations Read/write/monitor/query variables
Latency Low
Usage IIoT
Security Yes
Compression Yes

Table 5: OPC UA protocol specifications

For building the application, we will need the following:

  • Required software
    • Node.js 6+ (https:/​/​nodejs.​org/​en/​download/​)
    • PostgreSQL (https:/​/​www.​postgresql.​org/​download/​)
    • The Cloud Foundry CLI (https:/​/​github.​com/​cloudfoundry/cli#downloads)
    • Request (https:/​/​www.​npmjs.​com/​package/​request)
    • NodeOPCUA (https:/​/​www.​npmjs.​com/​package/​node-​opcua)
    • Async (https:/​/​www.​npmjs.​com/​package/​async)
    • Docker (https:/​/​docs.​docker.​com/​engine/​installation/​)
  • Required hardware
    • Raspberry Pi 3 (model B)
    • A power adapter (2A/5V)
    • A microSD card (8 GB+) and an SD adapter
    • Ethernet cable for a wired network connection   
         

Preparing an SD card

To prepare an SD card, follow the sequence of actions as described:

  1. Download the latest Raspbian LITE image (available at https:// raspberrypi.org/downloads/raspbian/).

  2. Connect your SD card to a computer and use Etcher (https://io/) to flash the Raspbian .img file to the SD card.

  3. Enable SSH:

cd /Volumes/boot touch ssh

  1. To enable Wi-Fi, create conf with the following content:

network={
   ssid="YOUR_SSID"
   psk="YOUR_WIFI_PASSWORD"
}

 
To create a file in a Linux console, you can use the GNU nano editor. It is pre-installed in most Linux distributives. All you need is to run the nano FILE_NAME command and follow the displayed instructions.
  1. Create the /home/pi/hub

  2. Create the /home/pi/hub/package.json file with the following content:

{
   "name": "hub",
   "version": "1.0.0",
   "description": "",
   "main": "index.js", "scripts": {
      "start": "node index.js",
      "test": "echo \"Error: no test specified\" && exit 1"
   },
   "author": "",
   "license": "ISC", "dependencies": {
      "async": "^2.4.0",
      "node-opcua": "0.0.64",
      "request": "^2.81.0"
   }
}

  1. Create the /home/pi/hub/index.js file with the following content, replacing

REMOTE-SERVER-ADDRESS.com and REMOTE-SENSOR-ADDRESS with real values:
var opcua = require("node-opcua"); var async = require("async");
var request = require("request");
var session, subscription;
var client = new opcua.OPCUAClient();
var sensor = "opc.tcp://REMOTE-SENSOR- ADDRESS:4334/UA/resourcePath";
var receiver = "http://REMOTE-SERVER-ADDRESS.com:8080";
async.series( [
// establishing connection function (cb) {
   client.connect(sensor, function (err) {
      if (err) {
         console.log("Connection to " + sensor + "failed");
      } else {
         console.log("Connection successful");
      }
      cb(err);
   });
},
// start session function (cb) {
   client.createSession(function (err, res) {
      if (!err) session = res;
      cb(err);
   });
},
// read value
function (cb) {
   session.readVariableValue("ns=1;s=Variable1", function (err, dataValue) {
      if (!err) console.log("Variable1 = ", dataValue.value.value);
      cb(err);
   });
},
// write value
function (cb) {
   session.writeSingleNode("ns=1;s=Variable1", new opcua.Variant({
      dataType: opcua.DataType.Double, value: 100
   }), function (err) {
      cb(err);
   });
},
// subscribe to changes
function (cb) {
   subscription = new opcua.ClientSubscription(session, {
      maxNotificationsPerPublish: 5,
      priority: 5,
      publishingEnabled: true,
      requestedLifetimeCount: 5,
      requestedMaxKeepAliveCount: 3,
      requestedPublishingInterval: 500,
   });
   subscription.on("started", function () {
      console.log("subscription id: ",
      subscription.subscriptionId);
   }).on("terminated", function () {
      cb();
   });
   setTimeout(function () {
      subscription.terminate();
   }, 5000);
   // install monitored item
   var monitor = subscription.monitor({
      attributeId: opcua.AttributeIds.Value,
      nodeId: opcua.resolveNodeId("ns=1;s=Variable1"),
   },
   {
      discardOldest: true,
      samplingInterval: 50,
      queueSize: 5,
   },
   opcua.read_service.TimestampsToReturn.Both
   );
   monitor.on("changed", function (dataValue) {
      console.log("Variable1 = ", dataValue.value.value);
      // send to receiver
      var data = {
         device: "sensor1",
         timestamp: Date.now(),
         Variable1: dataValue.value.value
      };
   request.post({url: receiver, form: data}, function (err) {
      if (err) console.log("Failed to send " +
      JSON.stringify(data) + " to " + receiver);
   });
});
},
// close session
function (cb) {
   session.close(function (err) {
      if (err) console.log("Failed to close session"); cb();
   });
}
],
function (err) { if (err) {
   console.log("Failed with error:", err);
} else {
   console.log("Successfully finished");
}
client.disconnect(function () {
});
}
);

  1. Create the /home/pi/hub/Dockerfile file with the following content:

FROM hypriot/rpi-node:boron-onbuild

  1. Create the /home/pi/sensor

  2. Create the /home/pi/sensor/package.json file with the following content:

{
   "name": "sensor",
   "version": "1.0.0",
   "description": "",
   "main": "index.js",
   "scripts": {
      "start": "node index.js",
      "test": "echo \"Error: no test specified\" && exit 1"
   },
   "author": "",
   "license": "ISC",
   "dependencies": {
      "node-opcua": "0.0.64"
   }
}

  1. Create the /home/pi/sensor/index.js file with the following content:

var opcua = require("node-opcua");
var min = 1;
var max = 100;
var host = new opcua.OPCUAServer({ buildInfo: {
   buildDate: new Date(2018, 8, 8),
   buildNumber: "1234",
   productName: "productName",
},
port: 4334,
resourcePath: "UA/resourcePath",
});
host.initialize(function () {
   var space = host.engine.addressSpace;
   var componentOf = space.addObject({
      browseName: "browseName",
      organizedBy: space.rootFolder.objects,
   });
   var variable1 = 0;
   // generate new value
   setInterval(function () {
      variable1 = Math.floor(max - Math.random() * (max - min));
   }, 500);
   space.addVariable({
      browseName: "browseName",
      componentOf: componentOf,
      dataType: "Double",
      nodeId: "ns=1;s=Variable1", // a string nodeID
      value: {
         get: function () {
         return new opcua.Variant({dataType: opcua.DataType.Double, value: variable1});
         },
         set: function (variant) {
            variable1 = parseFloat(variant.value);
            return opcua.StatusCodes.Good;
         }
      }
   });
   host.start(function () {
      var endpoint =
      host.endpoints[0].endpointDescriptions()[0].endpointUrl; console.log("Endpoint: ", endpoint);
   });
});

  1. Configure the min and max values at the beginning of the

/home/pi/sensor/index.js file.

  1. Create the /home/pi/sensor/Dockerfile file with the following content:

FROM hypriot/rpi-node:boron-onbuild

Continue reading on page two, Running a simulator application on an RPi >>

< Previous
Page 1 of 2
Next >

Loading comments...