Tutorial: Visualisieren von IoT-Gerätedaten aus IoT Hub mit dem Azure Web PubSub-Dienst und Azure Functions
In diesem Tutorial erfahren Sie, wie Sie den Azure Web PubSub-Dienst und Azure Functions verwenden, um eine serverlose Anwendung mit Echtzeit-Datenvisualisierung über IoT Hub zu erstellen.
In diesem Tutorial lernen Sie Folgendes:
- Erstellen einer serverlosen Datenvisualisierungs-App
- Zusammenarbeiten mit Ein- und Ausgabebindungen von Web PubSub-Funktionen und Azure IoT Hub
- Lokales Ausführen der Beispielfunktionen
Voraussetzungen
Ein Code-Editor wie Visual Studio Code
Node.js, Version 18.x oder höher.
Hinweis
Weitere Informationen zu den unterstützten Versionen von Node.js finden Sie in der Übersicht über die Runtimeversionen von Azure Functions.
Azure Functions Core Tools (v3 oder höher bevorzugt), um Azure-Funktions-Apps lokal auszuführen und in Azure bereitstellen zu können.
Azure CLI zum Verwalten von Azure-Ressourcen
Sollten Sie über kein Azure-Abonnement verfügen, können Sie zunächst ein kostenloses Azure-Konto erstellen.
Erstellen eines IoT-Hubs
In diesem Abschnitt verwenden Sie die Azure CLI zum Erstellen eines IoT-Hubs und einer Ressourcengruppe. Eine Azure-Ressourcengruppe ist ein logischer Container, in dem Azure-Ressourcen bereitgestellt und verwaltet werden. Ein IoT-Hub fungiert als zentraler Nachrichtenhub für die bidirektionale Kommunikation zwischen Ihrer IoT-Anwendung und den Geräten.
Wenn Sie bereits über einen IoT-Hub in Ihrem Azure-Abonnement verfügen, können Sie diesen Abschnitt überspringen.
So erstellen Sie einen IoT-Hub und eine Ressourcengruppe:
Starten der CLI-App. Kopieren Sie zum Ausführen der CLI-Befehle im restlichen Teil dieses Artikels die Befehlssyntax, fügen Sie diese in Ihre CLI-App ein, bearbeiten Sie die Variablenwerte und drücken Sie
Enter
.- Wählen Sie bei Verwendung von Cloud Shell die Schaltfläche Ausprobieren in den CLI-Befehlen aus, um Cloud Shell in einem geteilten Browserfenster zu starten. Alternativ können Sie Cloud Shell auch in einem separaten Browsertab öffnen.
- Gehen Sie wie folgt vor, wenn Sie die Azure CLI lokal verwenden: Starten Sie Ihre CLI-Konsolen-App, und melden Sie sich bei der Azure CLI an.
Führen Sie az extension add aus, um die Erweiterung azure-iot zu installieren bzw. auf die aktuelle Version zu aktualisieren.
az extension add --upgrade --name azure-iot
Führen Sie in Ihrer CLI-App den Befehl az group create aus, um eine Ressourcengruppe zu erstellen. Mit dem folgenden Befehl wird eine Ressourcengruppe mit dem Namen MyResourceGroup am Standort eastus erstellt.
Hinweis
Optional können Sie einen anderen Standort festlegen. Führen Sie zum Anzeigen der verfügbaren Standorte
az account list-locations
aus. In dieser Schnellstartanleitung wird eastus verwendet, wie im Beispielbefehl gezeigt.az group create --name MyResourceGroup --location eastus
Führen Sie den Befehl az iot hub create aus, um einen IoT-Hub zu erstellen. Es kann einige Minuten dauern, bis ein IoT-Hub erstellt wurde.
YourIotHubName: Ersetzen Sie diesen Platzhalter und die umgebenden geschweiften Klammern im folgenden Befehl durch den Namen, den Sie für Ihren IoT-Hub ausgewählt haben. Der Name eines IoT-Hubs muss in Azure global eindeutig sein. Verwenden Sie im weiteren Verlauf dieser Schnellstartanleitung den Namen des IoT-Hubs für alle Vorkommen dieses Platzhalters.
az iot hub create --resource-group MyResourceGroup --name {your_iot_hub_name}
Erstellen einer Web PubSub-Instanz
Wenn Sie bereits über eine Web PubSub-Instanz Hub in Ihrem Azure-Abonnement verfügen, können Sie diesen Abschnitt überspringen.
Führen Sie az extension add aus, um die Erweiterung webpubsub zu installieren bzw. auf die aktuelle Version zu aktualisieren.
az extension add --upgrade --name webpubsub
Verwenden Sie den Azure CLI-Befehl az webpubsub create, um eine Web PubSub-Instanz in der erstellten Ressourcengruppe zu erstellen. Mit dem folgenden Befehl wird eine Kostenlose Web-PubSub-Ressource unter der Ressourcengruppe myResourceGroup in USA, Osten erstellt:
Wichtig
Jede Web PubSub-Ressource muss einen eindeutigen Namen haben. Ersetzen Sie in den folgenden Beispielen <your-unique-resource-name> durch den Namen Ihrer Web PubSub-Instanz.
az webpubsub create --name "<your-unique-resource-name>" --resource-group "myResourceGroup" --location "EastUS" --sku Free_F1
In der Ausgabe dieses Befehls werden die Eigenschaften der neu erstellten Ressource angezeigt. Beachten Sie die beiden folgenden Eigenschaften:
- Resource Name: Der Name, den Sie oben für den Parameter
--name
angegeben haben. - hostName: Im Beispiel lautet der Hostname
<your-unique-resource-name>.webpubsub.azure.com/
.
An diesem Punkt ist nur Ihr Azure-Konto zum Ausführen von Vorgängen für die neue Ressource autorisiert.
Erstellen und lokales Ausführen der Funktionen
Erstellen Sie einen leeren Ordner für das Projekt, und führen Sie dann den folgenden Befehl im neuen Ordner aus.
func init --worker-runtime javascript --model V4
Erstellen Sie eine
index
-Funktion zum Lesen und Hosten einer statischen Webseite für Clients.func new -n index -t HttpTrigger
Aktualisieren Sie
src/functions/index.js
mit dem folgenden Code, der den HTML-Inhalt als statische Website bereitstellt.const { app } = require('@azure/functions'); const { readFile } = require('fs/promises'); app.http('index', { methods: ['GET', 'POST'], authLevel: 'anonymous', handler: async (context) => { const content = await readFile('index.html', 'utf8', (err, data) => { if (err) { context.err(err) return } }); return { status: 200, headers: { 'Content-Type': 'text/html' }, body: content, }; } });
Erstellen Sie eine
index.html
Datei unter dem Stammordner.<!doctype html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <script src="https://cdn.jsdelivr.net/npm/chart.js@2.8.0/dist/Chart.min.js" type="text/javascript" charset="utf-8"></script> <script> document.addEventListener("DOMContentLoaded", async function (event) { const res = await fetch(`/api/negotiate?id=${1}`); const data = await res.json(); const webSocket = new WebSocket(data.url); class TrackedDevices { constructor() { // key as the deviceId, value as the temperature array this.devices = new Map(); this.maxLen = 50; this.timeData = new Array(this.maxLen); } // Find a device temperature based on its Id findDevice(deviceId) { return this.devices.get(deviceId); } addData(time, temperature, deviceId, dataSet, options) { let containsDeviceId = false; this.timeData.push(time); for (const [key, value] of this.devices) { if (key === deviceId) { containsDeviceId = true; value.push(temperature); } else { value.push(null); } } if (!containsDeviceId) { const data = getRandomDataSet(deviceId, 0); let temperatures = new Array(this.maxLen); temperatures.push(temperature); this.devices.set(deviceId, temperatures); data.data = temperatures; dataSet.push(data); } if (this.timeData.length > this.maxLen) { this.timeData.shift(); this.devices.forEach((value, key) => { value.shift(); }) } } getDevicesCount() { return this.devices.size; } } const trackedDevices = new TrackedDevices(); function getRandom(max) { return Math.floor((Math.random() * max) + 1) } function getRandomDataSet(id, axisId) { return getDataSet(id, axisId, getRandom(255), getRandom(255), getRandom(255)); } function getDataSet(id, axisId, r, g, b) { return { fill: false, label: id, yAxisID: axisId, borderColor: `rgba(${r}, ${g}, ${b}, 1)`, pointBoarderColor: `rgba(${r}, ${g}, ${b}, 1)`, backgroundColor: `rgba(${r}, ${g}, ${b}, 0.4)`, pointHoverBackgroundColor: `rgba(${r}, ${g}, ${b}, 1)`, pointHoverBorderColor: `rgba(${r}, ${g}, ${b}, 1)`, spanGaps: true, }; } function getYAxy(id, display) { return { id: id, type: "linear", scaleLabel: { labelString: display || id, display: true, }, position: "left", }; } // Define the chart axes const chartData = { datasets: [], }; // Temperature (ºC), id as 0 const chartOptions = { responsive: true, animation: { duration: 250 * 1.5, easing: 'linear' }, scales: { yAxes: [ getYAxy(0, "Temperature (ºC)"), ], }, }; // Get the context of the canvas element we want to select const ctx = document.getElementById("chart").getContext("2d"); chartData.labels = trackedDevices.timeData; const chart = new Chart(ctx, { type: "line", data: chartData, options: chartOptions, }); webSocket.onmessage = function onMessage(message) { try { const messageData = JSON.parse(message.data); console.log(messageData); // time and either temperature or humidity are required if (!messageData.MessageDate || !messageData.IotData.temperature) { return; } trackedDevices.addData(messageData.MessageDate, messageData.IotData.temperature, messageData.DeviceId, chartData.datasets, chartOptions.scales); const numDevices = trackedDevices.getDevicesCount(); document.getElementById("deviceCount").innerText = numDevices === 1 ? `${numDevices} device` : `${numDevices} devices`; chart.update(); } catch (err) { console.error(err); } }; }); </script> <style> body { font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; padding: 50px; margin: 0; text-align: center; } .flexHeader { display: flex; flex-direction: row; flex-wrap: nowrap; justify-content: space-between; } #charts { display: flex; flex-direction: row; flex-wrap: wrap; justify-content: space-around; align-content: stretch; } .chartContainer { flex: 1; flex-basis: 40%; min-width: 30%; max-width: 100%; } a { color: #00B7FF; } </style> <title>Temperature Real-time Data</title> </head> <body> <h1 class="flexHeader"> <span>Temperature Real-time Data</span> <span id="deviceCount">0 devices</span> </h1> <div id="charts"> <canvas id="chart"></canvas> </div> </body> </html>
Erstellen Sie eine
negotiate
-Funktion, die Clients verwenden, um eine Dienstverbindungs-URL und ein Zugriffstoken abzurufen.func new -n negotiate -t HttpTrigger
Aktualisieren Sie
src/functions/negotiate.js
die VerwendungWebPubSubConnection
, die das generierte Token enthält.const { app, input } = require('@azure/functions'); const connection = input.generic({ type: 'webPubSubConnection', name: 'connection', hub: '%hubName%' }); app.http('negotiate', { methods: ['GET', 'POST'], authLevel: 'anonymous', extraInputs: [connection], handler: async (request, context) => { return { body: JSON.stringify(context.extraInputs.get('connection')) }; }, });
Erstellen Sie eine
messagehandler
-Funktion zum Generieren von Benachrichtigungen mithilfe der Vorlage"IoT Hub (Event Hub)"
.func new --template "Azure Event Hub trigger" --name messagehandler
Aktualisieren Sie
src/functions/messagehandler.js
, um die Web PubSub-Ausgabebindung mit dem folgenden JSON-Code hinzuzufügen. Wir verwenden die Variable%hubName%
als Hubname für eventHubName in IoT und den Web PubSub-Hub.const { app, output } = require('@azure/functions'); const wpsAction = output.generic({ type: 'webPubSub', name: 'action', hub: '%hubName%' }); app.eventHub('messagehandler', { connection: 'IOTHUBConnectionString', eventHubName: '%hubName%', cardinality: 'many', extraOutputs: [wpsAction], handler: (messages, context) => { var actions = []; if (Array.isArray(messages)) { context.log(`Event hub function processed ${messages.length} messages`); for (const message of messages) { context.log('Event hub message:', message); actions.push({ actionName: "sendToAll", data: JSON.stringify({ IotData: message, MessageDate: message.date || new Date().toISOString(), DeviceId: message.deviceId, })}); } } else { context.log('Event hub function processed message:', messages); actions.push({ actionName: "sendToAll", data: JSON.stringify({ IotData: message, MessageDate: message.date || new Date().toISOString(), DeviceId: message.deviceId, })}); } context.extraOutputs.set(wpsAction, actions); } });
Aktualisieren Sie die Funktionseinstellungen.
Fügen Sie die Einstellung
hubName
hinzu, und ersetzen Sie{YourIoTHubName}
durch den Hubnamen, den Sie beim Erstellen Ihrer IoT Hub-Instanz verwendet haben.func settings add hubName "{YourIoTHubName}"
Rufen Sie die Dienstverbindungszeichenfolge für IoT Hub ab.
az iot hub connection-string show --policy-name service --hub-name {YourIoTHubName} --output table --default-eventhub
Legen Sie
IOTHubConnectionString
fest, wobei Sie<iot-connection-string>
durch den Wert ersetzen.func settings add IOTHubConnectionString "<iot-connection-string>"
- Rufen Sie die Verbindungszeichenfolge für Web PubSub ab.
az webpubsub key show --name "<your-unique-resource-name>" --resource-group "<your-resource-group>" --query primaryConnectionString
Legen Sie
WebPubSubConnectionString
fest, wobei Sie<webpubsub-connection-string>
durch den Wert ersetzen.func settings add WebPubSubConnectionString "<webpubsub-connection-string>"
Hinweis
Der im Beispiel verwendete Funktionstrigger
Azure Event Hub trigger
ist von Azure Storage abhängig. Sie können jedoch einen lokalen Speicheremulator verwenden, wenn die Funktion lokal ausgeführt wird. Wird eine Fehlermeldung wieThere was an error performing a read operation on the Blob Storage Secret Repository. Please ensure the 'AzureWebJobsStorage' connection string is valid.
angezeigt, müssen Sie den Speicheremulator herunterladen und aktivieren.Führen Sie die Funktion lokal aus.
Jetzt können Sie Ihre lokale Funktion mit dem folgenden Befehl ausführen.
func start
Sie können die statische Seite Ihres lokalen Hosts aufrufen, indem Sie Folgendes besuchen:
https://localhost:7071/api/index
.
Ausführen des Geräts zum Senden von Daten
Registrieren eines Geräts
Ein Gerät muss bei Ihrer IoT Hub-Instanz registriert sein, um eine Verbindung herstellen zu können. Wenn Sie bereits ein Gerät in Ihrem IoT-Hub registriert haben, können Sie diesen Abschnitt überspringen.
Führen Sie den Befehl az iot hub device-identity create in Azure Cloud Shell aus, um die Geräteidentität zu erstellen.
YourIoTHubName: Ersetzen Sie diesen Platzhalter durch den für Ihren IoT-Hub ausgewählten Namen.
az iot hub device-identity create --hub-name {YourIoTHubName} --device-id simDevice
Führen Sie den Befehl Az PowerShell module iot hub device-identity connection-string show in Azure Cloud Shell aus, um die Verbindungszeichenfolge für das soeben registrierte Gerät abzurufen:
YourIoTHubName: Ersetzen Sie diesen Platzhalter unten durch den Namen, den Sie für Ihren IoT-Hub ausgewählt haben.
az iot hub device-identity connection-string show --hub-name {YourIoTHubName} --device-id simDevice --output table
Notieren Sie sich die Geräteverbindungszeichenfolge, die wie folgt aussieht:
HostName={YourIoTHubName}.azure-devices.net;DeviceId=simDevice;SharedAccessKey={YourSharedAccessKey}
Simulieren Sie Temperaturdaten mit dem Raspberry Pi-Azure IoT-Onlinesimulator, um schnellstmöglich Ergebnisse zu erzielen. Fügen Sie die Geräteverbindungszeichenfolge ein, und wählen Sie die Schaltfläche Ausführen aus.
Mit einem physischen Raspberry Pi- und BME280-Sensor können Sie reale Temperatur- und Luftfeuchtigkeitswerte messen und melden, indem Sie die Schritte im Tutorial Verbinden von Raspberry Pi mit Azure IoT Hub (Node.js) ausführen.
Ausführen der Visualisierungswebsite
Öffnen Sie die Indexseite des Funktionshosts (http://localhost:7071/api/index
), um das Echtzeitdashboard anzuzeigen. Registrieren Sie mehrere Geräte. Sie werden sehen, dass das Dashboard mehrere Geräte in Echtzeit aktualisiert. Öffnen Sie mehrere Browser, und Sie werden sehen, dass jede Seite in Echtzeit aktualisiert wird.
Bereinigen von Ressourcen
Falls Sie mit weiteren Schnellstartanleitungen und Tutorials fortfahren möchten, sollten Sie die Ressourcen nicht bereinigen.
Wenn Sie die Ressourcen nicht mehr benötigen, führen Sie den Azure CLI-Befehl az group delete aus, um die Ressourcengruppe und alle dazugehörigen Ressourcen zu löschen:
az group delete --name "myResourceGroup"