December 2018

Volume 33 Number 12

[Internet of Things]

Rapid IoT Development with Azure IoT Central

By Dawid Borycki

As an IoT developer, you face many challenges. Luckily, Microsoft IoT technologies have you covered. You can use Windows 10 IoT Core to develop Universal Windows Platform (UWP) apps for smart devices (bit.ly/2yJf6RJ). You can create machine learning (ML) algorithms graphically with Azure Machine Learning Studio (bit.ly/2yF2yes). And you can choose among various approaches to create Web apps using many Azure IoT services or self-developed ASP.NET MVC apps (either .NET Framework or .NET Core).

Though these technologies offer a comprehensive way to develop custom IoT solutions, combining them can be difficult, especially if you don’t have prior cloud or Web programming experience. To solve this problem, Microsoft created Azure IoT Suite (see my previous article at bit.ly/2yFaIU6), which was later renamed to Azure IoT solution accelerators (bit.ly/2pYaraX). Solution accelerators provide a number of preconfigured apps that target typical IoT problems, including dashboards, ML models, streaming data logic and programmatic components that tie everything together. However, solution accelerators are still complex. So, to simplify IoT development even further, Microsoft introduced Azure IoT Central, a cloud-based managed service you can use to quickly create an IoT back end. This is the modern portal, which contains dashboards and underlying services for telemetry, data processing and more. You use this back end to connect, monitor and manage your IoT devices.

In this article, I’ll show how to use Azure IoT Central to create the solution shown in Figure 1. This solution uses a custom IoT Central app that depicts telemetry data, device location and its settings, and two key performance indicators. An operator can use this dashboard to visualize the telemetry and remotely control the device through settings. The telemetry data is streamed from the .NET Core console application (shown in the top right corner). The full source code of this app is available at bit.ly/2D34XnV.

The IoT Solution I’ll Create
Figure 1 The IoT Solution I’ll Create

Azure IoT Services and Solutions

Before I explain how I created the IoT solution, I’ll briefly review other possible approaches to developing IoT applications with Azure IoT. First, you can create a fully custom solution by instantiating and manually configuring dedicated Azure IoT services, as shown in Figure 2. In this case, you typically start with the IoT Hub, which acts as the cloud gateway and is used for bi-directional communication and device management (for example, device registration). Data transmitted from the remote devices through the IoT Hub can then be preprocessed or transformed with Azure Stream Analytics. You use this service to filter out non-telemetry data or to average short portions of the telemetry data to reduce rapid fluctuations in your measurements. The preprocessed data can then be sent to the Power BI dashboard for visualization, persisted in dedicated cloud storage or transmitted to Event Hub for more complex analysis. Event Hub can send preprocessed data to an ML model to detect anomalies or predict trends in your monitored process. However, such an approach can be time-consuming, mostly because you don’t have a preconfigured Web portal to provide a convenient interface for your users and operators.

A Typical IoT Solution: Rectangles Denote Services and Arrows Show the Data Flow Between Solution Components
Figure 2 A Typical IoT Solution: Rectangles Denote Services and Arrows Show the Data Flow Between Solution Components

Second, you can start with IoT solution accelerators, which are templates you can adjust to your needs. IoT solution accelerators are based on the same Azure IoT services as in a fully custom solution. However, accelerators come with preconfigured services and sample code that show you how to take advantage of those services. In this case, you still have some work to do, but you start with adjustable templates.

Third, you can rapidly create a back end with Azure IoT Central, in which you’re given a fully prepared IoT application. The amount of work and necessary skills are minimized. You don’t need any cloud knowledge to create a fully functional, scalable and modern IoT solution. Everything is created for you automatically based on the same Azure IoT services as in a fully custom solution or when using solution accelerators. Hence, you can focus on developing your own things without taking care of other parts of your IoT solution. In what follows I’ll use Azure IoT Central to rapidly create the cloud endpoint.

Creating an IoT Central Application

To create the IoT Central app I used the IoT Central portal at apps.azureiotcentral.com, which displayed the Application Manager when I logged in with my Microsoft account. This manager shows all your apps, though initially, of course, there won’t be any apps. So, I clicked the New Application button, opening the IoT application creator, which lets you choose either a seven-day free trial or a paid subscription (bit.ly/2QLvk4t), as well as the application template, application name and the URL. I chose the Free plan and the Custom Application template, then set the app name to MSDN IoT Central app, which automatically created the following URL: msdn-iot-central-app.azureiotcentral.com. Finally, I clicked the Create button and, after several seconds, the app was ready.

