Tutoriel : Visualiser des données d’appareil IoT depuis IoT Hub à l’aide du service Azure Web PubSub et d’Azure Functions
Dans ce tutoriel, vous apprenez à utiliser le service Azure Web PubSub et Azure Functions pour générer une application serverless avec une visualisation des données en temps réel depuis IoT Hub.
Dans ce tutoriel, vous allez apprendre à :
- Générer une application de visualisation des données serverless
- Travailler de concert avec des liaisons d’entrée et de sortie de fonction Web PubSub et un hub Azure IoT
- Exécuter les exemples de fonctions localement
Prérequis
Un éditeur de code, comme Visual Studio Code.
Node.js, version 18.x ou ultérieure.
Remarque
Pour plus d’informations sur les versions prises en charge de Node.js, consultez la documentation sur les versions du runtime d’Azure Functions.
Azure Functions Core Tools (v3 ou supérieure de préférence) pour exécuter les applications Azure Function localement et les déployer vers Azure.
Azure CLI pour gérer les ressources Azure.
Si vous n’avez pas d’abonnement Azure, créez un compte gratuit Azure avant de commencer.
Créer un hub IoT
Dans cette section, vous utilisez Azure CLI pour créer un hub IoT et un groupe de ressources. Un groupe de ressources Azure est un conteneur logique dans lequel les ressources Azure sont déployées et gérées. Un hub IoT agit en tant que hub de messages central pour la communication bidirectionnelle entre votre application IoT et les appareils.
Si vous disposez déjà d’un hub IoT dans votre abonnement Azure, vous pouvez ignorer cette section.
Pour créer un hub IoT et un groupe de ressources :
Lancez votre application CLI. Pour utiliser les commandes Common Language Infrastructure (CLI) dans le reste de cet article, copiez la syntaxe de la commande, collez-la dans votre application CLI, modifiez les valeurs des variables et appuyez sur
Enter
.- Si vous utilisez Cloud Shell, sélectionnez le bouton Essayer dans les commandes CLI pour lancer Cloud Shell dans une fenêtre de navigateur partagée. Ou vous pouvez ouvrir Cloud Shell dans un onglet de navigateur distinct.
- Si vous utilisez Azure CLI localement, démarrez votre application console CLI et connectez-vous à Azure CLI.
Exécutez az extension add pour installer ou mettre à niveau l’extension azure-iot vers la version actuelle.
az extension add --upgrade --name azure-iot
Dans votre application CLI, exécutez la commande az group create pour créer un groupe de ressources. La commande suivante crée un groupe de ressources nommé MyResourceGroup à l’emplacement eastus :
Notes
Si vous le souhaitez, vous pouvez définir un autre emplacement. Pour voir les régions disponibles, exécutez
az account list-locations
. Ce démarrage rapide utilise eastus comme indiqué dans l’exemple de commande.az group create --name MyResourceGroup --location eastus
Exécutez la commande az iot hub create pour créer un hub IoT. La création de votre hub IoT peut prendre plusieurs minutes.
YourIotHubName. Remplacez cet espace réservé et les accolades qui l’entourent dans la commande suivante, en utilisant le nom que vous avez choisi pour votre hub IoT. Le nom du hub IoT doit être globalement unique dans Azure. Utilisez le nom de votre hub IoT dans le reste de ce guide de démarrage rapide là où vous voyez l’espace réservé.
az iot hub create --resource-group MyResourceGroup --name {your_iot_hub_name}
Créer une instance Web PubSub
Si vous avez déjà une instance Web PubSub dans votre abonnement Azure, vous pouvez ignorer cette section.
Exécutez la commande az extension add pour installer ou mettre à niveau l’extension webpubsub vers la version actuelle.
az extension add --upgrade --name webpubsub
Utilisez la commande az webpubsub create d’Azure CLI pour créer une instance Web PubSub dans le groupe de ressources que vous avez créé. La commande suivante crée une ressource Web PubSub GRATUITE sous le groupe de ressources myResourceGroup dans la zone EastUS :
Important
Chaque ressource Web PubSub doit avoir un nom unique. Remplacez <your-unique-keyvault-name> par le nom de votre Web PubSub dans les exemples suivants.
az webpubsub create --name "<your-unique-resource-name>" --resource-group "myResourceGroup" --location "EastUS" --sku Free_F1
La sortie de cette commande affiche les propriétés de la ressource que vous venez de créer. Notez les deux propriétés ci-dessous :
- Nom de la ressource : nom que vous avez fourni au paramètre
--name
ci-dessus. - Nom d’hôte : dans l’exemple, le nom d’hôte est
<your-unique-resource-name>.webpubsub.azure.com/
.
À ce stade, votre compte Azure est le seul autorisé à effectuer des opérations sur cette nouvelle ressource.
Créer et exécuter les fonctions localement
Créez un dossier vide pour le projet, puis exécutez la commande suivante dans le nouveau dossier.
func init --worker-runtime javascript --model V4
Créez une fonction
index
pour lire et héberger une page web statique pour les clients.func new -n index -t HttpTrigger
Mettez à jour
src/functions/index.js
avec le code suivant qui sert le contenu HTML en tant que site statique.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, }; } });
Créez un
index.html
fichier sous le dossier racine.<!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>
Créez une fonction
negotiate
que les clients utilisent pour obtenir une URL de connexion de service et un jeton d’accès.func new -n negotiate -t HttpTrigger
Mise à jour
src/functions/negotiate.js
pour l’utiliserWebPubSubConnection
qui contient le jeton généré.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')) }; }, });
Créez une fonction
messagehandler
pour générer des notifications en utilisant le modèle"IoT Hub (Event Hub)"
.func new --template "Azure Event Hub trigger" --name messagehandler
Mettez à jour
src/functions/messagehandler.js
pour ajouter la liaison de sortie Web PubSub avec le code json suivant. Nous utilisons la variable%hubName%
comme nom de hub pour l’eventHubName IoT et le hub Web PubSub.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); } });
Mettez à jour les paramètres de fonction.
Ajoutez un paramètre
hubName
et remplacez{YourIoTHubName}
par le nom de hub que vous avez utilisé lors de la création de votre IoT Hub.func settings add hubName "{YourIoTHubName}"
Obtenez la chaîne de connexion de service de IoT Hub.
az iot hub connection-string show --policy-name service --hub-name {YourIoTHubName} --output table --default-eventhub
Définissez
IOTHubConnectionString
, en remplaçant<iot-connection-string>
par la valeur.func settings add IOTHubConnectionString "<iot-connection-string>"
- Obtenez la chaîne de connexion pour Web PubSub.
az webpubsub key show --name "<your-unique-resource-name>" --resource-group "<your-resource-group>" --query primaryConnectionString
Définissez
WebPubSubConnectionString
, en remplaçant<webpubsub-connection-string>
par la valeur.func settings add WebPubSubConnectionString "<webpubsub-connection-string>"
Notes
Le déclencheur de la fonction
Azure Event Hub trigger
utilisé dans l’exemple a une dépendance vis-à-vis de Stockage Azure, mais vous pouvez utiliser l’émulateur de stockage local quand la fonction s’exécute localement. Si vous rencontrez une erreur commeThere was an error performing a read operation on the Blob Storage Secret Repository. Please ensure the 'AzureWebJobsStorage' connection string is valid.
, vous devez télécharger et activer l’émulateur de stockage.Exécutez la fonction localement.
Vous pouvez maintenant exécuter votre fonction locale par commande ci-dessous.
func start
Vous pouvez consulter la page statique de votre hôte local en visitant :
https://localhost:7071/api/index
.
Exécuter l’appareil pour envoyer des données
Inscrire un appareil
Un appareil doit être inscrit dans votre hub IoT pour pouvoir se connecter. Si vous disposez déjà d’un appareil inscrit dans votre hub IoT, vous pouvez ignorer cette section.
Exécutez la commande az iot hub device-identity create dans Azure Cloud Shell pour créer l’identité d’appareil.
YourIoTHubName : remplacez cet espace réservé par le nom que vous avez choisi pour votre hub IoT.
az iot hub device-identity create --hub-name {YourIoTHubName} --device-id simDevice
Exécutez la commande Az PowerShell module iot hub device-identity connection-string show dans Azure Cloud Shell pour obtenir la chaîne de connexion de l’appareil que vous venez d’inscrire :
YourIoTHubName : Remplacez l’espace réservé ci-dessous par le nom que vous avez choisi pour votre hub IoT.
az iot hub device-identity connection-string show --hub-name {YourIoTHubName} --device-id simDevice --output table
Notez la chaîne de connexion de l’appareil, qui ressemble à ce qui suit :
HostName={YourIoTHubName}.azure-devices.net;DeviceId=simDevice;SharedAccessKey={YourSharedAccessKey}
Pour obtenir les résultats les plus rapides, simulez des données de température à l’aide du simulateur en ligne Raspberry Pi Azure IoT. Collez la chaîne de connexion d’appareil, puis sélectionnez le bouton Exécuter.
Si vous disposez d’un capteur Raspberry Pi et BME280 physique, vous pouvez mesurer et signaler des valeurs réelles de température et d’humidité en suivant le tutoriel Connecter Raspberry Pi à Azure IoT Hub (Node.js).
Exécuter le site web de visualisation
Ouvrez la page d’index de l’hôte de fonction : http://localhost:7071/api/index
pour afficher le tableau de bord en temps réel. Inscrivez plusieurs appareils et vous verrez le tableau de bord mettre à jour plusieurs appareils en temps réel. Ouvrez plusieurs navigateurs et vous verrez que chaque page est mise à jour en temps réel.
Nettoyer les ressources
Si vous prévoyez d’utiliser d’autres démarrages rapides et didacticiels, il peut être utile de conserver ces ressources.
Quand vous n’en avez plus besoin, vous pouvez utiliser la commande Azure CLI az group delete pour supprimer le groupe de ressources ainsi que toutes les ressources associées :
az group delete --name "myResourceGroup"