Closing the Windows IoT Core feedback loop using Azure Functions

Windows IoT Core is my preferred solution for the proof of concepts I build. The IoT stack is both easy and powerful and it’s a good choice to build real world solutions on too.

Getting telemetry in the Cloud using Microsoft Azure IoT Hub is easy also. And in my previous blog, I showed that adding live charts for BI only takes a couple of minutes.

There is one other thing that is very typical to IoT Hub. And that is sending commands back to devices. I use Azure Functions for that purpose.

In this blog, I will show you how to make use of this new, cheap and handy feature in Azure.

Update: Azure Functions is still in preview. I fixed some blocking issues in this blog due to current changes in this Azure resource (and this is a good thing).

This is part 4 of a series of blogs about IoT Hub communication using UWP:

  • Part 1: A very simple complex IoT Hub example
  • Part 2: Using the Visual Studio 2015 IoT Hub Extension and IoT Hub Device explorer
  • Part 3: Adding the power of BI to your IoT Hub
  • Part 4: Closing the Windows IoT Core feedback loop using Azure Functions
  • Part 5: PowerBI showing telemetry of past ten minutes, always!
  • Part 6: Add more flexibility to StreamAnalytics

Windows Azure Functions

Azure Functions introduces an event driven, compute-on-demand experience that builds on Azure’s market leading PaaS platform.”

An Azure function is just a function, written in C# or NodeJS. Like a normal function, it has some parameters passed. and it executes when it is called. To be more specific: you can pass a reference to blob storage for example, and the function will be called when a new blob is created.

We will use an Azure Function to react to telemetry passed and send a command back to the device, the telemetry came from.

But first we make take care of some administration.

Updating the telemetry

In my previous blog, we passed some very rudimentary telemetry. Because we want to close the feedback loop, we need to pass the name of the sender, the device, in the message. So add the device name in the telemetry class as a property:

public class Telemetry
{
    public string devicename { get; set; }
    public DateTime time { get; set; }
    public decimal temperature { get; set; }
}

And pass the name of the device in the call towards StreamAnalytics using the IoT Hub:

var t = new AzureIoTHub.Telemetry
{
    devicename = "DeviceOne",
    time = DateTime.Now,
    temperature = 20.5m + _random.Next(-20, 2),
};
 
try
{
    await AzureIoTHub.SendDeviceToCloudMessageAsync(t);
 
    await Dispatcher.RunAsync(
        CoreDispatcherPriority.Normal, () =>
        {
            tbReceived.Text = "Message sent";
        });
}
catch (Exception ex)
{
    await Dispatcher.RunAsync(
        CoreDispatcherPriority.Normal, () =>
        {
            tbReceived.Text = ex.Message;
        });
}

Selecting output for StreamAnalytics

So we want to pass the telemetry from the StreamAnalytics to the Azure Function to build. But here we have a little problem. StreamAnalytics can output to:

  • SQL Database
  • Blob Storage
  • Event Hub
  • Table Storage
  • Service Bus Queue
  • Service Bus Topic
  • DocumentDB
  • PowerBI

And we can create a new Azure Function which will trigger using a:

  • BlobTrigger
  • EventHubTrigger
  • Generic WebHook
  • GitHub WebHook
  • HttpTrigger

Do you see which options are available?

To make it a bit more interesting, we can add extra input parameters based on:

  • Azure Blob Storage
  • SaaS file
  • Azure Storage Table
  • Azure DocumentDB document
  • Azure Mobile Table Record

StreamAnalytics and Function both support blobs and DocumentDB. But these will not help us. Because we only want to pass the telemetry, we do not want to store it, and these are based on storage. This is a hot path, remember? So the blob storage will not be our best choice.

No, we need another trigger which leaves no traces and it must be compatible both with StreamAnalytics and Azure Functions. There is only one choice left, we need an EventHub to pass the data. And EventHub is perfect, it can handle high loads coming from IoTHub and StreamAnalytics.

Add an EventHub as Stream Analytics output

EventHub live in the older parts of Azure. If we create one, we will be redirected to the classic portal.

Update: Event Hubs are now part of the new/ current Azure portal. Although, the information below was ok the time I wrote it, please create an EventHub in the current portal, you will not be redirected to the old portal. Be aware that EventHubs are part of a Name Space. First create a namespace, then create an Event Hub inside the namespace. Remember the connection string of the ‘RootManageSharedAccessKey' access policy of the namespace and the Event Hub name in lower case. You will need it later on.

So create one, make sure it is created in the same region as our StreamAnalytics:

AF01

The EventHub is living with a service bus. So select the service bus and select the EventHub tab:

AF02

To access an EventHub, we need a Share Policy Access key. This key will tell if the owner has Manage and/or Listen and/or Send rights. We only need Listen and Send rights so create an Access Policy. I named it ‘sa‘ after StreamAnalytics:

AF02a

Make a copy of the primary key when saved.

Go back to the modern portal, go to the StreamAnalytics, stop it and add the new EventHub as output:

AF03

Yes, you will need to enter the policy name and key too.

Finally, we alter the query by adding this part:

SELECT
  devicename,
  case
    when CAST(temperature as float) > 25 then 1
    when CAST(temperature as float) < 15 then -1
    else 0
  end as action
INTO
  ehoutput
FROM
  hubinput

We simply add a new column value named ‘action’ and it is based on the value of the temperature. Of course, you can come with a more sophisticated decision strategy. But this will do for now.