The IoT Central application consists of two important elements: the actual view and the navigation (the left side bar). You use the side bar to switch among the various views: Home, Device Explorer, Device Sets, Analytics, Jobs, Application Builder and Administration. In this article I’ll mostly work with the Device Explorer and Application Builder views.

The newly created app shows the default form of the homepage. You can customize this view by clicking the blue Edit button to activate edit view, in which you can add several components such as links, labels, images, device settings, maps and so on. However, before creating a dashboard you need telemetry data. Initially, telemetry will be produced by the simulated device. To add such a device, you can start by clicking the Create Device Template panel on the default Home Page. Eventually you may prefer to use the Application Builder, which lets you choose the Custom Device option. The device template defines your device, including telemetry data, settings, events and remote commands (bit.ly/2CjsoYH).

Device Template

No matter how you choose to create the device template, you first specify the template name (here, I’m using MSDN-DeviceTemplate), then click the Create button and the device will be provisioned. The view shown in Figure 3 appears. Use this view to configure various aspects of your device:

  • Measurements: Lets you specify what kind of data your device will provide to the cloud. You can also define device states and events.
  • Settings: Lets you create device-specific settings that can be used to parameterize each device.
  • Properties: Lets you configure device properties, for example its physical location.
  • Commands: Lets you define commands, which can be sent from the cloud to the device in order to update its state.
  • Rules: Lets you define rules for your device. These rules can monitor your data and trigger corresponding actions.
  • Dashboard: Lets you create the device’s dashboard. You use such a dashboard to create the summary for your device, which can include telemetry plots, images, key performance indicators, a map indicating device location, state and event history.

A Device Template
Figure 3 A Device Template

Telemetry, Settings and Properties

Next, I defined the device template. I created two measurements: temperature and humidity, then I defined a setting—IsTelemetry­Active—that allows the operator to remotely enable or disable the telemetry (the Idle state in Figure 1). If the IsTelemetryActive setting is false, the remote device won’t stream any data. Last, the device will have one property, Location, which contains the geospatial address of the device.

To create telemetry measurements, you first click the Edit Template button. As Figure 4 shows, the New Measurement button will appear right above the Telemetry. After clicking this button you’re given an option to choose between Telemetry, State and Event. I’ll click Telemetry, which activates another view, which you use to configure temperature measurement in the following way (see middle column of Figure 4):

  • Display Name: Temperature
  • Field Name: Temperature
  • Units: degC
  • Minimum Value: -20
  • Maximum Places: 2
  • Color: choose whichever you like

Configuring Telemetry
Figure 4 Configuring Telemetry

Most of the telemetry settings are self-explanatory, but keep in mind that the Field Name is the name that’s used while transmitting your data between your device and the cloud. So you use Field Name to properly serialize and deserialize your objects, representing telemetry data.

Defining a Humidity measure is much the same as Temperature, but use the following configuration:

  • Display Name: Humidity
  • Field Name: Humidity
  • Units: %
  • Minimum Value: 0
  • Maximum Value: 100
  • Decimal Places: 2

After preparing measurements, you can add the device setting. To do so, click the Settings tab and ensure you’re in edit mode. You’ll then see the available setting types on the right. Choose the Toggle type and configure it as follows:

  • Display Name: Is telemetry active
  • Field Name: IsTelemetryActive
  • ON Display Text: True
  • OFF Display Text: False

Finally, you add the device property: Location. To do so, open the Properties tab. The list of available property types will appear on the left. This list is referred to as a library. Click the Location object. Then, configure the property as follows:

  • Display Name: Device Location
  • Field Name: Location
  • Initial Value: Microsoft, 1 Microsoft Way, Redmond, WA 98052

Note that the Initial Value field includes an auto-suggestion list, which is populated when you start typing.

After configuring the telemetry, settings and properties, I can now prepare the device dashboard, which will combine all the information in a modern graphical interface.

Creating a Dashboard

Creating the dashboard is similar to creating Settings and Properties—­you first enable the template edit mode, which will open a library containing the list of available UI components. The components are located on the left side of the Azure IoT Central dashboard. When you choose an item, it will appear in the dashboard. The left side of the template editor will then display configurable settings for the UI component, as shown in Figure 5. Go ahead and click Line Chart. Next, set the Title to Telemetry, enable all three toggles, set the time range to Past 30 minutes, and then click the rightmost icon next to Humidity and Temperature so both these measures will be plotted.

