Öğretici: Azure Web PubSub hizmetini ve Azure İşlevleri kullanarak IoT Hub'dan IoT cihaz verilerini görselleştirme
Bu öğreticide Azure Web PubSub hizmetini kullanmayı ve IoT Hub'dan gerçek zamanlı veri görselleştirmesi içeren sunucusuz bir uygulama oluşturmak için Azure İşlevleri öğreneceksiniz.
Bu öğreticide aşağıdakilerin nasıl yapılacağını öğreneceksiniz:
- Sunucusuz veri görselleştirme uygulaması oluşturma
- Web PubSub işlevi giriş ve çıkış bağlamaları ve Azure IoT hub'ı ile birlikte çalışma
- Örnek işlevleri yerel olarak çalıştırma
Önemli
Ham bağlantı dizesi yalnızca tanıtım amacıyla bu makalede görünür.
bağlantı dizesi, uygulamanızın Azure Web PubSub hizmetine erişmesi için gereken yetkilendirme bilgilerini içerir. bağlantı dizesi içindeki erişim anahtarı, hizmetinizin kök parolasına benzer. Üretim ortamlarında erişim anahtarlarınızı her zaman koruyun. Anahtarlarınızı güvenli bir şekilde yönetmek ve döndürmek ve bağlantınızın WebPubSubServiceClient
güvenliğini sağlamak için Azure Key Vault kullanın.
Erişim anahtarlarını diğer kullanıcılara dağıtmaktan, sabit kodlamaktan veya başkalarının erişebileceği herhangi bir yerde düz metin olarak kaydetmekten kaçının. Ele geçirilmiş olabileceklerini düşünüyorsanız anahtarlarınızı döndürün.
Önkoşullar
Visual Studio Code gibi bir kod düzenleyicisi
Azure İşlevi uygulamalarını yerel olarak çalıştırmak ve Azure'a dağıtmak için Temel Araçlar'ı (v3 veya üzeri tercih edilen) Azure İşlevleri.
Azure aboneliğiniz yoksa başlamadan önce birücretsiz Azure hesabı oluşturun.
IoT hub oluşturma
Bu bölümde, IoT hub'ı ve kaynak grubu oluşturmak için Azure CLI'yi kullanacaksınız. Azure kaynak grubu, Azure kaynaklarının dağıtıldığı ve yönetildiği bir mantıksal kapsayıcıdır. IoT hub'ı, IoT uygulamanızla cihazlar arasında çift yönlü iletişim için merkezi bir ileti hub'ı işlevi görür.
Azure aboneliğinizde zaten bir IoT hub'ınız varsa bu bölümü atlayabilirsiniz.
IoT hub'ı ve kaynak grubu oluşturmak için:
CLI uygulamanızı başlatın. Bu makalenin geri kalanında CLI komutlarını çalıştırmak için komut söz dizimini kopyalayın, CLI uygulamanıza yapıştırın, değişken değerlerini düzenleyin ve tuşuna basın
Enter
.- Cloud Shell kullanıyorsanız Cloud Shell'i bölünmüş bir tarayıcı penceresinde başlatmak için CLI komutlarında Deneyin düğmesini seçin. Veya Cloud Shell'i ayrı bir tarayıcı sekmesinde açabilirsiniz.
- Azure CLI'yi yerel olarak kullanıyorsanız CLI konsol uygulamanızı başlatın ve Azure CLI'da oturum açın.
azure-iot uzantısını yüklemek veya geçerli sürüme yükseltmek için az extension add komutunu çalıştırın.
az extension add --upgrade --name azure-iot
CLI uygulamanızda az group create komutunu çalıştırarak bir kaynak grubu oluşturun. Aşağıdaki komut eastus konumunda MyResourceGroup adlı bir kaynak grubu oluşturur.
Not
İsteğe bağlı olarak, farklı bir konum ayarlayabilirsiniz. Kullanılabilir konumları görmek için komutunu çalıştırın
az account list-locations
. Bu hızlı başlangıçta örnek komutta gösterildiği gibi eastus kullanılmıştır.az group create --name MyResourceGroup --location eastus
IoT hub'ı oluşturmak için az iot hub create komutunu çalıştırın. IoT hub'ı oluşturmak birkaç dakika sürebilir.
YourIotHubName. Aşağıdaki komutta ioT hub'ınız için seçtiğiniz adı kullanarak bu yer tutucuyu ve çevresindeki küme ayraçlarını değiştirin. IoT hub adı Azure'da genel olarak benzersiz olmalıdır. Bu hızlı başlangıcın geri kalanında yer tutucuyu gördüğünüz her yerde IoT hub'ınızın adını kullanın.
az iot hub create --resource-group MyResourceGroup --name {your_iot_hub_name}
Web PubSub örneği oluşturma
Azure aboneliğinizde zaten bir Web PubSub örneğiniz varsa bu bölümü atlayabilirsiniz.
Webpubsub uzantısını yüklemek veya geçerli sürüme yükseltmek için az extension add komutunu çalıştırın.
az extension add --upgrade --name webpubsub
Oluşturduğunuz kaynak grubunda bir Web PubSub oluşturmak için Azure CLI az webpubsub create komutunu kullanın. Aşağıdaki komut, EastUS'ta myResourceGroup kaynak grubu altında bir Ücretsiz Web PubSub kaynağı oluşturur:
Önemli
Her Web PubSub kaynağının benzersiz bir adı olmalıdır. Aşağıdaki örneklerde unique-resource-name> değerini Web PubSub'ınızın adıyla değiştirin<.
az webpubsub create --name "<your-unique-resource-name>" --resource-group "myResourceGroup" --location "EastUS" --sku Free_F1
Bu komutun çıktısı yeni oluşturulan kaynağın özelliklerini gösterir. Aşağıda listelenen iki özelliği not edin:
-
Kaynak Adı: Yukarıdaki parametreye
--name
sağladığınız ad. -
hostName: Örnekte ana bilgisayar adı şeklindedir
<your-unique-resource-name>.webpubsub.azure.com/
.
Bu noktada, azure hesabınız bu yeni kaynak üzerinde herhangi bir işlem gerçekleştirme yetkisi olan tek hesaptır.
İşlevleri yerel olarak oluşturma ve çalıştırma
Proje için boş bir klasör oluşturun ve ardından yeni klasörde aşağıdaki komutu çalıştırın.
func init --worker-runtime javascript --model V4
İstemciler için statik bir
index
web sayfasını okumak ve barındırmak için bir işlev oluşturun.func new -n index -t HttpTrigger
HTML içeriğini statik bir site olarak sunan aşağıdaki kodla güncelleştirin
src/functions/index.js
.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, }; } });
Kök klasörün altında bir
index.html
dosya oluşturun.<!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>
negotiate
İstemcilerin hizmet bağlantısı URL'si ve erişim belirteci almak için kullandığı bir işlev oluşturun.func new -n negotiate -t HttpTrigger
Oluşturulan belirteci içeren kullanılacak
WebPubSubConnection
şekilde güncelleştirinsrc/functions/negotiate.js
.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')) }; }, });
Şablonu kullanarak
"IoT Hub (Event Hub)"
bildirim oluşturmak için birmessagehandler
işlev oluşturun.Ham bağlantı dizesi yalnızca tanıtım amacıyla bu makalede görünür. Üretim ortamlarında erişim anahtarlarınızı her zaman koruyun. Anahtarlarınızı güvenli bir şekilde yönetmek ve döndürmek ve bağlantınızın
WebPubSubServiceClient
güvenliğini sağlamak için Azure Key Vault kullanın.func new --template "Azure Event Hub trigger" --name messagehandler
Web PubSub çıkış bağlamasını aşağıdaki json koduyla eklemek için güncelleştirin
src/functions/messagehandler.js
. Hem IoT eventHubName hem de Web PubSub hub'ı için hub adı olarak değişken%hubName%
kullanıyoruz.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); } });
İşlev ayarlarını güncelleştirin.
Ayarı ekleyin
hubName
ve değerini IoT Hub'ınızı oluştururken kullandığınız hub adıyla değiştirin{YourIoTHubName}
.func settings add hubName "{YourIoTHubName}"
IoT Hub için Hizmet Bağlantı Dizesi'ni alın.
az iot hub connection-string show --policy-name service --hub-name {YourIoTHubName} --output table --default-eventhub
değerini değeriyle değiştirerek
<iot-connection-string>
ayarlayınIOTHubConnectionString
.func settings add IOTHubConnectionString "<iot-connection-string>"
- Web PubSub için Bağlantı Dizesini alın.
az webpubsub key show --name "<your-unique-resource-name>" --resource-group "<your-resource-group>" --query primaryConnectionString
değerini değeriyle değiştirerek
<webpubsub-connection-string>
ayarlayınWebPubSubConnectionString
.func settings add WebPubSubConnectionString "<webpubsub-connection-string>"
Not
Örnekte
Azure Event Hub trigger
kullanılan işlev tetikleyicisinin Azure Depolama'ya bağımlılığı vardır, ancak işlev yerel olarak çalışırken yerel bir depolama öykünücüsü kullanabilirsiniz. gibiThere was an error performing a read operation on the Blob Storage Secret Repository. Please ensure the 'AzureWebJobsStorage' connection string is valid.
bir hata alırsanız Depolama Öykünücüsü'ne indirmeniz ve etkinleştirmeniz gerekir.İşlevi yerel olarak çalıştırın.
Artık yerel işlevinizi aşağıdaki komutla çalıştırabilirsiniz.
func start
Yerel ana bilgisayar statik sayfanızı şu adresi ziyaret ederek ziyaret edebilirsiniz:
https://localhost:7071/api/index
.
Verileri göndermek için cihazı çalıştırma
Cihaz kaydetme
Bir cihazın bağlanabilmesi için IoT hub’ınıza kaydedilmesi gerekir. IoT hub'ınıza kayıtlı bir cihazınız varsa bu bölümü atlayabilirsiniz.
Cihaz kimliğini oluşturmak için Azure Cloud Shell'de az iot hub device-identity create komutunu çalıştırın.
YourIoTHubName: Bu yer tutucuyu IoT hub'ınız için seçtiğiniz adla değiştirin.
az iot hub device-identity create --hub-name {YourIoTHubName} --device-id simDevice
Az PowerShell module iot hub device-identity connection-string show komutunu Azure Cloud Shell'de çalıştırarak az önce kaydettiğiniz cihazın cihaz bağlantı dizesi alın:
YourIoTHubName: Aşağıdaki yer tutucuyu IoT hub'ınız için seçtiğiniz adla değiştirin.
az iot hub device-identity connection-string show --hub-name {YourIoTHubName} --device-id simDevice --output table
Cihaz bağlantı dizesi not edin ve şuna benzer:
HostName={YourIoTHubName}.azure-devices.net;DeviceId=simDevice;SharedAccessKey={YourSharedAccessKey}
En hızlı sonuçlar için Raspberry Pi Azure IoT Online Simulator'ı kullanarak sıcaklık verilerinin simülasyonunu başlatın. Cihaz bağlantı dizesi yapıştırın ve Çalıştır düğmesini seçin.
Fiziksel Raspberry Pi ve BME280 algılayıcınız varsa Raspberry Pi'yi Azure IoT Hub'a bağlama (Node.js) öğreticisini izleyerek gerçek sıcaklık ve nem değerlerini ölçebilir ve raporlayabilirsiniz.
Görselleştirme web sitesini çalıştırma
gerçek zamanlı panoyu görüntülemek için işlev ana bilgisayar dizini sayfasını http://localhost:7071/api/index
açın. Birden çok cihaz kaydettiğinizde panonun birden çok cihazı gerçek zamanlı olarak güncelleştirdiğini görürsünüz. Birden çok tarayıcı açtığınızda her sayfanın gerçek zamanlı olarak güncelleştirildiğini görürsünüz.
Kaynakları temizleme
Sonraki hızlı başlangıç ve öğreticilerle çalışmaya devam etmeyi planlıyorsanız, bu kaynakları yerinde bırakmanız yararlı olabilir.
Artık gerekli olmadığında, kaynak grubunu ve tüm ilgili kaynakları kaldırmak için Azure CLI az group delete komutunu kullanabilirsiniz:
az group delete --name "myResourceGroup"