azure-iot-hub-nanoframework

Azure IoT Hub with nanoFramework: Sending Telemetry

  • 4 min

Azure IoT Hub is a messaging service designed to connect IoT devices to the cloud securely and at scale.

In previous articles we’ve already covered Wi-Fi, HTTP, JSON, and MQTT. Now we’re going to put all these pieces together so our ESP32 with nanoFramework can send real telemetry to Azure, without having to build the entire MQTT protocol manually.

IoT Hub is not “just another REST API”. It’s designed for devices: per-device authentication, cloud-to-device messages, device twins, direct methods, and provisioning.

What we need

For this example we need to have ready:

  • An Azure IoT Hub created in Azure.
  • A registered device within that IoT Hub.
  • The IoT Hub hostname, for example my-hub.azure-devices.net.
  • The device Device ID.
  • The device Primary Key or Secondary Key, not the full connection string.

In nanoFramework we’ll use the NuGet package nanoFramework.Azure.Devices.Client, which exposes the DeviceClient class.

Don’t confuse the device key with the service shared access key. For an ESP32 connecting as a device, use that specific device’s key.

Connecting with DeviceClient

The DeviceClient class encapsulates the MQTT connection against Azure IoT Hub. We provide it with the hostname, the device identifier, and the SAS key, and it takes care of building the connection correctly.

using System.Diagnostics;
using nanoFramework.Azure.Devices.Client;

public class Program
{
    private const string IotHub = "my-hub.azure-devices.net";
    private const string DeviceId = "esp32-livingroom";
    private const string SasKey = "DEVICE_KEY";

    public static void Main()
    {
        DeviceClient client = new DeviceClient(IotHub, DeviceId, SasKey);

        if (client.Open())
        {
            Debug.WriteLine("Connected to Azure IoT Hub");
        }
        else
        {
            Debug.WriteLine("Could not connect to Azure IoT Hub");
        }
    }
}
Copied!

This assumes we already have Wi-Fi and correct time. If the device clock is wrong, TLS and SAS tokens will fail, because certificates and signatures depend on time.

Before opening DeviceClient, connect Wi-Fi using WifiNetworkHelper.ConnectDhcp(..., requiresDateTime: true, ...). This ensures you have a valid IP and date.

Sending telemetry

Telemetry is essentially a message the device sends to the cloud. It’s common to send it as JSON because it’s easy to read, process, and store.

using System.Diagnostics;
using System.Threading;
using nanoFramework.Azure.Devices.Client;

public class Program
{
    private const string IotHub = "my-hub.azure-devices.net";
    private const string DeviceId = "esp32-livingroom";
    private const string SasKey = "DEVICE_KEY";

    public static void Main()
    {
        DeviceClient client = new DeviceClient(IotHub, DeviceId, SasKey);

        if (!client.Open())
        {
            Debug.WriteLine("Could not connect to Azure IoT Hub");
            Thread.Sleep(Timeout.Infinite);
        }

        while (true)
        {
            string payload = "{ \"temperature\": 23.7, \"humidity\": 48 }";

            bool sent = client.SendMessage(payload, "application/json");

            Debug.WriteLine(sent
                ? "Telemetry sent"
                : "Error sending telemetry");

            Thread.Sleep(30000);
        }
    }
}
Copied!

For a quick test, writing JSON manually is fine. In a real project, it’s normal to build it from an object or a small function that centralizes the format.

Certificates and TLS

Azure IoT Hub uses secure connections. This means the device must trust the corresponding root certificate.

In nanoFramework we can pass the root certificate via the azureCert parameter, or have it already loaded on the device if we’ve prepared the firmware and resources for it.

byte[] azureRootCertificate = Resources.GetBytes(Resources.BinaryResources.AzureRoot);

DeviceClient client = new DeviceClient(
    IotHub,
    DeviceId,
    SasKey,
    azureCert: azureRootCertificate);
Copied!

If the connection fails without a very friendly message, first check three things: correct time, root certificate, and device key. In IoT, 80% of “mysteries” live right there.

User properties

Besides the message body, IoT Hub allows sending properties. These are useful metadata for routing messages, applying rules, or filtering telemetry.

For example, we can mark a message as alert = true when the temperature exceeds a threshold.

using System.Collections;
using nanoFramework.Azure.Devices.Client;
using nanoFramework.M2Mqtt;

ArrayList properties = new ArrayList();
properties.Add(new UserProperty("alert", "true"));
properties.Add(new UserProperty("sensor", "dht22"));

client.SendMessage(
    "{ \"temperature\": 61.2 }",
    "application/json",
    properties);
Copied!

This allows us to create rules in the cloud without having to open and parse every JSON.

What does it offer compared to direct MQTT

We could connect to IoT Hub using MQTT manually. In fact, underneath, something similar happens. The difference is that DeviceClient already knows Azure’s conventions:

  • Correct construction of the client ID and MQTT username.
  • Management of SAS keys or certificates.
  • Sending messages in the format expected by IoT Hub.
  • Access to Twin, reported properties, and direct methods.

In other words, we’re not losing control. We’re avoiding writing a glue layer that is usually tedious and easy to break.