Configuring the Chart
Figure 5 Configuring the Chart

I then added a Map to the dashboard, showing the device location. To create such a map, you choose the Map UI component from the library, then configure its properties. Set the Title to Location and, from the Location Property list, choose Device Location.

Next, I created two tiles showing key performance indicators (KPIs). These KPIs are the aver­age temperature and maximum humidity (see the right part of Figure 1), calculated from sensor readings obtained in the past 30 minutes. To create such tiles, you use KPI components from the library, configuring them as follows:

Average Temperature KPI:

  • Title: Average Temperature
  • Time Range: Past 30 minutes
  • Measurement Type: Telemetry
  • Measure: Temperature

Maximum Humidity KPI:

  • Title: Max Humidity
  • Time Range: Past 30 minutes
  • Measurement Type: Telemetry
  • Measure: Humidity

Finally, I created a tile showing the actual value of the IsTelemetryActive setting. To do this, I used the Settings and Properties UI component, which has two configurable options: Title (a text box) and Settings and Properties (a two-panel control showing available and selected columns). Use the text box to set the title to Device Location, then use the second control to drag the Device Location from the available to the selected columns. Once all UI components are in place I can position them within the dashboard as previously shown in Figure 1.

Provisioning a Real Device

The preceding discussion confirms that with IoT Central you can quickly create a modern-looking dashboard for your IoT solution. However, so far we’ve relied only on the simulated device. Let’s see now how to connect the real device to the IoT Central app. You do this with the Device Explorer, proceeding as explained in IoT Central documentation (bit.ly/2Ch4gWA). Briefly, you first click the New button (located in the top panel), choose Real from the dropdown list, then provide the name and unique identifier of your device. Here, I set these two options to MSDN-DeviceTemplate – msdn-device-id1 and msdn-device-id-1, respectively (see Figure 6).

Device Explorer Showing the Simulated and Real Devices
Figure 6 Device Explorer Showing the Simulated and Real Devices

If you now click the real device in Explorer, you’ll see that its telemetry, settings, properties and dashboard are all the same as that of the simulated device. However, you don’t have any measurement yet because the device isn’t connected. Also, in the top right corner of the real device template you’ll see additional hyperlinks: Block and Connect. Block lets you block the device so the IoT Central app won’t accept any requests from the remote device. Connect displays the Scope ID and credentials necessary to connect the device  to the cloud, as shown in Figure 7.

Device Connection
Figure 7 Device Connection

IoT Central supports two ways to authorize your device. You can use either Shared Access Signature (SAS) or X.509 Certificates. Both approaches are described in detail at bit.ly/2ClDv3z. In the client app I’m going to develop, I’ll use the SAS approach. So, to proceed further I’ll need to note the Scope ID, Device ID and either the primary or secondary key displayed on the device connection screen in Figure 7. I’ll use the dps_cstr command-line tool to generate the connection string. (You can download the Windows version of this tool from bit.ly/2Cj3Ejv.) Then, to obtain the actual connection string, you open the command line and type the following command:

dps_cstr <scope_id> <device_id> <SAS Key>

For the parameters shown in Figure 7, the dps_cstr tool generated the following connection string:

HostName=saas-iothub-28681fd2-94c7-4938-bf7e-7ae3e94a407c.azure-devices.net;DeviceId=msdn-device-id1;SharedAccessKey=nQqFzf6TvnQA+zFI4MVaSSBeZgsYSY0P7KXrl6z6oDE=

Implementing the Client App

To create the client app, I developed a C# .NET Core app based on the Console Application template. Most of the code could be used without any changes in a UWP Windows 10 IoT Core app, as well. However, I decided to use .NET Core in order to minimize the workloads required to implement the client app for IoT Central.

I used Visual Studio 2017 Community Edition, starting with the New Project dialog. I chose the Console App project template (.NET Core 2.1), then set the app name to IoTCentralClient, and installed the Microsoft.Azure.Devices.Client NuGet package to quickly connect to the IoT Hub.

I then proceeded to the actual implementation, starting with the DeviceClientHelper class shown in Figure 8.

Figure 8 The DeviceClientHelper Class

public static class DeviceClientHelper
{
  private static readonly string connectionString
    = "<your_connection_string>";
  private static DeviceClient deviceClient;
  public static DeviceClient Init()
  {
    if (deviceClient == null)
    {
      deviceClient = DeviceClient.
        CreateFromConnectionString(connectionString);
    }
    return deviceClient;
  }
}

