Für die Microsoft Azure Beispiele werde ich in Zukunft den Espressif ESP32 (ca. 5,00 € bei Amazon bei Abnahme von 5 Stück) und das MXChip AZ3166 IOT Developer Kit Board (ca. 43,00 € bei Amazon) verwenden. Der Grund ist, dass der Azure IoT Hub Support für den ESP32, aber vor allem für den MXChip, viel robuster und in der Anwendung und Programmierung viel einfacher ist (insbesondere Dokumentation und Beispiele sind genügend vorhanden). Zudem ist der ESP32, genauso wie der MXChip AZ3166, Azure Zertifiziert!

Ich habe es auch mit dem ESP8266 versucht und an sich lässt sich die Kommunikation mit dem IoT Hub nach langer Fehlersuche, Debuggen und Probieren zwar lösen, aber ein gutes Gefühl hinterlässt es nicht.

Der ESP32 in ein SoC (System-on-a-Chip) für IoT-Projekte. Der Chip ist ca. 25% größer als der ESP8266 und fast doppelt so teuer (wobei wir hier von Beträgen um die 5,00 € reden). Es wurde 2016 durch die Firma Espressif vorgestellt und besitzt integrierte WLAN und Bluetooth Unterstützung, CPU mit ein oder zwei Kernen, 532 KB Arbeitsspeicher und 448 KB ROM (Bootloader), 8 MHz interner Oszillator, 34 GPIOs, 2x 8-bit-DAC, 3x UART, 2x I²C, 2x SPI, 2x I²S, … (siehe auch das Datasheet von Espressif).

Das MXChip AZ3166 hingegen wurde von MXCHIP gemeinsam mit der Microsoft Development Platform Division entwickelt und ist folglich ebenfalls Microsoft Azure Zertifiziert. Es bietet von Hause aus 2 Taster, ein Display, einen Temperatur- und Feuchtigkeitssensor sowie weitere Sensoren für Druck, Bewegung (Beschleunigungssensor und Gyroskop) und ein Magnetometer. Das Ganze ist eine Kombination aus ST Microelectronics STM32F412 mit dem MXChip EMW3166

Für das MXChip AZ3166 IOT Developer Kit gibt es auch einen Simulator :) Einfach den Connection String zum IoT Hub angeben und schon kann man den Code ausführen und zwei Beispielprojekte testen.

Wir fokussieren uns in diesem Artikel auf den ESP32 und starten damit, die Entwicklungsumgebung aufzusetzen und anschließend simulierte Daten an den IoT Hub zu senden.

Arduino IDE

Wir verwenden die Arduino IDE für das Kompilieren und Hochladen des Codes. Dazu laden wir die entsprechende Version von der Seite herunter und installieren es einfach.

Wir starten die Arduino IDE und fügen unter Datei > Voreinstellungen im Reiter Einstellungen in Zusätzliche Boardverwalter-URLs die nachfolgende Url ein:

https://dl.espressif.com/dl/package_esp32_index.json

Anschließend öffnen wir den Boardverwalter unter Werkzeuge > Board: “…” > Boardverwalter. Geben im Suchfeld esp32 ein und klicken bei esp32 by Espressif Systems Version 1.04 auf Install.

Nun wählen wir als Board den Eintrag ESP32 Dev Module unter Werkzeuge > Board: “ESP32 Dev Module > ESP32 Arduino > ESP32 Dev Module.

Um das WLAN und die IoT Hub Konnektivität zu überprüfen erstellen wir eine neue Datei via Datei > Neu und kopieren nachfolgenden Code in die Datei.

#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiServer.h>
#include <WiFiUdp.h>

/**
 * A simple Azure IoT example for sending telemetry to Iot Hub.
 */

#include <WiFi.h>
#include "Esp32MQTTClient.h"

#define INTERVAL 10000
#define MESSAGE_MAX_LEN 256
// Please input the SSID and password of WiFi
const char* ssid     = "XXXXXXXX";
const char* password = "XXXXXXXXXX";

/*String containing Hostname, Device Id & Device Key in the format:                         */
/*  "HostName=<host_name>;DeviceId=<device_id>;SharedAccessKey=<device_key>"                */
/*  "HostName=<host_name>;DeviceId=<device_id>;SharedAccessSignature=<device_sas_token>"    */
static const char* connectionString = "HostName=XXXXXXXX.azure-devices.net;DeviceId=XXXXXX;SharedAccessKey=XXXXXXXX";
const char *messageData = "{\"messageId\":%d, \"Temperature\":%f, \"Humidity\":%f, \"target\":\"storage\"}";
static bool hasIoTHub = false;
static bool hasWifi = false;
int messageCount = 1;
static bool messageSending = true;
static uint64_t send_interval_ms;

static void SendConfirmationCallback(IOTHUB_CLIENT_CONFIRMATION_RESULT result)
{
  if (result == IOTHUB_CLIENT_CONFIRMATION_OK)
  {
    Serial.println("Send Confirmation Callback finished.");
  }
}

static void MessageCallback(const char* payLoad, int size)
{
  Serial.println("Message callback:");
  Serial.println(payLoad);
}

static void DeviceTwinCallback(DEVICE_TWIN_UPDATE_STATE updateState, const unsigned char *payLoad, int size)
{
  char *temp = (char *)malloc(size + 1);
  if (temp == NULL)
  {
    return;
  }
  memcpy(temp, payLoad, size);
  temp[size] = '\0';
  // Display Twin message.
  Serial.println(temp);
  free(temp);
}

