A few month ago, I wrote about how to collect telemetry from The Things Network back-end and send them to an Azure IoT Hub.
The code was simple and it only provided telemetry for one device. But the technology used, was working.
Last month, I was involved in creating a workshop for the TechDays 2016 in Amsterdam. The two-hour workshop gives a good impression how to build an IoT Backend in Azure. During this workshop, we used a NodeJs bridge to pass the telemetry to Azure.
This bridge is available at using the NPM package installer: npm install –save ttn-azure-iothub@preview
I got inspired by this bridge and now I have rebuilt my bridge to handle multiple devices and more!
So I had several goals during the creation of the bridge:
- Automatically register new TTN nodes as devices in an Azure IoT Hub
- Handle disabled devices
- Pass D2C (uplink) telemetry and pass C2D (downlink) commands
- Remove unused devices after a few minutes
- Handle MQTT disconnection
This resulted in a bridge, written in C# and available on GitHub.
Registering new devices
To register devices is not that hard. You need enough right (The IoT Hub Owner has that power). Just make a call like _registryManager.AddDeviceAsync(new Device(deviceId)); and the device is created. This can result in an exception DeviceAlreadyExistsException, In that case, call _registryManager.GetDeviceAsync(deviceId);
using this device, we know if the device is disabled and we have the access keys.
Handle disabled devices
So we check if the (already existing) device is disabled first. If so, we stop passing any communication.
Pass D2C (uplink) telemetry and C2D (downlink) commands
Using the enabled device, we can create a DeviceClient. To send D2C telemetry, I made it possible to select the primary or secondary key using the configuration.
Passing D2C telemetry is easy, just pass it on using the DeviceClient. But reacting to commands is a bit harder. For that, we need a stateful thread for every device registered, which listens for commands coming from the IoT Hub. So I created a dictionary and collect every DeviceClient created. And with every DeviceClient, I also created that thread.
But wait, What should I pass exactly?
Look at the telemetry I get from The Things Network:
Note that the ‘fields’ is the actual telemetry we get from the node (actually, from the payload functions of the TTN application). So I could pass much more information (like the gateway which received the telemetry or even the Latitude and Longitude of that gateway).
For now, I only pass the original payload, nothing more.
Reacting on commands is a bit harder. For that, we need a stateful thread for every device registered, which listens for commands coming from the IoT Hub. So I created a dictionary and collect every DeviceClient created. And with every DeviceClient, I also created that thread.
Now IoT Hub commands passed to the bridge can also be passed on to the TTN Back-end.
Update: The downlink functionality is updated and is now confirmed to work with both Gateways and some package forwarders (eg. https://github.com/bokse001/dual_chan_pkt_fwd)
Remove unused devices after a few minutes
But if a TTN Node is only communication once every day or so, we are using a lot of resources (waiting for commands) for no reason.
So I also remember the last time the node has communicated. The time is also configurable. I then try to remove all devices which are muted, every time a call is made.
Handle MQTT disconnection
My biggest fear is stability. And one of the thing that can happen is that the MQTT connection can be closed (don’t know why). But an event is raised when it happens and I can react to it. In that case, I just close the entire application! This seems strange when the app is running as a console app on your computer.
But this application is designed to run in the cloud as a WebJob.
In that case, when the application is closed, the WebJob will automatically restart the application again:
[11/04/2016 11:33:06 > 2f8a3a: INFO] time 11/4/2016 11:33:06 AM -> MQTT connection closed. Exit for restart. [11/04/2016 11:33:06 > 2f8a3a: SYS ERR ] Job failed due to exit code 1 [11/04/2016 11:33:07 > 2f8a3a: SYS INFO] Process went down, waiting for 0 seconds [11/04/2016 11:33:07 > 2f8a3a: SYS INFO] Status changed to PendingRestart [11/04/2016 11:33:07 > 2f8a3a: SYS INFO] WebJob singleton lock is released [11/04/2016 11:33:07 > 2f8a3a: SYS INFO] WebJob singleton lock is acquired [11/04/2016 11:33:08 > 2f8a3a: SYS INFO] Run script 'TtnAzureBridge.exe' with script host - 'WindowsScriptHost' [11/04/2016 11:33:08 > 2f8a3a: SYS INFO] Status changed to Running [11/04/2016 11:33:09 > 2f8a3a: INFO] time 11/4/2016 11:33:09 AM -> IoT Hub gpstestih connected [11/04/2016 11:33:09 > 2f8a3a: INFO] MQTT KeepAlivePeriod is 60000 [11/04/2016 11:33:09 > 2f8a3a: INFO] MQTT subscribed to 70B3D57ED00013AA on staging.thethingsnetwork.org [11/04/2016 12:33:07 > 2f8a3a: SYS INFO] WebJob is still running
Note: See my previous blog for more details about restarting WebJobs
This bridge could be of great value when you want to connect the TTN world with your Azure IoT Platform. Please check it out at my GitHub account.
If you are interested in the Workshop, here is the place to be.