Introduction

New Azure Sphere developers are presented with high level example applications that can be used as starting points or references for new projects and products.  Understanding how the examples are architected is a key first step to efficient re-use of these examples.  This blog will review one of the Microsoft Azure Sphere example applications called AzureIoT.  If you understand this application and its architecture, then you will be able to understand any of the Microsoft or Avnet high level Azure Sphere example applications.

 

Objectives

  • Become familiar with the AzureIoT example so it can quickly be extended and customized for new Azure Sphere applications
  • Learn about the Microsoft AzureIoT example high level application architecture
  • Learn about sending telemetry data an Azure IoT Hub
  • Learn about managing Azure IoTHub device twins
  • Learn about Azure IoTHub direct methods
  • Learn how to interface with hardware devices
  • Learn about the Azure Sphere Hardware abstraction layer

Example Source Code

This document examines one of the Microsoft Azure Sphere examples called AzureIoT.  You can pull this project to follow along with this blog to dig deeper into the implementation details.

 

High Level Application Architecture

Since Azure Sphere is a connected secure IoT solution.  I recommend leveraging Microsoft’s AzureIoT Azure Sphere example for new high level applications. This example connects to an Azure IoTHub and implements all the connection, Device to Cloud (D2C) and Cloud to Device (C2D) logic that any connected application requires.

 

To understand this application let’s examine the four ways the application execution path is driven.

  1. main() loop
  2. Event Loop Timers
  3. Event Loop Events
  4. Azure IoT Library Callbacks

 

The main() loop

On start up the operating system passes execution control to the main() function.  The main function in the AzureIoT example application can be simplified into three sections.

 

  1. Initialization: InitPeripheralsAndHandlers()
    1. Allocates system resources
    2. Configures hardware interfaces
    3. Configures timers and timer handlers
  2. Main loop
    1. Checks to see if any other part of the application has set the global variable exitCode to flag an exit condition.
      1. If application code in the rest of the high level application encounters an error condition it will set the global variable exitCode to reflect any issue encountered.
    2. EventLoop_Run() “Runs an EventLoop and dispatches pending events in the caller's thread of execution.”
  3. Clean up: ClosePeriheralsAndHandlers()
    1. Closes hardware interfaces
    2. Releases system resources

 

Upon application exit, the value of exitCode is returned to the OS so that the exit reason is visible for application health tracking and troubleshooting.  Exit codes are defined in main.c in the ExitCode enumeration definition.

 

 

Event Loop Timers

The AzureIoT example uses event loop timers to drive periodic logic.  Timers can be periodic (fire over and over again) or one shot (only fire once). Each timer has a user defined handler function that will execute when the timer fires.  When a timer expires, its handler will be called the next time EventLoop_Run() is called from the main() loop.

 

The high level application is single threaded. When a timer expires, the implementation does not guarantee that the handler will execute immediately.  Event timers are more memory efficient than using threads, however using timers in this way is a form of cooperative tasking and depend on the application quickly and efficiently processing the events so that they don't back up and cause execution errors.

 

The timers utilized in the AzureIoT Example are . . .

buttonPollTimer

  • Used to poll and de-bounce button presses
  • Fires every 1 millisecond

azureTimer

  • Used to establish, monitor and maintain the Azure IoTHub connection
  • Used to send simulated telemetry data periodically
  • Uses dynamic periods defined by the global variable azureIoTPollPeriodSeconds

 

Event Loop Timer functions

The Azure Sphere system supports the following functions to create, modify, use and destroy Event Loop Timers.  See eventloop_timer_utilities.h for details on each function.

  • CreateEventLoopPeriodicTimer()
  • CreateEventLoopDisarmedTimer()
  • SetEventLoopTimerPeriod()
  • SetEventLoopTimerOneShot()
  • ConsumeEventLoopTimerEvent()
  • DisarmEventLoopTimer()
  • DisposeEventLoopTimer()

 

Add a timer and handler

To add a new timer and handler to the Azure IoT example

  • Define a new global EventLoopTimer* variable in main.c
  • Add code in InitPeripheralsAndHandlers()to define the timer period and specify the handler to call when  the timer expires
  • Add the handler routine that will be called each time the timer expires

 

Search the AzureIoT Example application for buttonPollTimer to see a working example of defining and using an Event Loop Timer.

  

I/O Events

In addition to timer events, Azure Sphere supports events based on input/output (I/O).  These events are tied to file descriptors.  The Azure IoT example does not implement any I/O events, so let’s examine using I/O events in other example applications.

 

