# 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.

figuur

# 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

figuur

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.

figuur

figuur

# 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.

figuur

Vul je telefoonnummer in als verificatie.

figuur

Registreer je.

figuur

Ga akkoord met de voorwaarden.

figuur

# 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".

figuur

Geef in het zoekvenster "Resourcegroep" in.

figuur

Klik op Maken.

figuur

Geef de resourcegroep een zelfgekozen naam en kies de dichtste locatie om de resourcegroep te bewaren.

figuur

Klik vervolgens op "Beoordelen en maken".

figuur

# 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.

figuur

Met het gratis pakket kunnen 30 000 berichten per maand verzonden worden.

figuur

# 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”.

figuur

Geef in het zoekvenster “IoT Central application” in:

figuur

Klik op "Maken".

figuur

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.

figuur

Wanneer alles goed gelukt is krijg je de melding dat de implementatie goed gelukt is.

figuur

Selecteer in de Azure portal de zelf aangemaakte IoT-Central-toepassing.

figuur

Klik op de URL van de aangemaakte toepassing.

figuur

Je komt op de portal van de aangemaakte IoT Central toepassing.

figuur

# 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”.

figuur

Kies als type “IoT-apparaat” en klik op “Volgende: Aanpassen”.

figuur

Voer een zelfgekozen naam in voor het apparaatsjabloon en klik op Volgende: Beoordelen.

figuur

Klik op “Maken”.

figuur

Wanneer het sjabloon aangemaakt is krijg je bevestiging.

figuur

# 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.

figuur

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.

figuur

Klik op “+ Een mogelijkheid toevoegen” om de 2de waarde van de interface toe te voegen.

figuur

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.

figuur

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.

figuur

Klik op “Opslaan” om de interface te bewaren.

# Visuele weergave

Kies onder “Weergaven” voor “Het apparaat visualiseren”.

figuur

We kiezen in het eerste voorbeeld voor een lijndiagram om de telemetrie data weer te geven. (voorbeeld temperatuur, luchtvochtigheid, ...)

figuur

Klik vervolgens op "Tegel toevoegen".

figuur

Klik vervolgens op "Configureren". figuur

Stel de titel in, kies het weergavebereik, het interval en klik vervolgens op "+Mogelijkheid". figuur

Kies uit de Telemetrie de mogelijkheid die grafisch moet weergegeven worden. figuur

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”.

figuur

Klik nogmaals op “Publiceren”.

figuur

# Importeren van een apparaatsjabloon uit een json file

Het is mogelijk een apparaat sjabloon te importeren als json file. Download volgende json file:

https://github.com/KrisWerbrouck1/InnovetAzure-IoT/tree/master/docs/assets/IoT-device1.json (opens new window)

Ga naar “Apparaatsjablonen” in je Central application. Klik bovenaan op “nieuw”.

figuur

Kies voor “IoT-apparaat” Klik vervolgens onderaan op “Volgende: Aanpassen”.

figuur

Geef een naam aan het apparaat (vrij te kiezen) en klik vervolgens op “Volgende: Beoordelen”.

figuur

Klik vervolgens op “Maken”.

figuur

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.

figuur

Via het popup venster selecteren we ons model die beschreven staat als een JSON file.

figuur

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.

figuur

Via het popup venster selecteren we ons model die beschreven staat als een JSON file.

figuur

Als alles goed gaat is ons volledige model meteen klaar.

figuur

# Aanmaken apparaat

Maak vanuit het sjabloon een nieuw apparaat aan. Klik op “+ Nieuw” in Alle apparaten.

figuur

Selecteer het ontworpen Apparaatsjabloon, pas indien nodig de apparaatnaam aan.

figuur

# 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.

figuur

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"
1
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
1

Zorg dat IOT-Central op true staat.

#define IOT_CENTRAL true
1

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
1

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
1

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>";
1
2
3

De gegevens van het apparaat zijn te vinden in IoT-Central onder « Verbinding met »:

figuur

Vul het « id-bereik », de « Apparaat-id » en de « primaire sleutel » in de arduino code in.

figuur

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>";


1
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;
1
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
1
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.

figuur

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
1

figuur

De gegevens van het apparaat zijn te vinden in IoT-Central onder « Verbinding met »:

figuur

Vul het « id-bereik », de « Apparaat-id » en de « primaire sleutel » in de arduino code in.

figuur

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 }
1
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)
1
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())
1
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

figuur figuur figuur

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” figuur

Kies “Palette” in de user Settings en vervolgens het tabblad “Install”

figuur

Geef bij “search modules” “Iot-Central” in klik op “install” bij node-red-contrib-azure-iot-central.

figuur

Klik terug op Install.

figuur

Voeg de bibliotheek voor azure-iot-central toe. Klik op de 3 horizontale strepen rechtsboven en kies “Settings” figuur

Kies “Palette” in de user Settings en vervolgens het tabblad “Install”

figuur

Geef bij “search modules” “Sensehat” in klik op “install” bij node-red-pi-sense-hat.

figuur

Klik terug op Install.

figuur

Kies import in node-red.

figuur

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)

figuur

Plaats de json string in node red en klik op “Import”.

figuur

# Azure IoT-Central

Maak een nieuw apparaatsjabloon aan met een zelfgekozen naam.

figuur

Voorzie de interface door op “Aangepast model” te klikken.

figuur

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

figuur

figuur

Klik op “Opslaan”

figuur

# Visualisatie

Klik op “Het apparaat visualiseren”

figuur

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.

figuur

Voeg de bediening toe door bij Opdrachten de bediening te slecteren.

figuur

Wanneer alle tegels toegevoegd zijn klik je op “Opslaan” en vervolgens op “Publiceren”.

figuur

Voeg het device toe door op “+Nieuw” te klikken.

figuur

Selecteer het aangemaakte sjabloon.

figuur

# Instellingen in node-red

Dubbelklik op

figuur

Vul de Scope ID, Device ID en Primary Key afkomstig van IoT-Central in.

figuur

Pas eventueel de tijd aan om berichten door te sturen.

figuur

# Alarmen instellen

Kies “Regels”

figuur

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

figuur

# Gebruikers

De data kan zichtbaar gemaakt worden voor andere gebruikers. Kies “Beheer” en vervolgens “Gebruikers”

figuur

Klik op “+ Nieuwe gebruiker” vul het email adres in van de gebruiker en kies zijn rol.

figuur

# License

Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

Netlify Status (opens new window)