A Step-by-Step Guide to Creating Functions Within Azure’s IoT Hub

By Brian Blanchard

In days past, implementing a closed-loop IoT solution required significant coordination, time and effort—a luxury most understaffed IT departments don’t have. And with IoT adoption growing at a staggering rate, it’s imperative to streamline every process possible.

That’s where Microsoft Azure’s IoT Hub comes in, particularly its ability to send data back to a smart connected device, allowing for more local control and reaction time in any environment. Azure functions now add serverless compute on top of Azure IoT, so we can extend those capabilities and accomplish more.

Creating your own Azure Functions that integrate with IoT Hub is actually easy, so let’s dive in and you’ll soon reap the benefits of a closed-loop IoT model:

Device-to-Cloud Communications

IoT Hub and IoT Suite allow for streamlined communications from device to cloud. Moving data up to the cloud via IoT Hub, Azure Stream Analytics, and various data management structures is well-documented and straightforward. To get started sending data up to the cloud, check out the Azure IoT Developer Center for more details.

The flow of data in this approach can be visualized as follows:

Azure IoT Data Flow
Figure 1: The flow of data in device-to-cloud communications

 

Benefits of a Closed-Loop Model

Closing the loop—or sending data back to the device—allows for control at the local environment. This is particularly valuable when that data makes an observation that requires local change.

For example, let’s say a piece of monitored factory equipment sends up data that identifies a piece of equipment is overheating. IoT Hub and IoT Suite gets this data to people that can act on it. But what if the device has overheated to the point that it risks explosion on the factory floor? In this case, we’d want the machine to shut off instantly; closing the loop achieves that goal as well.

Closed-Loop IoT Communications (Without Azure Functions)

Until now, a closed-loop IoT solution required many moving parts and created scale issues. This has been a barrier to adoption for many customers who have large numbers of devices. The solutions we’ve implemented thus far have consisted of a flow from Device -> IoT Hub -> ASA -> Event Hub -> Web Service -> IoT Hub -> Device.

Closed-Loop IoT
Figure 2: Closing the IoT communication loop without Azure Functions

The additional Event Hub and web service have added overhead and complexity, but in the end, accomplished the goal of closing the IoT communication loop. While this is still a viable approach for many scenarios, there is now a more streamlined approach.

A Streamlined Way (IoT and Azure Functions)

Azure Functions, a newer service in the Azure lineup, can communicate with IoT Hub directly. It can consume data from the IoT Hub, execute business logic and then send data back to the IoT Hub for routing back to the device. This model reduces complexity and streamlines Closed-Loop IoT communications.

Streamlined Approach to Closing the IoT Loop
Figure 3: A more streamlined approach to closing the IoT loop

Let’s talk through the step-by-step process of implementing such a solution:

Step One: Gathering IoT Hub Connection Data

The first step of implementing an IoT and Azure function solution is to gather connection data from your IoT Hub. In particular, we will need the Event Hub-compatible endpoint and an SAS Key.

Getting the IoT Event Hub-Compatible Endpoint

  1. In the Azure portal, navigate to the targeted IoT Hub
  2. Navigate to Settings > Messaging
  3. Copy the Event Hub-compatible name and Event Hub-compatible endpoint
    • The endpoint will match this format: sb://<endpoint>.servicebus.windows.net/
IoT Event Hub-Compatible Endpoint
Figure 4: Finding an Event Hub-compatible connection string

 

Getting the SAS Key

  1. Navigate to IoT Hub > Settings > Shared Access Policies (directly above Messaging in the prior step)
  2. Click on a Policy
  3. Copy the Policy Name (SAS Key Name) and Primary Key (SAS Key)
SAS Key
Figure 5: Obtaining the SAS Key

 

Create an Event Hub-Compatible Connection String

  • Endpoint=sb://<Event hub-compatible endpoint>;SharedAccessKeyName=<SAS Key name>;SharedAccessKey=<SAS Key>

Create an IoT Hub-Compatible Connection String

  • HostName=<IoTHub name>.azure-devices.net;SharedAccessKeyName=<SAS Key Name>;SharedAccessKey=<SAS Key>

NOTE: It is highly suggested that you create a dedicated SAS key for Function reading and sending to avoid EPOCH conflicts. 

With these connection strings, we are now ready to start creating our first Azure Function to communicate with the IoT Hub and its underlying Event Hub.

Step Two: Creating an Azure Function

Function development is done largely through the Kodu web-based development tool:

In Kodu, we will create a function that can both read IoT Hub data as a trigger and send data back to the same IoT Hub.

The easiest way to read data from IoT Hub in a function is using the EventHubTrigger template function.

EventHubTrigger in Kodu
Figure 6: Using the EventHubTrigger template function

The following process walks you through creating this function:

  • Click New Function
  • Select EventHubTrigger -C#
  • Name your function
  • Enter the Event Hub Name from the prior pre-requisite steps
  • Click the selected link under Event Hub connection
    • Enter the Connection Name (This doesn’t need to match the IoT hub connection data; it’s strictly a variable name)
    • Enter the Event Hub-Compatible Connection String created in the pre-requisite step
Creating a New Function
Figure 7: Creating a new function

 

Step Three: Adding Nugget Packages

Much like in a C# project, dependencies in an Azure Function are controlled through a file referred to as a Project.json file. The following is a sample of the Project.json file used in this sample to reference Microsoft.Azure.Devices and its dependencies to allow for communication with IoT Hub.

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