The DeviceClientHelper class uses the Microsoft.Azure.Devices.Client.DeviceClient to associate the connection with the IoT Hub. To that end, DeviceClient uses the connection string generated by the dps_cstr tool. The connection string is passed as an argument to the CreateFromConnectionString static method of the DeviceClient class.

Telemetry

After preparing the connection, I created the Data class, shown in Figure 9, which is an abstract representation of the telemetry that will be sent to the cloud.

Figure 9 The Data Class

public class Data
{
  public double Temperature { get; set; }
  public double Humidity { get; set; }
  public Message ToMessage()
  {
    var dataJson = JsonConvert.SerializeObject(this);
    return new Message(Encoding.ASCII.GetBytes(dataJson));
  }
  public override string ToString()
  {
    return $"Temperature: {Temperature,6:F2}, Humidity: {Humidity,6:F2}";
  }
}

Remember that public properties of the Data class are used in the cloud endpoint to properly deserialize objects sent from your device. Hence, property names have to correspond to the Field Names used in the device template. Otherwise, your telemetry data will not be properly parsed by the cloud.

Next, to deserialize the telemetry object I wrote the ToMessage method shown in Figure 9. In the first step, ToMessage gets the JSON-­formatted string, representing the Data object. This is done with JsonConvert.SerializeObject. In the second step, the JSON string is converted to a byte array with Encoding.ASCII.GetBytes. The result of this operation is used to instantiate the Microsoft.Azure.Devices.Client.Message class. Instances of this class can then be sent to the IoT Hub using SendEventAsync method of the DeviceClient class.

Next, I wrote the Generator class to generate and send telemetry data. This class emulates a real sensor reading by pseudo-randomly generating temperature and humidity values (see Data/Generator.cs in the companion source code). To this end, Generator uses the System.Random class to synthesize sensor reading from the given range:

private Random randomNumberGenerator = new Random();
private double GetRandomValue(MeasurementRange measurementRange)
{
  var randomValueRescaled = randomNumberGenerator.NextDouble()
    * measurementRange.ValueRange();
  return measurementRange.Min + randomValueRescaled;
}

The measurement range is represented by instances of the MeasurementRange class:

public class MeasurementRange
{
  public double Min { get; set; }
  public double Max { get; set; }
  public double ValueRange()
  {
    return Max - Min;
  }
}

The Generator class has two fields of the preceding type (MeasurementRange). They correspond to the temperature and humidity measures:

private readonly MeasurementRange temperatureRange
  = new MeasurementRange() { Min = -20, Max = 60 };
private readonly MeasurementRange humidityRange
  = new MeasurementRange() { Min = 0, Max = 100 };

Note that these ranges are the same as specified previously in the device template.

The Generator class uses an instance of DeviceClient and CancellationToken. The first is used to send the telemetry, while the second will break the infinite telemetry loop. Actual instances of the DeviceClient and CancellationToken are passed through the Generator class constructor:

private DeviceClient deviceClient;
private CancellationToken cancellationToken;
public Generator(DeviceClient deviceClient,
  CancellationToken cancellationToken)
{
  Check.IsNull(deviceClient);
  Check.IsNull(cancellationToken);
  this.deviceClient = deviceClient;
  this.cancellationToken = cancellationToken;
}

The Check class is a static helper class used to verify whether arguments are null or not (see in the companion code Helpers/Check.cs).

The telemetry data is generated and sent to the cloud within the while loop implemented within the instance method Telemetry­Action of the Generator class, shown in Figure 10.

Figure 10 The TelemetryAction Method

public bool IsTelemetryActive { get; set; } = true;
private void TelemetryAction()
{
  while (!cancellationToken.IsCancellationRequested)
  {
    var telemetryData = new Data()
    {
      Temperature = GetRandomValue(temperatureRange),
      Humidity = GetRandomValue(humidityRange)
    };
    if (IsTelemetryActive)
    {
      deviceClient.SendEventAsync(telemetryData.ToMessage());
      Console.WriteLine($"Sending telemetry: {telemetryData}");
    }
    else
    {
      Console.WriteLine("Idle");
    }
      Task.Delay(delayTime).Wait();
  }
}

Note that the telemetry is sent to the cloud only when the IsTelemetryActive property is true. This property can be changed on the cloud endpoint using the Settings tab in the IoT Central app (Figure 4).

The TelemetryAction is executed in the background using the Task-based asynchronous pattern:

