Zelfstudie: IoT-apparaatgegevens van IoT Hub visualiseren met behulp van de Azure Web PubSub-service en Azure Functions
In deze zelfstudie leert u hoe u de Azure Web PubSub-service en Azure Functions gebruikt om een serverloze toepassing te bouwen met realtime gegevensvisualisatie van IoT Hub.
In deze zelfstudie leert u het volgende:
- Een serverloze app voor gegevensvisualisatie bouwen
- Samenwerken met invoer- en uitvoerbindingen van de Web PubSub-functie en Azure IoT Hub
- De voorbeeldfuncties lokaal uitvoeren
Belangrijk
Onbewerkte verbindingsreeks worden alleen in dit artikel weergegeven voor demonstratiedoeleinden.
Een verbindingsreeks bevat de autorisatiegegevens die nodig zijn voor uw toepassing voor toegang tot de Azure Web PubSub-service. De toegangssleutel in de verbindingsreeks is vergelijkbaar met een hoofdwachtwoord voor uw service. Beveilig uw toegangssleutels altijd in productieomgevingen. Gebruik Azure Key Vault om uw sleutels veilig te beheren en te roteren en uw verbinding te beveiligen.WebPubSubServiceClient
Vermijd het distribueren van toegangssleutels naar andere gebruikers, het coderen ervan of het opslaan van ze ergens in tekst zonder opmaak die toegankelijk is voor anderen. Draai uw sleutels als u denkt dat ze mogelijk zijn aangetast.
Vereisten
Een code-editor zoals Visual Studio Code
-
Notitie
Zie de documentatie over runtimeversies van Azure Functions voor meer informatie over de ondersteunde versies van Node.js.
Azure Functions Core Tools (v3 of hoger) om Azure Function-apps lokaal uit te voeren en te implementeren in Azure.
De Azure CLI voor het beheren van Azure-resources.
Als u geen Azure-abonnement hebt, kunt u een gratis Azure-account maken voordat u begint.
Een IoT Hub maken
In deze sectie gebruikt u Azure CLI om een IoT-hub en een resourcegroep te maken. Een Azure-resourcegroep is een logische container waarin Azure-resources worden geïmplementeerd en beheerd. Een IoT-hub fungeert als een centrale berichtenhub voor bidirectionele communicatie tussen uw IoT-toepassing en de apparaten.
Als u al een IoT-hub in uw Azure-abonnement hebt, kunt u deze sectie overslaan.
Een IoT-hub en een resourcegroep maken:
Start de CLI-app. Als u de CLI-opdrachten in de rest van dit artikel wilt uitvoeren, kopieert u de syntaxis van de opdracht, plakt u deze in uw CLI-app, bewerkt u variabelewaarden en drukt u op
Enter
.- Als u Cloud Shell gebruikt, selecteert u de knop Uitproberen in de CLI-opdrachten om Cloud Shell te starten in een gesplitst browservenster. U kunt de Cloud Shell ook openen in een afzonderlijk browsertabblad.
- Als u Azure CLI lokaal gebruikt, start u uw CLI-console-app en meldt u zich aan bij Azure CLI.
Voer az extension add uit om de azure-iot-extensie te installeren of bij te werken naar de huidige versie.
az extension add --upgrade --name azure-iot
Voer in uw CLI-app de opdracht az group create uit om een resourcegroep te maken. Met de volgende opdracht wordt een resourcegroep met de naam MyResourceGroup gemaakt op de locatie VS - oost.
Notitie
U kunt desgewenst een andere locatie instellen. Als u de beschikbare locaties wilt zien, voert u de opdracht uit
az account list-locations
. In deze quickstart wordt gebruikgemaakt van eastus , zoals wordt weergegeven in de voorbeeldopdracht.az group create --name MyResourceGroup --location eastus
Voer de opdracht az iot hub create uit om een IoT-hub te maken. Het kan enkele minuten duren voordat een IoT-hub is gemaakt.
YourIotHubName. Vervang deze tijdelijke aanduiding en de omringende accolades in de volgende opdracht met behulp van de naam die u hebt gekozen voor uw IoT-hub. De naam van de IoT-hub moet wereldwijd uniek zijn in Azure. Gebruik de naam van uw IoT-hub in de rest van deze quickstart, waar u de tijdelijke aanduiding ook ziet.
az iot hub create --resource-group MyResourceGroup --name {your_iot_hub_name}
Een Web PubSub-exemplaar maken
Als u al een Web PubSub-exemplaar in uw Azure-abonnement hebt, kunt u deze sectie overslaan.
Voer az extension add uit om de webpubsub-extensie te installeren of bij te werken naar de huidige versie.
az extension add --upgrade --name webpubsub
Gebruik de azure CLI az webpubsub create command om een Web PubSub te maken in de resourcegroep die u hebt gemaakt. Met de volgende opdracht maakt u een gratis Web PubSub-resource onder resourcegroep myResourceGroup in EastUS:
Belangrijk
Elke Web PubSub-resource moet een unieke naam hebben. Vervang <uw unieke resourcenaam> door de naam van uw Web PubSub in de volgende voorbeelden.
az webpubsub create --name "<your-unique-resource-name>" --resource-group "myResourceGroup" --location "EastUS" --sku Free_F1
In de uitvoer van deze opdracht ziet u eigenschappen van de zojuist gemaakte resource. Let op de onderstaande twee eigenschappen:
- Resourcenaam: de naam die u hebt opgegeven voor de
--name
bovenstaande parameter. - hostName: In het voorbeeld is
<your-unique-resource-name>.webpubsub.azure.com/
de hostnaam .
Op dit moment is uw Azure-account de enige die gemachtigd is om bewerkingen uit te voeren op deze nieuwe resource.
De functies lokaal maken en uitvoeren
Maak een lege map voor het project en voer vervolgens de volgende opdracht uit in de nieuwe map.
func init --worker-runtime javascript --model V4
Maak een
index
functie voor het lezen en hosten van een statische webpagina voor clients.func new -n index -t HttpTrigger
Werk
src/functions/index.js
bij met de volgende code, die de HTML-inhoud als een statische site dient.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, }; } });
Maak een
index.html
bestand onder de hoofdmap.<!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>
Maak een
negotiate
functie die clients gebruiken om een serviceverbindings-URL en toegangstoken op te halen.func new -n negotiate -t HttpTrigger
Werk
src/functions/negotiate.js
bij voor gebruikWebPubSubConnection
dat het gegenereerde token bevat.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')) }; }, });
Maak een
messagehandler
functie om meldingen te genereren met behulp van de"IoT Hub (Event Hub)"
sjabloon.Onbewerkte verbindingsreeks worden alleen in dit artikel weergegeven voor demonstratiedoeleinden. Beveilig uw toegangssleutels altijd in productieomgevingen. Gebruik Azure Key Vault om uw sleutels veilig te beheren en te roteren en uw verbinding te beveiligen.
WebPubSubServiceClient
func new --template "Azure Event Hub trigger" --name messagehandler
Werk
src/functions/messagehandler.js
bij om Web PubSub-uitvoerbinding toe te voegen met de volgende json-code. We gebruiken een variabele%hubName%
als hubnaam voor zowel IoT eventHubName als 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); } });
Werk de functie-instellingen bij.
Voeg instelling toe
hubName
en vervang deze door{YourIoTHubName}
de hubnaam die u hebt gebruikt bij het maken van uw IoT Hub.func settings add hubName "{YourIoTHubName}"
Haal de serviceverbindingsreeks voor IoT Hub op.
az iot hub connection-string show --policy-name service --hub-name {YourIoTHubName} --output table --default-eventhub
Stel
IOTHubConnectionString
de waarde in en vervang deze<iot-connection-string>
door de waarde.func settings add IOTHubConnectionString "<iot-connection-string>"
- Haal de verbindingsreeks voor Web PubSub op.
az webpubsub key show --name "<your-unique-resource-name>" --resource-group "<your-resource-group>" --query primaryConnectionString
Stel
WebPubSubConnectionString
de waarde in en vervang deze<webpubsub-connection-string>
door de waarde.func settings add WebPubSubConnectionString "<webpubsub-connection-string>"
Notitie
De
Azure Event Hub trigger
functietrigger die in het voorbeeld wordt gebruikt, is afhankelijk van Azure Storage, maar u kunt een lokale opslagemulator gebruiken wanneer de functie lokaal wordt uitgevoerd. Als u een fout krijgt, zoalsThere was an error performing a read operation on the Blob Storage Secret Repository. Please ensure the 'AzureWebJobsStorage' connection string is valid.
, moet u Opslagemulator downloaden en inschakelen.Voer de functie lokaal uit.
U kunt nu uw lokale functie uitvoeren met behulp van de onderstaande opdracht.
func start
U kunt de statische pagina van uw lokale host bezoeken door naar:
https://localhost:7071/api/index
.
Het apparaat uitvoeren om gegevens te verzenden
Een apparaat registreren
Een apparaat moet zijn geregistreerd bij uw IoT-hub voordat het verbinding kan maken. Als u al een apparaat hebt geregistreerd in uw IoT-hub, kunt u deze sectie overslaan.
Voer de opdracht az iot hub device-identity create uit in Azure Cloud Shell om de apparaat-id te maken.
YourIoTHubName: vervang deze tijdelijke aanduiding door de naam die u hebt gekozen voor uw IoT-hub.
az iot hub device-identity create --hub-name {YourIoTHubName} --device-id simDevice
Voer de opdracht voor de ioT-hub-apparaat-id-verbindingsreeks van de Az PowerShell-module uit in Azure Cloud Shell om het apparaat op te halen verbindingsreeks voor het apparaat dat u zojuist hebt geregistreerd:
YourIoTHubName: vervang deze tijdelijke aanduiding door een door u gekozen naam voor de IoT-hub.
az iot hub device-identity connection-string show --hub-name {YourIoTHubName} --device-id simDevice --output table
Noteer het apparaat verbindingsreeks. Dit ziet er als volgt uit:
HostName={YourIoTHubName}.azure-devices.net;DeviceId=simDevice;SharedAccessKey={YourSharedAccessKey}
Voor snelste resultaten simuleert u temperatuurgegevens met behulp van de Raspberry Pi Azure IoT Online Simulator. Plak het apparaat verbindingsreeks en selecteer de knop Uitvoeren.
Als u een fysieke Raspberry Pi- en BME280-sensor hebt, kunt u echte temperatuur- en vochtigheidswaarden meten en rapporteren door de zelfstudie Connect Raspberry Pi te volgen op Azure IoT Hub (Node.js ).
De visualisatiewebsite uitvoeren
Open de indexpagina van de functiehost: http://localhost:7071/api/index
om het realtime dashboard weer te geven. Registreer meerdere apparaten en u ziet dat het dashboard in realtime meerdere apparaten bijwerken. Open meerdere browsers en u ziet dat elke pagina in realtime wordt bijgewerkt.
Resources opschonen
Als u van plan bent om verder te gaan met volgende snelstarts en zelfstudies, kunt u deze resources intact laten.
U kunt de opdracht az group delete van Azure CLI gebruiken om de resourcegroep en alle gerelateerde resources te verwijderen wanneer u deze niet meer nodig hebt.
az group delete --name "myResourceGroup"