Bridging the gap from third party cloud to IoT Central

A few years ago I blogged about my open source project which makes it possible to connect The Things Network LoraWan cloud to Azure. It runs as a Webjob and provides a stateful bi-directional communication channel so devices from the third party (TTN) cloud are automatically registered in your IoTHub, can communicate their messages to the Azure cloud and they can receive commands back.

Recently Microsoft announced their generic bridge between third party IoT clouds for IoT Central. It is called: IoT Central Device Bridge.

Basically, it supports all cloud services which are able to send device telemetry to a REST endpoint.

Let’s check out how it works.

The current documentation describes how clouds like Sigfox and Particle can connect. It even includes The Things Network.

One architecture picture says more than a thousand words so this is an example based on the Particle Cloud using a Photon device:

The Azure cloud part of the IoT Central Device Bridge is based on Azure Functions. This HTTP Triggered function is generic to every third party cloud call.

Creating the bridge

The enrollment of this bridge resource is pretty straightforward, Microsoft provides a nice tutorial on the GitHub page of the bridge. It even provides a direct deployment button to start a deployment wizard within your Azure subscription:

You only have to fill in the Scope ID and a SAS Key of your already existing IoT Central portal. You can find them in the Administration section:

Once the deployment is ready, a small collection of Azure service is created:

Most important is the Azure Function App service. It runs on a consumption plan and contains one function only. It is triggered by a HTTP/HTPPS call:

The function is written in NodeJS and consists of a few files:

As shown in the manual, there is no need for extra programming. You only have to finalize the NodeJS installation by running ‘npm install’ on the console command line of the azure function:

This can take a while, just wait and ignore possible node messages (I got one message regarding a file lock). Once the installation is done, restart the Azure Function App Service.

Testing the bridge

As said already, the bridge just exposes a REST endpoint. You have to copy the function URL (based on the default function key) for later usage:

How do we test is it works? That is simple, it should even be possible to test it with Postman. Let’s do that.

We only need to know that endpoint (which we already have) and the format of the body of the POST action. The measurement nodes are arbitrary:

    "device": {
        "deviceId": "postmantest"
    "measurements": {
        "temp": 20.31,
        "pressure": 50,
        "humidity": 8.5,
        "ledColor": "blue"

Note: this format of Bridge input messages supports only one level within the measurements node (except for the optional location node).

If we send this message with Postman, this is what happens:

That’s interesting, an ‘error’ message is returned, “Unable to register device ‘postmantest’: The device may be unassociated or blocked”:

Actually, this message is coming from the IoT Central already, as seen in the logging of the Azure function:

It’s interesting to see that the function successfully registers the device because it is not yet known in IoT Central. But it’s blocked or unassociated… What does that mean?

When we look up the device in IoT Central, we see it’s not yet associated to an device template:

Let’s change that. Let’s create a device template first which matches the arbitrary nodes in the Postman message body:

And we associate our Postman device with the template in IoT Central:

Now, send the same message a few more times and notice we now get a ‘200 OK’ as response:

And we see the first incoming messages in IoT Central:

So the bridge is working as described. And it is generic enough for all kinds of solutions.

Keep in mind, the bridge is only supporting uni-directional communication. If you want to send commands back, the current bridge is not supporting that functionality.

Note: The Azure function is deployed with a Consumption plan. The main advantages are that you do not have to worry about scale and the first million executions are included for free each month! But the function needs some time to ‘heat up’ if it is not called regularly. So while testing there is a chance you encounter some time-outs…

Testing it with the Particle cloud

Again, the documentation gives some hints on how to connect devices living on the Particle cloud to IoT Central using the bridge.

We connect a Photon having on a Sparkfun Weather shield.

First, we create a Partical Cloud integration of type ‘webhook’ on the Particle Cloud portal. I name it ‘iotcentral’:

I then fill in the URL of our Azure Function and I tell it to submit a Post with a body of type JSON:

But we have to make a little addition to the Advanced settings before we save the settings. We have to set the custom body format. Here is a format that supports the weather shield:

  "device": {
    "deviceId": "{{{PARTICLE_DEVICE_ID}}}"
  "measurements": {
    "tf": {{tf}},
    "hy": {{hy}},
    "bp": {{bp}},
    "ps": {{ps}},
    "af": {{af}}

Note: This format is not as generic as the one provided in the Bridge documentation. But I never got that original format working, this one does. Trivia: I was inspired to do it like this due to a blog I wrote three years ago.

I got this red cross:

That is a ‘bad string’ warning due to no quotes around the values. Just ignore it…

Once save, let’s check out the code to compile and deploy on the Photon.

First we have to start a new project and search for the library ‘SPARKFUN_PHOTON_WEATHER_SHIELD_LIBRARY’:

And we include it in the project:

Now we write our own code:

// This #include statement was automatically added by the Particle IDE.
#include <SparkFun_Photon_Weather_Shield_Library.h>

// This #include statement was automatically added by the Particle IDE.
#include <SparkFun_Photon_Weather_Shield_Library.h>

float humidity = 0;
float tempf = 0;
float pascals = 0;
float baroTemp = 0;
float altf = 0;
Weather sensor;

void setup() {
  sensor.setModeBarometer();//Set to Barometer Mode
  sensor.setOversampleRate(7); // Set Oversample rate
  sensor.enableEventFlags(); //Necessary register calls to enable temp, baro and alt

void loop() {
  char payload[256];
           "{ \"tf\":%f, \"hy\":%f, \"bp\":%f, \"ps\":%f, \"af\":%f }",
           tempf, humidity, baroTemp, pascals, altf);
  Particle.publish("iotcentral", payload);
  delay(60000); // Send a message every 60 seconds

void getWeather() {
  humidity = sensor.getRH();
  tempf = sensor.getTempF();
  baroTemp = sensor.readBaroTempF();
  pascals = sensor.readPressure();
  altf = sensor.readAltitudeFt();  // if in altimeter Mode

Once deployed to the photon, we have to follow the same steps as with the Postman test. We have to create the Device Template and we have associate our Photon with it.

This is the Device Template for our weather shield data:

Note: I was a bit lazy with the names. I just took the same names as the JSON fields.

And I associated the Photon to the new Device Template:

Now let’s look at the History of the Webhook. As you can see, I first had some issues with the template (the red part). After that, the device was not associated so I got some rejections (the blue part). And finally, messages were accepted. That it the green part:

Finally, here are the weather shield values arriving:


The IoT Central Device bridge is a generic solution of all sorts of Device clouds which are able to make REST calls to the Azure Function REST endpoint.

Microsoft provides documentation to connect to The Things Network, Sigfox and Particle cloud.

You actually only need one bridge to support multiple third party clouds.

It’s even possible to use it as an alternative for non-IoT devices or IoT Edge devices. But keep in mind the communication is uni-directional and only support simple message structures (one layer of JSON nodes deep).

Een gedachte over “Bridging the gap from third party cloud to IoT Central

Reacties zijn gesloten.