Once you create a Project.json file, you will need to deploy it to your Azure Function. In a browser, go to https://<functionname>.scm.azurewebsites.net/DebugConsole (Don’t forget to replace <functionname> with your function name.). Browse to site/wwwroot/<functionname>. Drag and drop your package.json file on the page to add it to the folder.

Deploying the Project.json File
Figure 8: Deploying the Project.json file

If your Project.json file was properly deployed, your Azure Function will begin loading the desired nugget packages. To test this deployment, open https://functions.azure.com/signin and scroll down to the Logs pane for your Function. You should see references to the nugget packages loading, as reflected in the image below.

Confirm Nugget Package loads
Figure 9: Confirm Nugget Package loads

 

Step Four: Setting up Application Variables in an Azure Function

You will need to set a few variables to control your function. App setting variables are a little hard to find in Azure functions, but once you find them, they can be extremely valuable in your Function.

To integrate an IoT Hub, you will need to register your IoT Hub’s connection string. If needed, you can also modify your Event Hub-compatible connections string here as well.

Open your function at https://functions.azure.com/signin and click on “Function App Settings”:

Setting Variables to Control Your Function
Figure 10: Setting variables to control your Function

Scroll down & click on “Go to App Service Settings” in the Application Settings pane:

Setting variables to control your Function Two
Figure 11: Setting variables to control your Function (cont.)

Scroll to the Application Settings section of Application Service Settings. (No, that isn’t a typo; just redundant.). From here you can add or edit Application Settings to be referenced in the code base:

Editing Application Settings
Figure 12: Setting variables to control your Function (cont.)

 

Step Five: Loading Message Data from the IoT Hub

In the last four steps, we created an EventHubTrigger function that consumes data from an IoT Hub via its underlying Event Hub. Upon creation, it creates the following code:

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

 

Assuming the prior steps have been followed properly, this sample code will write anything received by the IoT Hub into the Logs window of the Function.

Adding Using Statements

You’ll need a few required namespaces to make this sample work properly though.

Note: Newtonsoft.json and other .Net namespaces are already deployed. The .Net namespaces and namespaces added by nugget need only be added via the using statement. Other namespaces like Newtonsoft.json, will require a metadata reference denoted with the #r command.

Add the namespaces in place of the existing “using System;” statement:

#r "Newtonsoft.Json"

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

 

Adding the Contract

To ease the loading and management of objects, the Function should contain a model that represents the object(s) being transmitted via the IoT hub. The following model is the one used in this sample:

public class OurObject
{
  public string Time;
  public string IoTDeviceId;
   public string FieldToChange
}

 

Instantiating the Model

In this sample, we will use the JsonConvert class to DeserializeObject and load OurObject.

Note: You’ll need to add logic to filter messages that may be unrelated to the object we are attempting to capture. In this sample, we’ve simply wrapped a basic try-catch around the deserialization.

//Retrieve IoT Hub Messages
var CloudObject = new OurObject();
try{
      //(This line is what it’s all about. Take the Message that trigger this function and load it into our object by deserializing it.)
   CloudObject =
Newtonsoft.Json.JsonConvert.DeserializeObject<OurObject>(myEventHubMessage);
}
catch (Exception ex)
{
    log.Info($"Error deserializing: {ex.Message}");
     return;
}

From here, you are free to execute business logic based on the populated model.

Step Six: Pushing Data Back to the Device via IoT Hub

In the last step, we populated data from the IoT Hub. This allowed us to apply business logic to the object we’ve instantiated.

Now it’s time to push data back down to our device:

Preparing our Return Message

In this sample, we’ve chosen to push the Device ID into Azure with the Json payload. This is not necessarily advisable in a production scenario, but works for this demonstration.

Next, our device is trained to receive data that looks exactly like the data it sent up to the cloud. That said, our first step is to serialize our object into a string, but with the fields that we’ve updated thanks to our business logic:

//Mundane business logic
If (true) {
      OurObject.FieldToChange = “New value”;
}
String deviceID = OurObject.IoTDeviceId;
string messageString = JsonConvert.SerializeObject(OurObject);

 

Declaring the ServiceClient

The ServiceClient class of Microsoft.Azure.Devices is being used in this sample to send the message to IoT hub. At the class level, we’ll add a static declaration for ServiceClient’s interface:

static ServiceClient serviceClient;

 

Connecting to the IoT Hub

Next, we will retrieve our environmental variable containing the IoT Hub connection string. We will use that to connect to the IoT Hub via a Service Client.

        var connectionString = GetEnvironmentVariable("AZURE_IOTHUB");
serviceClient = ServiceClient.CreateFromConnectionString(connectionString);

 

Sending the Message

Finally, we send our message back to the device via the IoT Hub:

var commandMessage = new Message(Encoding.ASCII.GetBytes(messageString));
serviceClient.SendAsync(deviceID, commandMessage);

If your device is trained to receive messages via the IoT Hub, this message will be received on your device. If you have not done so already, the following tutorial will explain how you can connect your device for message receipt from the IoT Hub.

Final Thoughts

Easy as (Raspberry) Pi, right? <groan> In all seriousness though, closing the IoT loop using Azure’s IoT suite of products is a great step forward that lowers barriers to IoT adoption for lots more companies.

Feel free to send any questions or comments my way via Twitter and let’s start a conversation.