public Task Start()
{
  telemetryTask = new Task(TelemetryAction);
  telemetryTask.Start();
  return telemetryTask;
}

Putting Things Together

With the DeviceClientHelper and the Generator classes ready, I combined them in the Program class (see Program.cs in the companion code). I started by implementing the static Program.Main method as shown in Figure 11.

Figure 11 The Program.Main Method

private static CancellationTokenSource cancellationTokenSource
   = new CancellationTokenSource();
static void Main(string[] args)
{
  // Configure cancel key press handler (to stop the app)
  Console.CancelKeyPress += new ConsoleCancelEventHandler(
    CancelKeyPressHandler);
  // Connect to the cloud
  var deviceClient = DeviceClientHelper.Init();
  // Telemetry generator produces random temperature
  // and humidity, and then sends them both to the cloud
  var telemetryGenerator = new Generator(
    deviceClient, cancellationTokenSource.Token);
  // Associate handler to update device properties according to cloud requests
  deviceClient.SetDesiredPropertyUpdateCallbackAsync(
    PropertyUpdateCallback, telemetryGenerator).Wait();
  // Start telemetry
  telemetryGenerator.Start().Wait();
}

Program.Main first wires an event handler to the Console.Cancel­KeyPress event in order to stop the telemetry loop (using the cancellation token) and close the application:

private static void CancelKeyPressHandler(object sender,
  ConsoleCancelEventArgs e)
{
  if (e.SpecialKey == ConsoleSpecialKey.ControlC)
  {
    cancellationTokenSource.Cancel();
    Environment.Exit(0);
  }
}

Next, the Main method connects to the IoT Hub using static Init method of the DeviceClientHelper. Init returns an instance of the DeviceClient class, which is then passed to the Generator class constructor.

The method SetDesiredPropertyUpdateCallbackAsync of the DeviceClient class instance is now used to set up the callback that’s invoked when the operator changes device settings at the cloud endpoint. This callback, PropertyUpdateCallback (see Figure 12) is provided with an instance of the Microsoft.Azure.De­vices.Shared.TwinCollection class. This object represents the collection of device settings. In particular, the class indexer can be used to read values of the selected settings. You identify particular settings using their Field Names, configured at the cloud endpoint. In Figure 12, I show how to read the value of the IsTelemetry­Active setting and use it to update the corresponding property of the Generator class instance.

Figure 12 PropertyUpdateCallback

private static readonly string telemetryActivePropertyName =
  "IsTelemetryActive";
private static readonly string propertyValue = "value";
private static Task PropertyUpdateCallback(
  TwinCollection desiredProperties, object userContext)
{
  if (desiredProperties.Contains(telemetryActivePropertyName))
  {
    var telemetryGenerator = userContext as Generator;
    telemetryGenerator.IsTelemetryActive =
      desiredProperties[telemetryActivePropertyName][propertyValue];
  }
  return Task.CompletedTask;
}

Finally, the Main method starts the telemetry by invoking the Start method of the Generator class instance.

To test the client app you need to execute it. If the connection string is valid, the app will connect to the IoT hub and start streaming telemetry data. Each synthesized measurement will be printed in the console and you’ll then see these measures in the cloud dashboard (refer back to Figure 1). You can also remotely change IsTelemetryActive to temporarily disable telemetry. To do so, open the Settings tab of your Azure IoT Central app, switch the toggle, and click Update button. In that case, the client app prints the Idle string instead of the actual telemetry data.

Wrapping up

In this article I showed how to rapidly create a custom, fully functional, modern-looking Web application for an IoT solution using Azure IoT Central. I also demonstrated how to create a device template and use it to present telemetry data, device location and KPIs. I then developed a C# client app and connected it to stream telemetry data to the cloud. Finally, I showed how to respond to device setting changes requested through the IoT Central application. You can use all this to quickly develop modern Web dashboards for your IoT solutions without any prior cloud or Web programming knowledge.


Dawid Borycki is a software engineer and biomedical researcher, author and conference speaker. He enjoys learning new technologies for software experimenting and prototyping. Borycki is an author of two Microsoft Press books: “Programming for Mixed Reality” (2018) and “Programming for the Internet of Things” (2017).

Thanks to the following Microsoft technical expert for reviewing this article Bruno Sonnino
Bruno Sonnino is a Windows Development Microsoft MVP. He has been a developer, consultant , author and trainer for more than 20 years and is passionate about software development.


Discuss this article in the MSDN Magazine forum