When an application uses a UART, it can register an Event Loop I/O event so that when the UART receives data, an event fires and a user defined handler will be called that executes the logic to read and process the UART received data. See the UART_HighLevelApp example to see how this is configured and used.

Another example is when two Azure Sphere applications share an inter-core communication connection.  The Event Loop I/O event can be configured so that when the real time application inserts a message into the queue, the high level application is notified and a handler is called to process the incoming data.  See the intercoreComms High Level example application to see how this is configured and used.

Azure Sphere Interrupt Processing

The Azure Sphere MT3620 does support interrupts, but only in real time applications running on one of the real time cores. If your application requires responding to events based on interrupts you need to implement that functionality in a real time application or determine if the functionality can be managed by the high level application polling the hardware interface using Event Loop Timer Events.

 

Azure IoT Library Callbacks

The last mechanism to discuss are Azure IoT library callbacks.  The Azure Sphere IoT Library leverages much of the same functionality of the Azure IoT Hub Device SDKs.  These SDKs provide support for connecting to an Azure IoT Hub and all the hooks and functionality required to send and process D2C and C2D messages.

 

The example application uses callback functions to manage Azure IoT Hub related events.  The theme here is to define callback routines for each event and implement the callback handlers to manage the event, whatever it is.  The AzureIoT example supports the following Azure IoT Library events . . .

 

High Level Application Diagram

If we put together a diagram that shows all the elements discussed above it would look like . . .

 

 

Send IoT Telemetry to Azure

Sending telemetry is a basic function for any IoT project.  Typically, IoT devices collect data, they may or may not pre-process the data, and then they send data in the form of telemetry to the cloud.  The AzureIoT example project sends simulated data to Azure.  Sending telemetry data to Azure is easy.  There are 3 things that need to happen . . .

 

  1. Establish and maintain a secure connection to an Azure IoT Hub or IoT Central application
  2. Construct a JSON object that contains valid JSON that includes your data
  3. Call the routine to send the data to our IoT Hub or IoT Central application

 

Establish a connection to Azure

The recommended method to provision IoT devices to Azure IoT Hubs is to use an Azure service called a Device Provisioning Service (DPS).  You can read all about DPS here.  Using a DPS you can deploy a single software application build onto millions of devices.  The first time each device connects to the internet, and then the global DPS server, they will all automatically be provisioned to one or more IoT Hubs and then connect to the IoT Hub that each device was provisioned to.  This is a powerful IoT concept and is required to deploy IoT devices at scale.  The diagram below shows the process . . .

 

The AzureIoT example supports multiple ways to connect your device to an IoT Hub or IoT Central.  The readme.md file in the gitHub repo contains all the details to connect the application to an IoT Hub.

 

Construct a JSON object, and send the telemetry

Below you’ll see the AzureIoT example application code to create and send a JSON telemetry message.  There are basically five things to do . . .

 

  1. Allocate memory to store the JSON object (line 993)
  2. Construct the JSON object in the new memory buffer (line 941)
  3. Create a IOTHUB_MESSAGE_HANDLE (line 872)
  4. Send the message to the IoTHub (line 878)
  5. Release the IOTHUB_MESSAGE_HANDLE resource (line 884)

One thing that I think is really cool about telemetry is that you can send any {“key”: value} pair, or any valid JSON object, you want to your Azure IoT Hub. You don’t have to tell Azure anything about your data.  As long as the data is valid JSON the IoT Hub will accept the data and store it for you to use.  Of course if you want to access that data, some other Azure service will need to know about your data so it can ingest it and do something meaningful with it, but the IoT Hub does not care as long as it’s valid JSON.

 

Process Device Twin Messages from Azure IoT Hub

Device twins are another powerful IoT concept. You can read the Azure documentation on device twins here.

 

Device twins are JSON documents that store device state information including metadata, configurations, and conditions. Azure IoT Hub maintains a device twin for each device that you connect to IoT Hub.”

 

Using device twins you can make changes in the cloud to a device twin’s desired property object and the IoT device will receive a message containing the new desired property.  Your application then uses the information in the desired property to do something in your application.  For example, toggle a GPIO signal to control an LED, change a variable/property that defines how your application behaves, or anything that your creative mind can think up.

 

Device twins can be read only as well.  For example, the AzureIoT example sends up two device twins when the application connects to the Azure IoT Hub:  {"manufacturer":"Microsoft","model":"Azure Sphere Sample Device"}.  These device twins capture the manufacturer and model of the IoT Device so it can be viewed or used to drive a custom work flow from the Azure cloud.

 