static int  DeviceMethodCallback(const char *methodName, const unsigned char *payload, int size, unsigned char **response, int *response_size)
{
  LogInfo("Try to invoke method %s", methodName);
  const char *responseMessage = "\"Successfully invoke device method\"";
  int result = 200;

  if (strcmp(methodName, "start") == 0)
  {
    LogInfo("Start sending temperature and humidity data");
    messageSending = true;
  }
  else if (strcmp(methodName, "stop") == 0)
  {
    LogInfo("Stop sending temperature and humidity data");
    messageSending = false;
  }
  else
  {
    LogInfo("No method %s found", methodName);
    responseMessage = "\"No method found\"";
    result = 404;
  }

  *response_size = strlen(responseMessage) + 1;
  *response = (unsigned char *)strdup(responseMessage);

  return result;
}



void setup() {
  Serial.begin(115200);
  Serial.println("ESP32 Device");
  Serial.println("Initializing...");
  Serial.println(" > WiFi");
  Serial.println("Starting connecting WiFi.");

  delay(10);
  WiFi.mode(WIFI_AP);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    hasWifi = false;
  }
  hasWifi = true;
  
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println(" > IoT Hub");
  if (!Esp32MQTTClient_Init((const uint8_t*)connectionString, true))
  {
    hasIoTHub = false;
    Serial.println("Initializing IoT hub failed.");
    return;
  }
  hasIoTHub = true;
  Esp32MQTTClient_SetSendConfirmationCallback(SendConfirmationCallback);
  Esp32MQTTClient_SetMessageCallback(MessageCallback);
  Esp32MQTTClient_SetDeviceTwinCallback(DeviceTwinCallback);
  Esp32MQTTClient_SetDeviceMethodCallback(DeviceMethodCallback);
  Serial.println("Start sending events.");
  randomSeed(analogRead(0));
  send_interval_ms = millis();

}

void loop() {
if (hasWifi && hasIoTHub)
  {
    if (messageSending && 
        (int)(millis() - send_interval_ms) >= INTERVAL)
    {
      // Send teperature data
      char messagePayload[MESSAGE_MAX_LEN];
      float temperature = (float)random(0,500)/10;
      float humidity = (float)random(0, 1000)/10;
      snprintf(messagePayload, MESSAGE_MAX_LEN, messageData, messageCount++, temperature, humidity);
      Serial.println(messagePayload);
      EVENT_INSTANCE* message = Esp32MQTTClient_Event_Generate(messagePayload, MESSAGE);
      Esp32MQTTClient_Event_AddProp(message, "target", "storage");
      Esp32MQTTClient_SendEventInstance(message);
      send_interval_ms = millis();
    }
    else
    {
      Esp32MQTTClient_Check();
    }
  }
  delay(10);
}

Wichtig dabei sind folgende Zeilen im Code:

// Please input the SSID and password of WiFi
const char* ssid     = "XXXXXXXX";
const char* password = "XXXXXXXXXX";

Bei ssid und password geben wir die WLAN SSID an mit dem sich der ESP32 verbinden soll und das dazugehörige Passwort. Der nächste wichtige Eintrag ist der connectionString.

static const char* connectionString = "HostName=XXXXXXXX.azure-devices.net;DeviceId=XXXXXX;SharedAccessKey=XXXXXXXX";

Das ist der Connection String von unserem IoT-Gerät im Azure IoT Hub was wir zuvor erstellt haben. Diesen finden wir im Azure Portal. Dort öffnen wir unseren IoT Hub mit dem Namen iotghub und wählen in der linken Menüleiste den Eintrag Explorer > IoT-Geräte.

Wir klicken auf das IoT-Gerät ESP3201 und kopieren die Primäre Verbindungszeichenfolge in die Zwischenablage und fügen diese in die Konstante ein.

Danach wählen wir im Menü Sketch > Überprüfen/Kompilieren und nach erfolgreichem Kompilieren Sketch > Hochladen. Um zu sehen was passiert, öffnen wir den Seriellen Monitor unter Werkzeuge > Serieller Monitor.

Jetzt sollten Nachrichten an den IoT Hub gesendet werden. Um wiederum zu prüfen ob die Nachrichten im Event Hub kompatiblen Endpunkt des IoT Hubs ankommen, öffnen wir unseren C# Client den wir im Artikel Ein IoT Gerät in C# simulieren und den IoT Hub testen entwickelt haben. Beide Fenster positionieren wir nebeneinander und können sehr schön sehen wie die Nachrichten vom IoT-Gerät verschickt und binnen einer Sekunde im IoT Hub ankommen.

Da die vom IoT-Gerät verschickten Nachrichten mit der Eigenschaft target = “storage” versehen werden, sollten sie auch von unserem im IoT Hub erstellten Route an das Data Lake weitergeroutet werden. Das können wir prüfen in dem wir im Azure Portal unser Azure Speicherkonto iotgstorage öffnen und im linken Menü auf Storage Explorer “Vorschau” klicken. Danach klicken wir auf Container > iotgdatalake und in meinem Fall öffnen wir das Verzeichnis iotghub > 02 > 2021 > 02 > 15 > 13 (es muss das aktuelle Datum und die aktuelle Uhrzeit in Form einer Verzeichnisstruktur sein). Dort sollte sich eine JSON Datei befinden. Wenn wir diese Datei herunterladen und öffnen, sollten wir alle Nachrichten sehen welche vom IoT-Gerät in den letzten Minuten verschickt wurden.

Ein anderes Mal richten wir uns Microsoft Visual Studio Code als Entwicklungsumgebung für die ESP32 und MXChip AZ3166 Projekte ein.