Start the StreamAnalytics job and that’s that for StreamAnalytics.

Start your UWP app and send some telemetry. If you wait long enough, you will see some message occur in the EventHub dashboard:

AF04

Do not forget to execute one more step in the classic portal. Click the “Connection Information” option at the bottom of the Dashboard tab. And make a copy of the sa connection string:

AF07

Adding the Azure Function

But we are not interested in that fancy chart. We just want to pass data to an Azure Function. So add one, it is called “Function App” in the new portal:

AF05

Again, make sure it lives in the same region as the rest of the services.

Now we add a trigger template. Select the C# EventHubTrigger which will be shown, together with the other triggers, after we press the plus (+ New Function) sign on the left of the new function page:

AF06

Fill in the EventHub name and click on the ‘select’ right to the EventHub connection. You will be invited to add the connection name (the policy name) and the connection string:

AF08

Note: This seems to be a work in progress. The last part of the regular connection string must be removed. So remove something like “;EntityPath=blogtesteh” including the semicolon.

Update: Azure Functions are in preview. This particular behaviour has changed. Now, you are invited to enter the connection string of the ‘RootManageSharedAccessKey‘ of the namespace, the EventHub is living in. Do not use a policy of the EventHub itself. And you do not have to change the connection string any more…

Save this function. A default function is now available which provides some logging. Look at it in the Develop tab of the function:

using System;
  
public static void Run(string myEventHubMessage, TraceWriter log)
{
  log.Info(
   $"C# Event Hub trigger function processed a message: {myEventHubMessage}");
}

Check the Logs windows at the bottom. It says something like: “2016-06-14T21:40:37 Welcome, you are now connected to log-streaming service.” And it ’s possible it contains a lot of telemetry messages. If not, send some telemetry:

AF09

And there it is, the logging shows the EventHub messages like {“devicename”:”DeviceOne”,”action”:-1}.

Closing the feedback loop, enter the ServiceClient

Ok, we are almost there. We can send a telemetry message to StreamAnalytics and we receive an ‘action’ in the Azure Function including the device name.

Now we want to send a command back to that device. So add this code inside the function:

using System;
using Microsoft.Azure.Devices;
using System.Text;
using Newtonsoft.Json;

public static void Run(string myEventHubMessage, TraceWriter log)
{
  var connectionString = 
    "HostName=blogtest.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=XYZ=";

  var serviceClient = ServiceClient.
    CreateFromConnectionString(connectionString);

  var message = JsonConvert.
    DeserializeObject<Command>(myEventHubMessage);

  var commandMessage = new 
    Message(Encoding.ASCII.GetBytes(message.action.ToString()));

  serviceClient.SendAsync(message.devicename, commandMessage);

  log.Info($"C# Event Hub trigger function processed a message: {myEventHubMessage}");
}

public class Command
{
  public string devicename {get; set;}
  public int action {get; set;}
}

What’s happening here?

To send a message back to the IoTHub, we need the connection string of the hub. And we create a ServiceClient to talk to it. Then we deserialize the json message using the extra Command class. And now we can create and send the commandMessage to the right device.

This is the same code as seen in part 1 of this series of blogs.

Note: Azure Functions are ment to be very light weight and they are elastic (you can run multiple instances to scale out). Keep that in mind while adding more and more code into your Azure Function…

But wait, what happens if we save this? We get compilation errors. Yes,

Yes, the function is compiled but it is complaining about the unknown namespace name “Devices” and the unknown class “ServiceClient”. Why?

In part 1 we added some nuget packages 🙂 That’s what we have forgotten!

Adding Nuget packages to an Azure Function

Right next to the title “Code” the name of the current file is shown: “run.csx”. Click on the “View files” link lower in the screen to see all files involved in the function:

AF11

What we need to do is adding a new file called “project.json”, right next to “run.csx”. This new file must be filled with:

{
"frameworks": {
"net46": {
"dependencies": {
"Microsoft.AspNet.WebApi.Client": "5.2.3",
"Microsoft.AspNet.WebApi.Core": "5.2.3",
"Microsoft.Azure.Amqp": "1.1.5",
"Microsoft.Azure.Devices": "1.0.9",
"Newtonsoft.Json": "8.0.3"
}
}
}
}

Note: this list of packages is selected by looking at the original code running in a console app. Both the main packages and related packages are added. I omitted “System.Net.Htpp” because Azure Function already imports some namespaces.

There is a little issue adding this file. The current Develop tab makes it possible to create a new file or add an existing? Well, the latter one gave me an error:

AF12

but the insert new file seems to work fine:

AF13

And you can also use another option. Azure Functions supports Visual Studio Online aka Project Orleans. To use it:

  • Select Function app settings at the top
  • Open Advanced Settings / Go To App Service settings
  • Select the Tools button at the top of the new blade
  • Select Visual Studio Online (preview)
  • Flip the switch to On
  • Follow the ‘Go’ link

AF14

Note: There is one drawback, this does not work yet in Firefox, the security redirections looses track. So use another browser:

AF15

A nice benefit of this step, is having some IntelliSense inside the browser.

And after this last step, we now can compile the Azure Function.

Now switch to your UWP device and submit some telemetry. Just before or right after that, try to receive a command. And yes, it is shown:

AF16

In this example, we get a -1 command to our device. And yes, it’s the command sent to the right device!

So finally we have a very efficient way of controlling command using the power of Stream Analytics combined with the flexibility of Azure Functions.

Advertenties