To work with Device Twins we need four different things . . .

  1. Define the JSON {“key”: value} pair that we want to implement for our solution
  2. Setup a callback routine that will be instantiated when the Azure IoT Hub sends a Device Twin update
  3. Implement the callback routine, then add code to look for and do something with our specific {“key”: value} pair data
  4. Send a Device Twin reported properties message back to Azure with the new reported value of our property.

 

Define the JSON {“key”: value}

The first thing we need to do is define our {"key": value} pair.  The the AzureIoT example they implement a Device Twin called "statusLED," the JSON is shown below, it's a Boolean entry.

 

{"statusLED": (true|false)}

 

Setup a Device Twin callback routine

Before we can receive a Device Twin update, we need to tell the Azure IoT Library how to inform the application when a new Device Twin message is received.  In main.c around line #598 the application informs the Azure IoT Library that the routine DeviceTwinCallback should be used to process incoming Device Twin messages.

 

 

Implement the callback routine

The callback implementation is shown below. There are five sections identified in the graphic.

 

  1. Allocate memory to construct a null terminated copy of the incoming payload
    1. The parson JSON parsing library requires the JSON to be null terminated
  2. Pull the desired properties out of the incoming payload
  3. Look for and read the value of the {“statusLED” true|false} device twin, call the routine to set the GPIO value based on the statusLED value
  4. Send the Azure IoT Hub a reported property that reflects the new value
  5. Release the JSON resources allocated by the call to json_parse_string(nullTerminatedJsonString)

 

 

Process Azure IoTHub Direct Methods

Another powerful IoT Hub feature is Direct Methods. You can read the Azure documentation on Direct Methods here.  You can also read a very detailed blog all about Azure Sphere and Direct Methods here.

 

Direct methods represent a request-reply interaction with a device similar to an HTTP call in that they succeed or fail immediately (after a user-specified timeout). This approach is useful for scenarios where the course of immediate action is different depending on whether the device was able to respond.”

 

Let’s see how to implement a Direct Method on Azure Sphere and review the AzureIoT example application direct method implementation.  There are three things we need to accomplish to implement and use a direct method.

 

  1. Define the direct method
  2. Setup a callback routine that will be instantiated when the Azure IoT Hub sends a direct method call to our device
  3. Implement the callback function to catch, validate and respond to the direct method.  Direct methods return a result code and a return JSON string.

 

Define the direct method

The AzureIoT example implements a direct method called “TriggerAlarm” and it returns a JSON response string {“Alarm Triggered”}.  This direct method does not require any arguments or payload with additional information for the direct method.

 

To call this direct method you would enter the direct method name “TriggerAlarm” with an empty JSON payload {}

 

The Direct Method call returns

 

Setup a Direct Method callback routine

Similar to processing Device Twin messages from the IoTHub, we tell the Azure IoT Library that we want to be notified when a direct method is called on our device and provide a callback handler.

 

 

Implement the callback routine

The callback implementation is shown below. There are three sections identified in the graphic.

 

  1. Check to see if the Direct Method called is the one we expect “TriggerAlarm”
  2. Construct a response string and a response code
  3. Allocate memory on the heap and copy the response string to the allocated memory.  Note that the Azure IoT Library is responsible for freeing this memory. Return the response code 200 to tell the Azure IoT Library that we successfully processed the Direct Method.

 

Interfacing with hardware devices

Working with hardware interfaces is common with IoT projects.  Whether it’s driving an LED, or reading an I2C sensor the Azure Sphere OS and Azure Sphere SDK APIs help you to interface with your hardware devices.

 

GPIO

In this section we’ll identify the code required to read the General Purpose I/O (GPIO) signal in the AzureIoT example project that’s connected to User Button A.  Working with GPIO interfaces is straight forward, we need to do 5 things . . .

 

  1. Include the GPIO libraries in the project
  2. Add the GPIO reference to the app_manifest.json file
  3. Declare a file descriptor that we’ll associate to our GPIO signal
  4. Open the GPIO pin as an input and assign a file descriptor to work with the hardware
  5. Read the GPIO level using the file descriptor as a reference to the hardware
  6. Close the file descriptor

 

Using the AzureIoT example let’s identify the source code for each of these items . . .

 

Include the GPIO libraries in the project

To add the GPIO libraries to our project just include gpio.h header file

 

Add the GPIO reference to the app_manifest.json file

