# IoT Central
# Doelstelling
- Visualiseren op een dashboard van data afkomstig van een IoT-device (microcontroller of Raspberry Pi).
- Actuatoren op een IoT-device aansturen met een dashboard.
Er wordt gebruik gemaakt van Azure IoT-Central van Microsoft.
# Inleiding
Azure is de cloud computing omgeving van Microsoft. Bij de cloud denken we meestal aan het online bewaren van data (foto’s, documenten, .. ) bij One Drive, Dropbox, Google Drive of een andere cloud opslag dienst.
Bij de cloud staat de data echter altijd op een fysieke server. Deze fysieke server staat echter niet bij je thuis maar in een datacenter.
Enkele voorbeelden van datacenters bij Microsoft:
https://www.youtube.com/watch?v=80aK2_iwMOs (opens new window)
https://www.youtube.com/watch?v=9nLD7bc5O1g (opens new window)
Bij cloud computing is het ook mogelijk om zelf virtuele servers op te zetten. Hierbij maak je een virtuele server aan op een fysische server in het datacenter. Deze virtuele server kan vervolgens dienst doen als webserver, database, analyseren van data, ..
Enkele voordelen van cloud computing zijn:
Er moet door de gebruiker geen eigen hardware en software aangekocht worden voor een eigen datacenter.
De beveiliging van de infrastructuur zit bij de cloud provider.
De cloud provider maakt back-ups van de gegevens waardoor deze niet verloren kunnen gaan.
De rekenkracht van het datacenter wordt verdeeld over alle gebruikers van het datacenter.
De gebruiker moet enkel over een computer met internettoegang en een browser beschikken. Via een portal krijgt de gebruiker toegang tot de nodige servers.
Meer uitleg over cloud computing kan je nalezen op [https://www.true.nl/blog/wat-is-azure/] (https://www.true.nl/blog/wat-is-azure/ (opens new window))
# De portal van de azure cloud omgeving
Er zijn een aantal modellen mogelijk. Zowel SaaS (Software as a Service), Paas (Platform as a Service) en IaaS (Infrastructure as a Service) zijn mogelijk met Azure.
# On-Premises
Dit is geen cloudmodel. Alle servers, software, data en applicaties staan bij de gebruiker.
# IaaS
Dit cloudservicemodel komt het dichtst in de buurt van het beheren van fysieke servers. Een cloudprovider houdt de hardware up-to-date, maar het onderhoud van het besturingssysteem en de netwerkconfiguratie blijft een taak van de gebruiker. Een voordeel van dit cloudservicemodel is de snelle implementatie van nieuwe toestellen. Het instellen van een nieuwe virtuele machine gaat aanzienlijk sneller dan het aanschaffen, installeren en configureren van een fysieke server.
# PaaS
Dit cloudservicemodel is een beheerde hostingomgeving. De cloudprovider beheert de virtuele machines en netwerkresources en de gebruiker implementeert de bijbehorende toepassingen in de beheerde hostingomgeving.
# SaaS
In dit cloudservicemodel beheert de cloudprovider alle aspecten van de toepassingsomgeving, zoals virtuele machines, netwerkresources, gegevensopslag en toepassingen. De gebruiker hoeft alleen maar de gegevens op te geven bij de toepassing die door de cloudprovider wordt beheerd.
# Registratie Azure
Een gratis studentenaccount kan aangemaakt worden op https://azure.microsoft.com/nl-nl/free/students/ (opens new window)
Registreer je met je e-mailadres van school. Wanneer je registreert met een ander e-mailadres, krijg je de vraag om met het e-mailadres van de school te registeren.
Vul je telefoonnummer in als verificatie.
Registreer je.
Ga akkoord met de voorwaarden.
# Resourcegroep
Wanneer je iets wil maken in Azure (Virtuele server, IoT-Hub, IoT-Central, database, …) moet er een resourcegroep aangemaakt worden.
Kies "+ Een resource maken".
Geef in het zoekvenster "Resourcegroep" in.
Klik op Maken.
Geef de resourcegroep een zelfgekozen naam en kies de dichtste locatie om de resourcegroep te bewaren.
Klik vervolgens op "Beoordelen en maken".
# Azure IoT-Central
Azure IoT-Central maakt gebruik van het SaaS (Software as a Service) model. In deze cursus stuurt een IoT-apparaat (ESP8266, ESP32 of raspberry pi) data door naar Azure IoT-Cental. De data (random temperatuur en luchtvochtigheid) wordt vervolgens weergegeven op een dashboard in je browser.
Eveneens wordt een actuator op het IoT-apparaat (led) bediend vanuit het dashboard.
Na het realiseren van dit voorbeeld ben je in staat om in combinatie met de andere cursussen op https://stem-ict.be/ (opens new window) data afkomstig van een sensor door te sturen naar IoT-Central en actuatoren aan te sturen.
Met het gratis pakket kunnen 30 000 berichten per maand verzonden worden.
# Aanmaak IoT-Central
Azure IOT-Central is een cloud applicatie (SaaS).
Maak deze cloud applicatie aan door in de portal te klikken op “+ Een resource maken”.
Geef in het zoekvenster “IoT Central application” in:
Klik op "Maken".
Vul een zelfgekozen Resourcenaam in. De URL van je aangemaakte IoT-Central applicatie is gebaseerd op je zelfgekozen Resourcenaam. Selecteer het studentenabonnement en de vooraf aangemaakte Resourcegroep.
Kies voor aangepaste toepassing als sjabloon en de dichtste locatie.
Wanneer alles goed gelukt is krijg je de melding dat de implementatie goed gelukt is.
Selecteer in de Azure portal de zelf aangemaakte IoT-Central-toepassing.
Klik op de URL van de aangemaakte toepassing.
Je komt op de portal van de aangemaakte IoT Central toepassing.
# Aanmaken Apparaatsjabloon
Voor een nieuw IoT-device moet eerst een sjabloon apparaat aangemaakt worden.
Maak een Apparaatsjabloon aan door op "Apparaatsjablonen" te klikken in de linker balk. Kies vervolgens “+Nieuw”.
Kies als type “IoT-apparaat” en klik op “Volgende: Aanpassen”.
Voer een zelfgekozen naam in voor het apparaatsjabloon en klik op Volgende: Beoordelen.
Klik op “Maken”.
Wanneer het sjabloon aangemaakt is krijg je bevestiging.
# Interface toevoegen
We voorzien voor het apparaat een interface. Een interface wordt gebruikt om een sensorwaarde in te lezen of een actuator aan te sturen. In het voorbeeld voorzien we het inlezen van 2 sensorenwaardes (temp en hum) en aansturen van 1 actuator waarvoor 2 commando's nodig zijn (LedOn en LedOff).
Klik op Aangepast model.
Klik op “Mogelijkheid toevoegen aan standaardonderdeel” en vul de gekozen weergavenaam in. Wanneer je sensordata wil inlezen staat “Type van …” op Telemetry. Kies een eenheid uit de lijst. Dit is echter geen verplichting.
Klik op “+ Een mogelijkheid toevoegen” om de 2de waarde van de interface toe te voegen.
Indien we een actuator toevoegen moet je “Telemetry” aanpassen naar “Command”. In het voorbeeld schakelen we een led in en uit. Voorzie hiervoor 2 commando's.
Bij het programma voor de ESP wordt gebruik gemaakt van polling. Hierdoor is de ESP niet constant verbonden. Schakel daarom "Wachtrij indien offline" in bij beide commando's. In het voorbeeld ledOn en ledOff.
Klik op “Opslaan” om de interface te bewaren.
# Visuele weergave
Kies onder “Weergaven” voor “Het apparaat visualiseren”.
We kiezen in het eerste voorbeeld voor een lijndiagram om de telemetrie data weer te geven. (voorbeeld temperatuur, luchtvochtigheid, ...)
Klik vervolgens op "Tegel toevoegen".
Klik vervolgens op "Configureren".
Stel de titel in, kies het weergavebereik, het interval en klik vervolgens op "+Mogelijkheid".
Kies uit de Telemetrie de mogelijkheid die grafisch moet weergegeven worden.
Klik vervolgens op "Bijwerken".
Voeg eventueel nog andere telemetrie toe door een type tegel (voorbeeld Lijndiagram te selecteren) en vervolgens op "Tegel toevoegen" te klikken.
Wanneer alle telemetry gegevens aanwezig zijn klik je op "opslaan".
Voor het sjabloon bruikbaar is moet dit gepubliceerd worden. Klik hiervoor op “Publiceren”.
Klik nogmaals op “Publiceren”.
# Importeren van een apparaatsjabloon uit een json file
Het is mogelijk een apparaat sjabloon te importeren als json file. Download volgende json file:
Ga naar “Apparaatsjablonen” in je Central application. Klik bovenaan op “nieuw”.
Kies voor “IoT-apparaat” Klik vervolgens onderaan op “Volgende: Aanpassen”.
Geef een naam aan het apparaat (vrij te kiezen) en klik vervolgens op “Volgende: Beoordelen”.
Klik vervolgens op “Maken”.
Op de volgende pagina kiezen we nu voor “Een model importeren”. Dit is anders dan bij de normale stappen waar we zelf ons model opstellen.
Via het popup venster selecteren we ons model die beschreven staat als een JSON file.
Op de volgende pagina kiezen we nu voor “Een model importeren”. Dit is anders dan bij de normale stappen waar we zelf ons model opstellen.
Via het popup venster selecteren we ons model die beschreven staat als een JSON file.
Als alles goed gaat is ons volledige model meteen klaar.
# Aanmaken apparaat
Maak vanuit het sjabloon een nieuw apparaat aan. Klik op “+ Nieuw” in Alle apparaten.
Selecteer het ontworpen Apparaatsjabloon, pas indien nodig de apparaatnaam aan.
# ESP8266 en ESP32 arduino
In het voorbeeld sturen we data door met een ESP8266 of ESP32 microcontroller (thing) naar Azure IOT-Central. Eveneens sturen we vanop het dashboard in Azure IoT-Central een actuator aan op de ESP8266 of ESP32.
Download het voorbeeldprogramma van volgende github pagina. https://github.com/VTITorhout/IoT_ESP_Azure (opens new window)
Opteer eventueel om de volledige code als zip file te downloaden.
De instellingen van het wifi netwerk en de verbinding met Azure is mogelijk in de config.h file.
Vul de gegevens van het wifi netwerk in:
#define WIFI_SSID "YOUR-SSID-HERE"
#define WIFI_PSK "YOUR-PSWD-HERE"
2
In het voorbeeld is de ingebouwde led van het Firebeetle ESP32 board de actuator die verbonden is met pin 2. Pas dit indien nodig aan bij een ander bord.
#define DBG_PIN 2 //LED is connected to GPIO2 on the Firebeetle ESP32 board
Zorg dat IOT-Central op true staat.
#define IOT_CENTRAL true
Met volgende regel code kan ingesteld worden om de hoeveel tijd info naar Azure verzonden wordt.
#define IOT_UPDATE 300 //send new message to cloud every x seconds
Via polling wordt gecontroleerd of de toestand van een acutator moet veranderen. Met volgende regel code wordt ingesteld om de hoeveel seconden gecontroleerd wordt.
#define POLL_TIME 60 //check for new C2D message every x seconds
De volgende delen code zijn afhankelijk van de instellingen in Azure IOT-central
const char* SCOPE_ID = "<ENTER SCOPE ID HERE>";
const char* DEVICE_ID = "<ENTER DEVICE ID HERE>";
const char* DEVICE_KEY = "<ENTER DEVICE primary/secondary KEY HERE>";
2
3
De gegevens van het apparaat zijn te vinden in IoT-Central onder « Verbinding met »:
Vul het « id-bereik », de « Apparaat-id » en de « primaire sleutel » in de arduino code in.
De totale code van de config file:
//Global settings (hardware, WiFi credentials)
#define WIFI_SSID "YOUR-SSID-HERE" //set to SSID to connect to
#define WIFI_PSK "YOUR-PSWD-HERE" //set to password of the SSID
#define USE_LAN false //set true if wired network must be used
//removing this setting will use WiFi
//will only work with Wizznet SPI board on ESP8266
//other libraries are needed for ESP32 (future?)
#define ETH_CS 4 //wired ethernet WIZ5500 CS pin
#define DBG_PIN 2 //LED is connected to GPIO2 on the Firebeetle ESP32 board
//Azure interface setup
#define IOT_CENTRAL false //set to true if we use central, if false, direct hub access will be used
#define SAS_RENEW 15 //SAS regenerate every x minutes
#define IOT_UPDATE 300 //send new message to cloud every x seconds
#define POLL_TIME 60 //check for new C2D message every x seconds
//Azure credentials
//when using hub, a primary connection string is needed (containing hostname, device and SAS key)
// --> you can find this connection string in the azure portal
const char* primaireVerbindingsreeks = "<primaire verbindingsreeks IoT-Hub>";
//when using central, you have to provide the scope, device and key
// --> you can find these settings in the azure portal
const char* SCOPE_ID = "<ENTER SCOPE ID HERE>";
const char* DEVICE_ID = "<ENTER DEVICE ID HERE>";
const char* DEVICE_KEY = "<ENTER DEVICE primary/secondary KEY HERE>";
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
De data wordt in json formaat doorgezonden. In onderstaande voorbeeld worden de devicenaam, de tijd en 2 random waarde doorgezonden (temp en hum).
String json = "{\"device\":\"" + (String)prefixedMac +
"\",\"timestamp\":\"" + time(nullptr) +
"\",\"temp\":\"" + random(-2500, 8500)/100.0 +
"\",\"hum\":\"" + random(0, 100) +
"\"}";
return json;
2
3
4
5
6
De totale arduino code:
#include "config.h" //contains all settings needed for network & credentials
#include "src/ntp/ntp.h" //library for timekeeping (needed for httpS)
#include "src/azure/iot.h" //library to access IoT central/hub on Azure
IoT azure; //create class azure of type IoT
#if defined(ARDUINO_ARCH_ESP8266)
#include <ESP8266WiFi.h> //needed for MAC
#elif defined(ARDUINO_ARCH_ESP32)
#include <WiFi.h> //needed for MAC
#endif
#if defined USE_LAN && USE_LAN == true
#include <SPI.h>
#include "src/eth/ethernet.h" //adapted version because @2.0.0 from Arduino give errors and @1.0.4 included in ESP is too old
#endif
//create enum for wired network steps
typedef enum{
NOT_YET_INITIALIZED, //no setup has been done
INITIALIZED_OK, //found Wizz hardware
INITIALIZED_NOK, //did not found Wizz hardware
CABLE_DISCONNECTED, //not connected to transport medium
DHCP //received IP from DHCP
} NETWORK_STATUS;
/*Next subroutine will create a char array
--> prefix can be given, e.g. IoT
--> prefix will be seperated from MAC with a dash
--> last 6 hex values of MAC of device will be added to the array
*/
void ssidPrefixMac(char* buff, const char* prefix) {
uint8_t macAddr[6];
WiFi.softAPmacAddress(macAddr); //get the MAC
for (uint8_t i = 0; i < strlen(prefix); i++) { //add the prefix to the array
buff[i] = prefix[i];
}
buff[strlen(prefix)] = '-'; //add a dash as seperator sign
for (uint8_t i = 0; i < 3; i++) { //add the last 3 bytes of the MAC
sprintf(&buff[strlen(prefix) + 1 + i * 2], "%02X", macAddr[i + 3]);
}
buff[strlen(prefix) + 7] = '\0'; //null terminated array
}
/*Next subroutine will setup the network, depending on user needs
--> WiFi can be used on ESP8266 or ESP32
--> Wired network can be used on ESP8266 (through Wizznet SPI)
--> Wired network will not work on ESP32, this is not (yet) implemented
(different libraries need to be used, because ESP32 does have a MAC)
*/
void setupNetwork() {
#if defined USE_LAN && USE_LAN == true //check if we want WiFi or Wired network connection
NETWORK_STATUS wiredStatus = NOT_YET_INITIALIZED;
Serial.println("ETH:\tSetup");
//SPI.pins(6, 7, 8, 0); //switch to flash SPI0 (extended mode)
Ethernet.init(ETH_CS); //set CS pin --> see config.h
uint8_t macAddr[6];
WiFi.softAPmacAddress(macAddr); //will use MAC of WiFi for Wired
//Try to get IP from DHCP
//If failed, check why?
while (!Ethernet.begin(macAddr, 1000, 1000)) { //begin DHCP request with timeout of 1 second --> blocking code!
if (wiredStatus==NOT_YET_INITIALIZED) {
switch (Ethernet.hardwareStatus()) {
case 0: Serial.println("\tNo hardware detected.");
wiredStatus = INITIALIZED_NOK; //no hardware found
break;
case 1: Serial.println("\tWizNet W5100 detected.");
wiredStatus = INITIALIZED_OK; //init passed
break;
case 2: Serial.println("\tWizNet W5200 detected.");
wiredStatus = INITIALIZED_OK; //init passed
break;
case 3: Serial.println("\tWizNet W5500 detected.");
wiredStatus = INITIALIZED_OK; //init passed
break;
default:Serial.println("\tUNKNOWN hardware");
wiredStatus = INITIALIZED_NOK; //no usefull hardware found
}
}
if (wiredStatus == INITIALIZED_OK) {
//once initialized, check link status
if (Ethernet.linkStatus() == LinkOFF) {
//first time here, print the problem
Serial.write("\tEthernet cable is not connected.\n");
wiredStatus = CABLE_DISCONNECTED;
}
} else {
while (true) {
delay(1); // do nothing, no point running with none or unknown hardware
}
}
}
//passed DHCP init, so IP received
wiredStatus = DHCP;
Serial.print("\n\tGot wired IP: ");
Serial.println(Ethernet.localIP());
#else
//use WiFi
Serial.println("WIFI:\tsetup");
WiFi.mode(WIFI_STA);
//create hostname for device, based on prefix and mac
char prefixedMac[3 + 1 + 6 + 1]; //prefix of 3 characters, 1 dash, 6 hex values of MAC and 1 terminating character
ssidPrefixMac(&prefixedMac[0], "IoT");
#if defined(ARDUINO_ARCH_ESP8266)
WiFi.hostname(prefixedMac);
#elif defined(ARDUINO_ARCH_ESP32)
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE); // Workaround: makes setHostname() work
WiFi.setHostname(prefixedMac);
#endif
Serial.print("\tconnecting ");
WiFi.begin(WIFI_SSID, WIFI_PSK); //set credentials in config.h
while (WiFi.status() != WL_CONNECTED) {
delay(250);
Serial.print(".");
}
Serial.print("\n\tGot wireless IP: ");
Serial.println(WiFi.localIP());
#endif
} // setupNetwork
/*Function that returns data for Azure
User can customize this funtion to his needs
--> this is just for demonstration
--> JSON string with unique device ID (based on MAC) & some random data
*/
String buildPayload() {
char prefixedMac[3 + 1 + 6 + 1]; //prefix of 3 characters, 1 dash, 6 hex values of MAC and 1 terminating character
ssidPrefixMac(&prefixedMac[0], "IoT"); //create unique device name
String json = "{\"device\":\"" + (String)prefixedMac +
"\",\"timestamp\":\"" + time(nullptr) +
"\",\"temp\":\"" + random(-2500, 8500)/100.0 +
"\",\"hum\":\"" + random(0, 100) +
"\"}";
return json;
} // buildPayload
void setup() {
Serial.begin(115200); //debug the needed things on serial port
while (!Serial); //is this needed?
Serial.println("\r\nPWR:\tBooting ESP");
//provide noise to the random generator --> can be left out
#if defined(ARDUINO_ARCH_ESP8266)
randomSeed(analogRead(A0));
#elif defined(ARDUINO_ARCH_ESP32)
randomSeed(analogRead(36));
#endif
//set a digital output to demonstrate C2D (Cloud To Device)
pinMode(DBG_PIN, OUTPUT);
digitalWrite(DBG_PIN, LOW); //change to default state when power on
//setup network depending on needs (wired/wireless)
setupNetwork();
} // setup
//two timeout counters for C2D and D2C
uint32_t lastCheckC2D, lastCheckD2C;
void loop() {
String methodName, payload;
AZURE_REP reply;
#if defined USE_LAN && USE_LAN == true
while(Ethernet.linkStatus() == LinkOFF)
if(wiredStatus==DHCP){
//lost connection
Serial.println("ETH:\tEthernet cable disconnected.");
wiredStatus = CABLE_DISCONNECTED;
}
}
if(wiredStatus==DHCP){
Ethernet.maintain(); //maintain DHCP lease for wired ethernet
}else{
if(wiredStatus==CABLE_DISCONNECTED){
//linked again
Serial.print("\tRequesting IP");
while (!Ethernet.begin(macAddr, 1000, 1000)){
Serial.print(".");
}
//passed DHCP init, so IP received
wiredStatus = DHCP;
Serial.print("\n\tGot wired IP: ");
Serial.println(Ethernet.localIP());
}else{
//can we ever come here?
}
}
#else
//maintain DHCP lease for WiFi?
yield();
//check if wifi is disconnected
if (WiFi.status() != WL_CONNECTED) {
WiFi.reconnect();
}
#endif
if (lastCheckC2D > millis()) { //48-days overflow check of timer
lastCheckC2D = millis();
}
if (lastCheckD2C > millis()) { //48-days overflow check of timer
lastCheckD2C = millis();
}
azure.worker(); //check for problems (SAS renew, ...)
//start with time
if (ntpWorker() == SYNCHRONIZED) {
//we are only able to do communication if time is correct
if (!azure.isConnected()) {
azure.setSasExpiry((uint16_t)SAS_RENEW);
//azure is not connected/initalized
#if defined IOT_CENTRAL && IOT_CENTRAL == true
//use function overloading
azure.initAzure(SCOPE_ID, DEVICE_ID, DEVICE_KEY); //do or retry
#else
//use function overloading
azure.initAzure(primaireVerbindingsreeks); //do or retry
#endif
} else {
//azure is connected, we are able to transmit/receive messages
if ((lastCheckD2C + (IOT_UPDATE * 1000) < millis()) || (lastCheckD2C == 0)) {
Serial.println("MAIN:\tSending message... ");
payload = buildPayload();
reply = azure.send(payload); //Send to azure
if (reply == AOK || reply == OK_EMPTY) {
Serial.println("\ttransmit succeeded");
lastCheckD2C = millis();
} else {
Serial.print("\terror! (");
switch (reply) { //you can create a custom message for every reply you got
case CLOCK_NOT_SET: Serial.print("Clock has not been set.");
break;
case TLS_ERROR: Serial.print("TLS could not be established.");
break;
case DPS_ERROR: Serial.print("DPS has failed.");
break;
case UNAUTH: //Unauthorized
Serial.print("Server failed to authenticate the request.");
break;
case BAD_REQUEST: //bad request
Serial.print("Bad format and/or request.");
break;
case FORBIDDEN: //forbidden
Serial.print("Forbidden.");
break;
case UNKNOWN: Serial.print("Got an unknown reply.");
break;
default: //none of the above
Serial.printf("%u - huh?", reply);
break;
}
Serial.println(")");
//create 5 seconds wait to prevent overloading the service
if (millis() < ((IOT_UPDATE * 1000) - 5000)) {
delay(5000); //use delay, because unable to make subtraction
} else {
lastCheckD2C = millis() - ((IOT_UPDATE * 1000) + 5000);
}
}
}
//once every POLL_TIME
if (lastCheckC2D + (POLL_TIME * 1000) < millis()) {
Serial.println("MAIN:\tChecking for new message... ");
reply = azure.receive(methodName, payload);
if (reply == AOK) { //there was data
Serial.printf("MAIN:\tmethodName: %s, payload: %s\r\n", methodName.c_str(), payload.c_str());
if (methodName == "ledOn") {
Serial.println("\tturning LED on");
digitalWrite(DBG_PIN, HIGH); //change to default state when power on
}
if (methodName == "ledOff") {
Serial.println("\tturning LED off");
digitalWrite(DBG_PIN, LOW); //change to default state when power on
}
} else {
Serial.println("\tqueue empty");
}
lastCheckC2D = millis();
}
}//azure.isConnected
}//ntpWorker==SYNCHRONIZED
} // loop
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# Python op raspberry pi
In het voorbeeld sturen we data door met een Raspberry Pi (thing) naar Azure IOT-Central. Eveneens sturen we vanop het dashboard in Azure IoT-Central een actuator aan op de Raspbery Pi.
Bron: Dieter De Preester docent HoWest MCT
# Installatie Azure-iot-device
Installeer op de raspberry pi met pip3 de azure-iot-device lib.
pip3 install azure-iot-device
De gegevens van het apparaat zijn te vinden in IoT-Central onder « Verbinding met »:
Vul het « id-bereik », de « Apparaat-id » en de « primaire sleutel » in de arduino code in.
In het voorbeeldprogramma wordt een random temperatuur en een random luchtvochtig doorgezonden. Indien je andere waardes wil doorsturen in een praktische toepassing pas je volgende stukje code aan:
temp = random.randint(0, 100)
vocht = random.randint(0,100)
data = {"temperatuur" : temp, "luchtvochtigheid" : vocht }
2
3
Een actuator (led verbonden met GPIO pin 18) op de raspberry pi aansturen via het dashboard is mogelijk met volgende code
if method_request.name == "ledOn":
print("Led On")
led.on()
elif method_request.name =="ledOff":
print ("Led Off")
led.off()
else:
print("Received unknown method: " + method_request.name)
2
3
4
5
6
7
8
De totale code:
import asyncio
import os
from azure.iot.device.aio import IoTHubDeviceClient, ProvisioningDeviceClient
from azure.iot.device import MethodResponse
import random
import json
import gpiozero as io
led = io.LED(18)
id_scope = ''
device_id = ''
primary_key = ''
# Declare the device client so it can be used from all the function
device_client = None
# Provisions the device with the Azure device provisioning service or returns
# the connection details if the device is already provisioned
async def register_device():
provisioning_device_client = ProvisioningDeviceClient.create_from_symmetric_key(
provisioning_host='global.azure-devices-provisioning.net',
registration_id=device_id,
id_scope=id_scope,
symmetric_key=primary_key,
)
return await provisioning_device_client.register()
async def property_handler(patch):
print("Patch received:", patch)
async def command_handler(method_request):
print("Message received:", method_request.name)
print("Message payload:", method_request.payload)
# Determine how to respond to the command based on the IoT Hub direct method method name
# which is the same as the IoT Central command name
if method_request.name == "ledOn":
print("Led On")
led.on()
elif method_request.name =="ledOff":
print ("Led Off")
led.off()
else:
print("Received unknown method: " + method_request.name)
# Method calls have to return a response so IoT Central knows it was handled correctly,
# So send a 200 response to show we handled this
payload = {"result": True}
status = 200
# Send the response
method_response = MethodResponse.create_from_method_request(method_request, status, payload)
await device_client.send_method_response(method_response)
# The main async function that runs the app
async def main():
global device_client
# Regsiter the Pi as an IoT device in IoT Central
registration_result = await register_device()
# Build the IoT Hub connection string from the registration details
# IoT Central sits on top of IoT Hub, and the Python SDK only supports IoT Hub,
# So to talk to IoT central the IoT Hub connection string needs to be built from details
# from registering the device with the provisioning service
conn_str='HostName=' + registration_result.registration_state.assigned_hub + \
';DeviceId=' + device_id + \
';SharedAccessKey=' + primary_key
# The client object is used to interact with your Azure IoT Central app via IoT Hub, so create this
# from the connection string
device_client = IoTHubDeviceClient.create_from_connection_string(conn_str)
# Connect the client to IoT Hub
print('Connecting')
await device_client.connect()
print('Connected')
# IoT Central stores properties in the device twin, so read this to see if we have a color
# stored from the last run for the lights. This way when the device starts up it can set the color
# to the last setting
# Set the method request handler on the client to handle IoT Central commands
device_client.on_method_request_received = command_handler
# Handle updates to the color property from IoT Central
device_client.on_twin_desired_properties_patch_received = property_handler
# Define a message loop that keeps the app alive whilst listening for commands
async def main_loop():
while True:
temp = random.randint(0, 100)
vocht = random.randint(0,100)
data = {"temperatuur" : temp, "luchtvochtigheid" : vocht }
await device_client.send_message(json.dumps(data))
print(json.dumps(data))
await asyncio.sleep(5)
# Wait for user to indicate they are done listening for method calls
await main_loop()
# Finally, disconnect
await device_client.disconnect()
# Start the async app running
if __name__ == "__main__":
asyncio.run(main())
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# Node red
In onderstaande voorbeeld wordt:
- data (temperatuur, luchtvochtigheid en luchtdruk) afkomstig van het sense HAT bord verzonden vanuit node red op een rapsberry pi naar IoT Central.
- De leds op het sense HAT bediend vanuit het IoT-Central dashboard
De uitwerking is gebaseerd op volgende document https://pietrobrambati.blog/2020/08/21/sending-commands-from-azure-iot-central-to-raspberry-pi-with-sense-hat-in-node-red/ (opens new window)
# Installatie node-red
Installeer indien nodig node-red op de raspberry pi. https://nodered.org/docs/getting-started/raspberrypi (opens new window)
Open node-red via de browser door het IP-adres en de poort 1880 in de browser in te voeren:
# Importeren van modules in node red
Voeg de bibliotheek voor azure-iot-central toe. Klik op de 3 horizontale strepen
rechtsboven en kies “Settings”
Kies “Palette” in de user Settings en vervolgens het tabblad “Install”
Geef bij “search modules” “Iot-Central” in klik op “install” bij node-red-contrib-azure-iot-central.
Klik terug op Install.
Voeg de bibliotheek voor azure-iot-central toe. Klik op de 3 horizontale strepen
rechtsboven en kies “Settings”
Kies “Palette” in de user Settings en vervolgens het tabblad “Install”
Geef bij “search modules” “Sensehat” in klik op “install” bij node-red-pi-sense-hat.
Klik terug op Install.
Kies import in node-red.
Kopieer de json string van https://github.com/pietrobr/node-red-contrib-azure-iot-central/blob/master/samples/flow SenseHAT commands.json (opens new window)
Plaats de json string in node red en klik op “Import”.
# Azure IoT-Central
Maak een nieuw apparaatsjabloon aan met een zelfgekozen naam.
Voorzie de interface door op “Aangepast model” te klikken.
Vul de weergavenaam en de eenheid in voor:
Temperatuur sensor telemetry
Humidity sensor telemetry
Pressure sensor telemetry
turnLedOn bediening actuator Command
turnLedOff bediening actuator Command
Klik op “Opslaan”
# Visualisatie
Klik op “Het apparaat visualiseren”
Voeg voor de temperatuur, de luchtdruk en de luchtvochtigheid een tegel toe door de grootheid te selecteren onder Telemetrie en vervolgens op “Tegel toevoegen” te klikken.
Voeg de bediening toe door bij Opdrachten de bediening te slecteren.
Wanneer alle tegels toegevoegd zijn klik je op “Opslaan” en vervolgens op “Publiceren”.
Voeg het device toe door op “+Nieuw” te klikken.
Selecteer het aangemaakte sjabloon.
# Instellingen in node-red
Dubbelklik op
Vul de Scope ID, Device ID en Primary Key afkomstig van IoT-Central in.
Pas eventueel de tijd aan om berichten door te sturen.
# Alarmen instellen
Kies “Regels”
Kies om een nieuwe regel in te stellen. Druk op “+Nieuw”
Geef de regel een naam.
Selecteer het appartaatsjabloon
Stel de voorwaarde in
Kies een actie
# Gebruikers
De data kan zichtbaar gemaakt worden voor andere gebruikers. Kies “Beheer” en vervolgens “Gebruikers”
Klik op “+ Nieuwe gebruiker” vul het email adres in van de gebruiker en kies zijn rol.
# License
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.