Connecting OPC UA Publisher to Azure IoT Central with MQTT
20.07.2022
Azure IoT Central is an easily scalable application platform as a service (aPaaS) that streamlines creation and maintenance of IoT devices. It lowers the entry barrier for clients with limited technological knowledge. It also has built-in visualisation which does not require Power BI.
While Azure IoT Central has some distinct features compared to Azure IoT Hub, we recommend reading our IoT Hub demo, as well. IoT Central uses a underlaying IoT Hub and as there is no IoT Central SDK, the user needs to use IoT Hub SDK for programmatic implementations.
Even though IoT Central is not a fully compliant MQTT broker, it can still act as an OPC UA subscriber and receive OPC UA messages. Device-to-cloud messaging works normally, but there are some connection errors occurring when trying to send messages from another MQTT broker to Azure IoT Central i.e. cloud-to-cloud messages.
Azure IoT Central application and device
To start using IoT Central, you need to create an Azure IoT Central application and create a device in the application. We created a blank custom application with a blank device without any device template. The template will be assigned in a later step.
Provisioning the device
Connecting a device to Azure IoT Central requires you to provision the device to IoT Hub using Azure IoT Hub Device Provisioning Service (DPS). This process allocates a position in IoT Hub for the device. Note that the IoT Hub position is not permanent and the provisioning process should be repeated every time device is turned on. After the device owner/user has added the device registration information to the DPS device enrolment list in the Azure IoT Central, the provisioning process can be done on device boot automatically and requires no effort from the user.
In order to connect to the IoT Central with MQTT, we need to generate connection strings <iot_hub_hostname>
and <sas_token>
using the variables <id_scope>
, <device_id>
, <symmetric_key>
and <model_id>
. First three of these variables can be found from the connect page of the previously created IoT Central device. <model_id>
is the name of a device template that defines how the device interacts with IoT Central application.
In our demo we used a small Python program and config.json to generate all necessary data to connect the device to IoT Central.
Azure IoT Hub SDKs offers some plug and play -samples that our code was based on. As our device only needs to send temperature value as telemetry. We used simple example thermostat device template example. <port>
and <provisioning_host>
are normally default values that can not be changed. Default port is 8883
and default provisioning server address is global.azure-devices-provisioning.net
. If you can not use the MQTT default port, you will have to use MQTT over Web Sockets protocol and port 443
.
config.json
{
"model_id": "dtmi:com:example:Thermostat;1",
"port": 8883,
"provisioning_host": "global.azure-devices-provisioning.net",
"id_scope": "xxxx",
"device_id": "xxxx",
"symmetric_key": "xxxx"
}
IoT Hub hostname
.create_from_symmetric_key()
or .create_from_x509_certificate()
is a function provided by Azure Python library that is used to provision the IoT Hub for the given device. The only difference between these functions is the authentication method. In our demo we use The Shared Access Signature (SAS) authentication. X509 is commonly used as well but it does not offer all the same functionality as SAS in Azure.
async def provision_device(provisioning_host, id_scope, registration_id, symmetric_key, model_id):
provisioning_device_client = ProvisioningDeviceClient.create_from_symmetric_key(
provisioning_host=provisioning_host,
registration_id=registration_id,
id_scope=id_scope,
symmetric_key=symmetric_key
)
provisioning_device_client.provisioning_payload = {"modelId": model_id}
return await provisioning_device_client.register()
hostname = (await provision_device(provisioning_host, id_scope, device_id, symmetric_key, model_id)).registration_state.assigned_hub
SAS token
<sas_token>
can be generated with (resource) <uri>
, <symmetric_key>
, <policy_name>
which in our case is None
and <expiry>
in seconds which is set by default to approximately one month. The resource URI is in format <iot_hub_hostname>/devices/<device_id>
. <sas_token>
is used as the password for the publisher. The function returns the generated SAS string as: <sas_token> = "SharedAccessSignature <iot_hub_hostname>%2Fdevices%2F<device_id>&sig={signature}&se={expiry time in epoch}"
When using the default settings, the only variables we need to provide are <symmetric_key>
and <uri>
, where: <uri> = "<hostname>/devices/<device_id>"
def generate_sas_token(uri, symmetric_key, policy_name=None, expiry=2700000):
ttl = time() + expiry
sign_key = "%s\n%d" % ((parse.quote_plus(uri)), int(ttl))
signature = b64encode(HMAC(b64decode(symmetric_key), sign_key.encode('utf-8'), sha256).digest())
rawtoken = {
'sr' : uri,
'sig': signature,
'se' : str(int(ttl))
}
if policy_name is not None:
rawtoken['skn'] = policy_name
return 'SharedAccessSignature ' + parse.urlencode(rawtoken)
sas_token = generate_sas_token(string(hostname+"/devices/"+device_id), symmetric_key)
Now we have all the necessary connection strings that we need to launch the SamplePublisherServer that our Prosys OPC UA SDK for Java provides.
OPC UA Publisher
IoT Central can receive messages from all common publishers. We tested our SamplePublisherServer with Windows, Linux (Raspberry Pi) and macOS with following launch arguments:
samplepublisherserver.bat
--address ssl://<iot_hub_hostname>:<port>
--encoding json
--username "<iot_hub_hostname>/<device_id>/?api-version=2021-04-12&model-id=<model_id>"
--password "sas_token"
--client-id <device_id>
--queue-name devices/<device_id>/messages/events/
--metadata-queue-name devices/<device_id>/messages/events/
--non-reversible-json
While the launch command can be typed to console or terminal in a single line as it is, we recommend creating a new .bat
or .sh
-file to launch the SamplePublisherServer. There might be some issues due to the complexity of the arguments. For example, the argument --password
has {space}
, &
and %
-characters included.
Windows Command Prompt (cmd) and .bat
-files has some issues that are caused by characters {space}
and %
. These problems can be fixed by adding "
before and after the --username
and --password
as seen on the code snippet above. The problem with %
can be fixed by adding a second %
next to it. This issue is caused by the fact that there are two %
-characters in a string, which makes the cmd think that the string between these two is a variable, which breaks the argument.
Unix-based systems, for example the Raspberry Pi we used for this demo had some similar issues. Almost all the issues were caused by the {space}
in the --password
. We had to rewrite part of the samplepublisherserver.sh with the following lines to call --username
and --password
from variables.
USER='<iot_hub_hostname>/<device_id>/?api-version=2021-04-12&model-id=<model_id>'
PASS="<sas_token>"
--username "$USER" --password "$PASS"
--queue-name
and --metadata-queue-name
arguments define the MQTT topics the publisher sends messages to. We use the same MQTT topics in the demo, but you could define metadata to another MQTT topic or leave it empty to save messages to IoT Central as it is billed per message.
--non-reversible-json
is an optional launch commandline argument that removes the network headers that the publisher normally sends. When the argument is given, the publisher sends plain name/value pairs in the following format:
{
"MyLevel": {
"Value": 2,
"SourceTimestamp": "2022-07-07T09:17:26.890Z"
}
}
SamplePublisherServer also sends some event and header messages to the metadata queue that the IoT Central does not need in our case.
Data mapping
IoT Central uses JSONPath expression to map the values from telemetry to corresponding aliases. In our demo JSONPath $["MyLevel"]["Value"]
has been mapped to variable temperature
, which allows the dashboards to search for the corresponding variable from the telemetry messages.
Dashboard
IoT Central uses dashboards to internally visualise the data for quick use. We made a simple dashboard that shows data from the last hour in a linechart and heatmap. Additionally it shows some keyvalues from a longer timespan. The dashboard can be shared to other users of your organization but it can not be published outside.
Device management
Devices connected to IoT Central can be managed in bulk with Jobs. Jobs include management of device cloud properties, properties, templates or calling command to a device or group of devices. Devices can also be imported or exported in bulk with csv.
Extending IoT Central
Rules
Rules are customized response to a trigger on a monitored events in the connected devices. For example, if our thermostat measured temperature over 100°C, we could send email or POST request to other application. There are also advanced rules to expand the triggers further inside and outside of Azure.
Data export
IoT Central is used to connect a large number of devices to Azure. It can be stand-alone but normally its just the first step in Azure IoT System. Data can be exported from IoT Central. For example, we have digital distribution center, which uses different devices and Azure applications that use data exported from devices in IoT Central.
Improvements
The maximum size of an Azure IoT Central message is 4KB, our average message size was 0.1KB and the service is billed per message. If you would want to reduce cost of running large scale IoT Central application, you could send messages without headers and compress key/value pairs list to single JSON array and send it as a single message. For example, if you would have 300 different sensors in a single automation system, you could first locally gather all the data to single “Azure IoT device” and send it in compressed format:
{
"Values": {
"T0": 2,
"T1": 20,
"T2": 10,
...
"T300": 1
},
"SourceTimestamp": "2022-07-07T09:17:26.890Z"
}
This is just a sample message, whereas a real message could also include some header data. The size of this single message is still only around 4KB. This compressed format would need complex Azure IoT Central model template for all the data mapping, but it reduces the price per message cost to 1/300 of the original proposed message style. Using float values with two decimals would still allow use of around 150-200 variables.
Conclusion
We have shown that our Prosys OPC UA SDK for Java is capable of publishing OPC UA data directly to Azure IoT Central. Our implementation demo only uses one device but the IoT Central can easily be upscaled to support multitude of devices.
Do you want to know more?
If you are interested in developing your own Azure or other cloud-based systems with OPC UA connectivity, feel free to contact us. We can provide you with software tools and professional services that enable fast development in this rapidly growing new market.
You can send us an email at sales@prosysopc.com or use the contact form.
Elias Nykänen
Software Engineer
Email: elias.nykanen@prosysopc.com
Tags: Azure, IoT, Cloud, OPC UA, PubSub, MQTT, Java, SDK for Java, Raspberry Pi, Demo
comments powered by DisqusAbout Prosys OPC Ltd
Prosys OPC is a leading provider of professional OPC software and services with over 20 years of experience in the field. OPC and OPC UA (Unified Architecture) are communications standards used especially by industrial and high-tech companies.
Newest blog posts
Why Do Standards Matter in Smart Manufacturing?
The blog post discusses the importance of standards in smart manufacturing, envisioning a future where auto-configurable systems in manufacturing rely on standardized data formats for seamless integration and reduced costs, with a focus on the OPC UA standard family as a key enabler.
OPC UA PubSub to Cloud via MQTT
Detailed overview of the demo presented at the OPC Foundation booth
SimServer How To #3: Simulate data changes on a server using an OPC UA client
A two-part step-by-step tutorial on how to write data changes on an OPC UA server using an OPC UA client.