We need to explicitly grant permission for the application to use the GPIO signal for Sample Button 1.  We add "$SAMPLE_BUTTON_1" to the app_manifest.json file in the Capabilities --> Gpio section. At build time the Hardware Abstraction layer implementation will map "$SAMPLE_BUTTON_1" to the number 12 or GPIO12.  If we were to omit this step, when the application attempted to open GPIO 12 the OS services would block the operation and return an error.

 

The Microsoft documentation on the app_manifest.json file can be reviewed here.

 

 

Declare a file descriptor

The system uses file descriptors to reference and operate on hardware interfaces.  We declare a file descriptor sendMessageButtonGpioFd to allow the code to operate on the GPIO interface.  You’ll notice that all calls to initialize and read the GPIO pin reference this file descriptor.

 

 

Open the GPIO as an input

This line of code opens GPIO 12 (SAMPLE_BUTTON_1) as an input. That will allow us to read the GPIO level.

 

Read the GPIO level

This is the code that actually reads the GPIO hardware.

 

 

Close the file descriptor

When the application exits, it calls the ClosePeripheralsAndHandlers() function to clean up.  We call the routine that will close the file descriptor.

 

The easiest way I’ve found to look at an existing high level Azure Sphere project to understand how a hardware interface is implemented is to search for the file descriptor.  When you find all the code that uses some piece of hardware’s file descriptor, you’ll see all the necessary code for that interface.

 

You can review the AzureIoT example project to see how to drive a GPIO signal, search for deviceTwinStausLedGpioFd.

 

app_manifest.json

The Microsoft documentation on this Azure Sphere feature is very well written, and I don’t think I can add anything to this very important discussion.  The documentation is here, it’s a short document that should be reviewed by every Azure Sphere developer.  The text below is taken from the Microsoft documentation.

 

“The application manifest describes the resources, also called application capabilities that an application requires. Every application has an application manifest.  Applications must opt in to use capabilities by listing each required resource in the Capabilities section of the application manifest; no capabilities are enabled by default. If an application requests a capability that is not listed, the request fails. If the application manifest file contains errors, attempts to sideload the application fail. Each application's manifest must be stored as app_manifest.json in the root directory of the application folder on your PC.”

 

Hardware Abstraction Layer

Microsoft has implemented a hardware abstraction layer so that the Microsoft Azure Sphere GitHub samples can run on multiple Azure Sphere hardware platforms.  All the Microsoft Azure Sphere GitHub examples use this abstraction layer. This is a great feature because there are multiple Azure Sphere platforms and each of these platforms may expose hardware features of the underlying Azure Sphere device in different ways.  You can read about the Hardware definitions and how to create custom definitions here.

 

For example the Seeed development kits and the Avnet Starter Kit all use pushbuttons and user LEDs.  These devices use GPIO signals.  Microsoft needed an implementation so that a single sample application could correctly reference the GPIO signals for the hardware platform that the application was built for, even if different hardware platforms used different signals for a common function like driving a LED.

 

To implement the abstraction layer, they have included a “HardwareDefinitions” folder in the azure-sphere-samples directory structure.  Under the “HardwareDefinitions” folder, there are folders for each of the currently supported platforms.

 

 

Each folder contains a JSON file and a header file that maps the board-specific, or module-specific features to the underlying Azure Sphere MCU hardware signals.  The example projects use identifiers when referring to hardware signals which are mapped to the specific peripherals on the target hardware platform.

 

Here’s a code snippet where the AzureIoT example opens the GPIO for the button.  Note that we don’t pass in the GPIO integer, but an identifier that references the actual GPIO signal, an integer.  In this way different hardware platforms can use different GPIO signals for the buttons, but the sample application will work for all hardware platforms as long as the hardware definitions are correctly defined and the correct hardware platform is defined for the build.

 

 

Modify a project to reference a different hardware solution

To specify the target hardware in an Azure Sphere project open the CMakeLists.txt file and update the azsphere_target_hardware_definitions() statement to specify to the correct HardwareDefinitions directory for your kit or solution.

 

Or

 

When you modify and save this file, the CMAKE subsystem will automatically re-generate the build configuration.  If there is a problem with the path or filename that you added, you’ll see errors.  A successful process will look like the graphic below.

 

 

Note that the default platform for all the Microsoft GitHub examples is the Seeed development kit.  So if you’re using the Avnet Starter Kit, you’ll need to update this property each time you open a sample project from GitHub for the first time.

 

Wrap Up

Hopefully you have learned a lot about Azure Sphere High Level applications and specifically all the important details for Microsoft’s Azure Sphere AzureIoT example application.  Keep this blog handy when you’re starting your first few Azure Sphere applications as a reference.

 

Additional Azure Sphere Resources