About this project
Next, I will show you how to integrate our xyz-mIoT shield by itbrainpower.net with ORANGE LiveObjects cloud service. The target goal of the project is to upload the temperature and humidity sensors data, together with xyz-mIoT battery and input voltages into the Live Objects cloud service. At the end, I will show you how to build a graphic reporting tool that retrive the cloud stored data by using NODE RED, REST based web-services and Elasticsearch.
The Arduino code used in this project support the new low power LTE CAT M1 service setup - BG96 version of xyz-mIoT and access to LTE CATM1 service is required. As alternative, if LTE CAT M1 it is not yet available in your area, GPRS data transmission may be used.
Very soon, the this project Arduino code will be updated in order to enable NB IoT socket data upload into Live Objects cloud.
Live Objects is a secure and evolutive Cloud-based platform, developed and operated by Orange, which covers the major functionalities required for any professional IoT project. Read about Live Objects here.

The ARM0 controller used in the xyz-mIoT shield is Microchip/Atmel ATSAMD21G and it is integrated in Arduino Zero compatible design.
The xyz-mIoT shield may have up to 5 integrated sensors, as:
- THS (temperature and humidity sensors) - HDC2010,
- tVOC & eCO2 (air quality sensor - CO2 + total volatile organic compounds as CO2 equivalent) - CCS811,
- HALL (magnetic sensor) - DRV5032 or IR (infrared sensor) KP-2012P3C,
- secondary IR (infrared sensor) - KP-2012P3C,
- TILT (movement/vibration sensor) or REED (magnetic sensor) - SW200D.
More about xyz-mIoT shield sensors and modems variants check the xyz-mIoT shield datasheet.
References used for this project
-
Live Objects - Swagger API, Data BULK Injection, User guide ....
-
NODE RED HTTP guidelines
-
Elasticsearch reference
-
xyz-mIoT shield block schematics
-
xyz-mIoT shield pinout, port mapping and LEDs
Required time*
- Hardware side: around 5 minutes.
- Live Objects account and device setup: around 10 minutes.
- Arduino classes install: 5-10 minutes.
- Arduino software side: 5 minutes.
- NODE RED install and setup: around 5 minutes.
Bill of materials
- xyz-mIoT shield equipped with Quectel BG96 modem and HDC2010 sensor, PN: XYZMIOT209#BG96-UFL-1100000
- nano-size [4FF] LTE CATM1 or 2G SIM card [having data plan enabled]. We used LTE CAT M1 SIM provided by Orange Romania.
- small LiPo battery
- GSM embedded antenna with uFL [check xyz-mIoT accesories] or,
- GSM antenna with SMA plus u.FL to SMA pigtail [check xyz-mIoT accesories]
Step1 [hardware, soldering and wires]:
Follow the very same hardware directives as described in steps 1 and 2 of this project.
Step 2 [Arduino software download and install, preliminary settings]
a. Download the "xyz-mIoT shields Arduino class", then download the last version of classes: "IoT REST [transparent socket] support for xyz-mIoT shields" and "xyz-mIoT shields SENSORS support Arduino class" from here.
b. Install the classes. Uncompress the archives and install the classes - in a nutshell:
- copy the "xyz-mIoT shields Arduino class" files in Arduino local hardware folder (mine is: "C:\Users\dragos\Documents\Arduino\hardware"), then
- copy the the support classes folders into your Arduino local user folder [mine is: "C:\Users\dragos\Documents\Arduino\libraries"] and
- restart Arduino environment.
More detail about manual library installing, read Arduino library manual installation.
c. Make a folder named "xyz_mIoT_REST_OrangeLiveObjects_THS".
d. Copy the code bellow, paste it one new file and save the file as "xyz_mIoT_REST_OrangeLiveObjects_THS.ino" in the folder created in previous step. Alternate, you can
download from here (right click & save as):
xyz-mIoT shield upload sensor data to Live Objects - Arduino main code
xyz_mIoT_REST_OrangeLiveObjects_THS.ino
1 | /* |
2 | xyz_mIoT_REST_LiveObjects_THS.ino v 0.1/20181102 - upload data [json packed] via REST to Orange LiveObjects cloud service |
3 | COPYRIGHT (c) 2015-2018 Dragos Iosub / R&D Software Solutions srl |
4 | |
5 | You are legaly entitled to use this SOFTWARE ONLY IN CONJUNCTION WITH xyz-mIoT by itbrainpower.net DEVICE USAGE. Modifications, derivates |
6 | and redistribution of this software must include unmodified this COPYRIGHT NOTICE. You can redistribute this SOFTWARE and/or modify it under |
7 | the terms of this COPYRIGHT NOTICE. Any other usage may be permited only after written notice of Dragos Iosub / R&D Software Solutions srl. |
8 | |
9 | This SOFTWARE is distributed is provide "AS IS" in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty |
10 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
11 | |
12 | Dragos Iosub, Bucharest 2018. |
13 | https://itbrainpower.net |
14 | */ |
15 | /*IMPORTANT NOTICE 0: |
16 | - goto https://liveobjects.orange-business.com/#/login and create your demo account |
17 | - Add one new device, MQTT type, having "Namespace" set as "xyz-mIoT" and "Id" set as your modem IMEI and name it as you like. After this, |
18 | check the in device detail: |
19 | * Identifier: "namespace: xyz-mIoT / id: YOUR_MODEM_IMEI" |
20 | * Device ID: "urn:lo:nsid:xyz-mIoT:YOUR_MODEM_IMEI" |
21 | - in Configuration\Api Keys, create a new key type "Customized" and having DATA_R and DATA_W rights. Right down the new created API KEY |
22 | - observation: nothing is required to setup the sensors. Those will be stored in cloud based on their JSON key names [set in here, in IoT node code]. |
23 | In this demo, the sensors names transmited into cloud are: "tmp", "hum", "Vbat" and "Vin" [for temperature, humidity, battery voltaga and input voltage] |
24 | |
25 | xyz-mIoT shield PN "XYZMIOT209#BG96-UFL-1100x00" is required for this project. If you use other PN, you must adapt the code. |
26 | |
27 | - Check for hardware setup as described in: https://itbrainpower.net/a-gsm/xyz-mIoT-LiveObjects-LTE-CATM1_howto |
28 | */ |
29 | /*IMPORTANT NOTICE 1: check itbpGSMdefinition.h for line 45-51 and set "__itbpModem__ xyzmIoT" */ |
30 | /*IMPORTANT NOTICE 2: check itbpGSMdefinition.h for line 53&57 and set to "__Qmodule__ CATM1"*/ |
31 | /*IMPORTANT NOTICE 3: in itbpGPRSIPdefinition.h set "SERVER_ADDRESS" with value "liveobjects.orange-business.com" and "SERVER_PORT" with value "443"- LiveObjects server address and port*/ |
32 | /*IMPORTANT NOTICE 4: in itbpGSMdefinition.h "MAX_BUFFER_SIZE", "itbpGSMClassDebug" and "atDebug" may help you....*/ |
33 | |
34 | #include <String.h> |
35 | |
36 | String message = ""; |
37 | |
38 | #define DEVICE_NAMESPACE "xyz-mIoT" //LiveObjects Device namespace, replace it with yours [Eg.: namespace: xyz-mIoT / id: YOUR_DEVICE_IMEI ] |
39 | #define xAPIKEY "e4e89323aa594c9c9c87f6xyxyxyxyxy" //LiveObjects Api Key, replace it with your Key ==> HINT: in order to able to upload data, the key must have at least DATA_W rights |
40 | |
41 | |
42 | int samplingPeriod = 60; //interval in seconds between transmissions... keep it > 20 sec |
43 | |
44 | float Vbat = 0, Vraw = 0; |
45 | |
46 | char IMEI [16]; |
47 | |
48 | /*THS start*/ |
49 | double temperature = 0; |
50 | double humidity = 0; |
51 | /*THS end*/ |
52 | |
53 | #include <itbpGSMClass_v0_6_02.h> |
54 | |
55 | /*THS start*/ |
56 | #include <HDC2010.h> //I2C THS HDC2010 from Texas [option for xyz-mIoT] |
57 | HDC2010 sensor(I2C_HDC2010_ADDRESS); |
58 | /*THS end*/ |
59 | |
60 | |
61 | #define DEBUG(x) {__itbpDebugPort__.print(millis());__itbpDebugPort__.print(" - ");__itbpDebugPort__.println(x);} |
62 | #define DEBUGP(x,y) {__itbpDebugPort__.print(millis());__itbpDebugPort__.print(" - ");__itbpDebugPort__.print(x);__itbpDebugPort__.println(y);} |
63 | |
64 | #if defined (__AVR_ATmega328P__) |
65 | int freeRam () { |
66 | extern int __heap_start, *__brkval; |
67 | int v; |
68 | return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); |
69 | } |
70 | #endif |
71 | |
72 | |
73 | //some defines for IoT - CLOUD chat interaction |
74 | #define PostSensorSuccess "HTTP/1.1 202" //used to verify in cloud response if data upload was succeed |
75 | #define GetActuatorSuccess "HTTP/1.1 200" //used to verify in cloud response if data upload was succeed |
76 | |
77 | #define POSTSENSOR 0 //look for {"request_status":1} |
78 | #define GETACTUATOR 1 //SERVER SAY: '[{"server_timestamp":"2016-09-10T11:50:21.189Z","timestamp":1473508221.147962,"timestamp_iso":"2016-09-10T11:50:21.147Z","state":25,"meta":{"protocol":"http"},"protocol":"http"}]' |
79 | #define POSTACTUATOR 2 //look for {"request_status":1} |
80 | |
81 | //#define ActuatorTag "TBD" //used for test success and for Actuator value extraction |
82 | //#define ActuatorNextTag "TBD" //the actuator value is placed bw: ActuatorTag and ActuatorNextTag |
83 | |
84 | //int c_actuator; // global actuator value |
85 | |
86 | |
87 | unsigned long startTime = 0; |
88 | |
89 | bool ledState = 0; |
90 | |
91 | |
92 | // initialize the library instance |
93 | aGsmClient client; |
94 | |
95 | |
96 | |
97 | void readVoltages(){ |
98 | //read voltage |
99 | Vbat=analogRead(VbatPin)*0.0065344650650891/*0.0063636363636364*/; //8bit read, value in volts |
100 | Vraw=analogRead(VrawPin)*0.0102016129032258; //8bit read, value in volts |
101 | __itbpDebugPort__.print(F("Vbat: ")); |
102 | __itbpDebugPort__.println(Vbat); |
103 | __itbpDebugPort__.print(F("Vraw: ")); |
104 | __itbpDebugPort__.println(Vraw); |
105 | } |
106 | |
107 | /*get sensor data and pack data in JSON for LiveObjects*/ |
108 | void buildJsonPayload(){ // |
109 | |
110 | readVoltages(); |
111 | |
112 | /*THS start*/ |
113 | sensor.triggerMeasurement(); |
114 | delay(100); |
115 | temperature = /*100**/(double)sensor.readTemp(); |
116 | humidity = /*100**/(double)sensor.readHumidity(); |
117 | DEBUGP(F("Temp: "),temperature); |
118 | DEBUGP(F("Hum%: "),humidity); |
119 | /*THS stop*/ |
120 | |
121 | //via bulk DATA |
122 | //[{"streamId": "xyz-mIoT:YOUR_DEVICE_IMEI", "value": {"Vbat": 4.22,"Vin": 5.05}}] |
123 | message=""; |
124 | message += "[{"; |
125 | message += "\"streamId\":\"" + String(DEVICE_NAMESPACE) + IMEI + "\","; //Device ID |
126 | //sensors values next |
127 | message += "\"value\":{"; |
128 | /*THS start*/ |
129 | message += "\"tmp\":"+ String(temperature); //temperature sensor |
130 | message += ",";//separator |
131 | message += "\"hum\":"+ String(humidity); //humidity sensor |
132 | message += ",";//separator |
133 | /*THS stop*/ |
134 | message += "\"Vin\":"+ String(Vraw); //first voltage monitor |
135 | message += ",";//separator |
136 | message += "\"Vbat\":"+ String(Vbat); //second voltage monitor |
137 | message += "}"; |
138 | message += "}]"; |
139 | |
140 | /* |
141 | //or via streams |
142 | //{"value": {"Vbat": 4.24,"Vin": 5.09}} |
143 | message=""; |
144 | |
145 | message += "{"; |
146 | message += "\"value\":{"; |
147 | message += "\"tmp\":"+ String(temperature); //temperature sensor |
148 | message += ",";//separator |
149 | message += "\"hum\":"+ String(humidity); //humidity sensor |
150 | message += ",";//separator |
151 | message += "\"Vin\":"+ String(Vraw); //first voltage monitor |
152 | message += ",";//separator |
153 | message += "\"Vbat\":"+ String(Vbat); //second voltage monitor |
154 | message += "}"; |
155 | message += "}"; |
156 | */ |
157 | __itbpDebugPort__.print(F("JSON data: ")); |
158 | __itbpDebugPort__.println(message); |
159 | } |
160 | |
161 | |
162 | /* |
163 | Function to parse the LiveObjects REST server reply |
164 | */ |
165 | int readRestServerResponse(int type, int timeout){ |
166 | char actuator [5]; |
167 | char * pch0; |
168 | char * pch1; |
169 | memset(actuator, 0x00, sizeof(actuator)); |
170 | int i = 0; |
171 | |
172 | unsigned long startTime; |
173 | startTime = millis(); |
174 | delay(10); |
175 | __itbpDebugPort__.println(F("start read...")); |
176 | while(true){ |
177 | client.readline(1000);//16 linii |
178 | __itbpDebugPort__.println(client.resp);//just to see the server response |
179 | |
180 | if (type==POSTSENSOR || type==POSTACTUATOR) ////HTTP/1.1 202 |
181 | { |
182 | if (strstr(client.resp, PostSensorSuccess)) |
183 | { |
184 | __itbpDebugPort__.println(F("found confirmation...")); |
185 | i = 1; |
186 | while (i < 16){ |
187 | client.readline(1000); |
188 | __itbpDebugPort__.println(client.resp);//just to see the server response |
189 | i++; |
190 | } |
191 | return 1; |
192 | } |
193 | } |
194 | |
195 | if (strstr(client.resp, sockDisconnected0)||strstr(client.resp, sockDisconnected1)) |
196 | { |
197 | __itbpDebugPort__.println(F("sk disconnected...")); |
198 | return 0; |
199 | }//sockDisconnected0 |
200 | if(millis() - startTime > (unsigned long)timeout*1000 ) |
201 | { |
202 | __itbpDebugPort__.println(F("mark to...")); |
203 | |
204 | return -1; |
205 | } |
206 | } |
207 | return -2;//never here |
208 | } |
209 | |
210 | |
211 | /* |
212 | * send sensors data |
213 | * via data/bulk |
214 | * curl -X POST -k --header 'Content-Type: application/json' --header 'X-API-KEY: e4e89323aa594c9c9c87f6a97e3a0df1' -d '[{"streamId": "xyz-mIoT:YOUR_DEVICE_IMEI", "value": {"Vbat": 4.22,"Vin": 5.05}}]' 'https://liveobjects.orange-business.com/api/v0/data/bulk' -v |
215 | * |
216 | * success return HTTP/1.1 202 |
217 | * |
218 | */ |
219 | int liveObjetsSendSensors(){//send all sensor data JSON packed |
220 | int res = 0; |
221 | |
222 | __itbpDebugPort__.print(" >>> send data >>> "); |
223 | __itbpDebugPort__.println(message); |
224 | |
225 | client.print(F("POST ")); |
226 | __itbpDebugPort__.print(F("POST ")); |
227 | |
228 | //'https://liveobjects.orange-business.com' |
229 | client.print(F("https://")); |
230 | __itbpDebugPort__.print(F("https://")); |
231 | |
232 | client.print(SERVER_ADDRESS); |
233 | __itbpDebugPort__.print(SERVER_ADDRESS); |
234 | |
235 | //next line for bulk mode |
236 | client.print(F("/api/v0/data/bulk")); |
237 | __itbpDebugPort__.print(F("/api/v0/data/bulk")); |
238 | |
239 | /* |
240 | //next 3 for streams mode api/v0/data/streams/ |
241 | client.print(F("api/v0/data/streams/")); |
242 | client.print(F(DEVICE_NAMESPACE ":")); |
243 | client.print(IMEI); |
244 | __itbpDebugPort__.print(F("api/v0/data/streams/")); |
245 | __itbpDebugPort__.print(F(DEVICE_NAMESPACE ":")); |
246 | __itbpDebugPort__.print(IMEI); |
247 | */ |
248 | |
249 | client.println(F(" HTTP/1.1")); |
250 | client.println(F("Host: " SERVER_ADDRESS)); |
251 | client.println(F("X-API-KEY: " xAPIKEY)); |
252 | |
253 | __itbpDebugPort__.println(F(" HTTP/1.1")); |
254 | __itbpDebugPort__.println(F("Host: " SERVER_ADDRESS)); |
255 | __itbpDebugPort__.println(F("X-API-KEY: " xAPIKEY)); |
256 | |
257 | if(samplingPeriod <= 30){ |
258 | client.println(F("Connection: keep-alive")); |
259 | __itbpDebugPort__.println(F("Connection: keep-alive")); |
260 | }else{ |
261 | client.println(F("Connection: close")); |
262 | __itbpDebugPort__.println(F("Connection: close")); |
263 | } |
264 | |
265 | |
266 | client.println(F("Content-Type: application/json")); |
267 | client.print (F("Content-Length: ")); |
268 | client.println(message.length()); |
269 | |
270 | __itbpDebugPort__.println(F("Content-Type: application/json")); |
271 | __itbpDebugPort__.print (F("Content-Length: ")); |
272 | __itbpDebugPort__.println(message.length()); |
273 | |
274 | client.println(); |
275 | __itbpDebugPort__.println(); |
276 | |
277 | client.println(message); |
278 | __itbpDebugPort__.println(message); |
279 | |
280 | client.println(); |
281 | __itbpDebugPort__.println(); |
282 | |
283 | res = readRestServerResponse(POSTSENSOR, 20); |
284 | if(samplingPeriod > 30) client.stop(); |
285 | return res; |
286 | |
287 | } |
288 | |
289 | |
290 | void setup(){ |
291 | #if (__itbpModem__ == xyzmIoT) |
292 | delay(10000); |
293 | #endif |
294 | |
295 | __itbpDebugPort__.begin(115200); //start debug port connection |
296 | //configure led |
297 | pinMode(LED_BUILTIN, OUTPUT); //embedded LED status indicator |
298 | digitalWrite(LED_BUILTIN, LOW); |
299 | ledState = 0; |
300 | |
301 | /*THS start*/ |
302 | sensor.begin(); //I2C THS sensor init |
303 | sensor.reset(); //reset H2010 |
304 | |
305 | //sensor.setTemperatureOffset(0b11000000); //-10.32 |
306 | sensor.setTemperatureOffset(0b11010111); //-6.64 |
307 | |
308 | // Set up the comfort zone |
309 | sensor.setHighTemp(28); // High temperature of 28C |
310 | sensor.setLowTemp(22); // Low temperature of 22C |
311 | sensor.setHighHumidity(55); // High humidity of 55% |
312 | sensor.setLowHumidity(40); // Low humidity of 40% |
313 | |
314 | // Configure interrupt pin |
315 | /* |
316 | sensor.enableInterrupt(); // Enable the Interrupt/DRDY pin |
317 | sensor.enableThresholdInterrupt(); // Enable Interrupt triggering based on comfort zone |
318 | sensor.setInterruptPolarity(ACTIVE_HIGH); // Set Interrupt pin to Active High |
319 | sensor.setInterruptMode(COMPARATOR_MODE); // Set Interrupt to return to inactive state when in bounds |
320 | */ |
321 | // Configure Measurements |
322 | sensor.setMeasurementMode(TEMP_AND_HUMID); // Set measurements to temperature and humidity |
323 | //sensor.setRate(ONE_HZ); // Set measurement frequency to 1 Hz |
324 | sensor.setRate(MANUAL); // Set measurement frequency to 1 Hz |
325 | sensor.setTempRes(FOURTEEN_BIT); |
326 | sensor.setHumidRes(FOURTEEN_BIT); |
327 | |
328 | /* |
329 | // Configure Measurements |
330 | //sensor.setRate(ONE_HZ); // Set measurement frequency to 1 Hz |
331 | // Configure THS Interrupt Pin |
332 | pinMode(HDC2010_INTERRUPT_PIN, INPUT); // HDC2010_INTERRUPT_PIN will be the interrupt input |
333 | attachInterrupt(digitalPinToInterrupt(HDC2010_INTERRUPT_PIN), alertTHS, CHANGE); //ISR "alertTHS" will be triggered by changes in ALERTTHSPIN |
334 | // Configure HALL Interrupt Pin |
335 | pinMode(HALL_IRQ_PIN, INPUT_PULLUP); // HALL_IRQ_PIN will be the interrupt input |
336 | attachInterrupt(digitalPinToInterrupt(HALL_IRQ_PIN), alertHALL, CHANGE); //ISR "alertHALL" will be triggered by changes in ALERTHALLPIN |
337 | |
338 | */ |
339 | //begin measuring |
340 | sensor.triggerMeasurement(); |
341 | temperature = /*100**/(double)sensor.readTemp(); |
342 | humidity = /*100**/(double)sensor.readHumidity(); |
343 | /*THS end*/ |
344 | |
345 | |
346 | #if defined (__AVR_ATmega328P__) |
347 | DEBUGP(F("Free RAM: "), freeRam()); |
348 | #endif |
349 | |
350 | __itbpDebugPort__.println(F("Orange LiveObjects cloud REST data upload ...let's rock")); |
351 | __itbpDebugPort__.flush(); |
352 | |
353 | client.begin(); |
354 | |
355 | client.setSSLmode(SSLENABLED); //enable SLL data exchange mode |
356 | |
357 | |
358 | #if __Qmodule__ == CATM1 // for BG96 modems |
359 | //Set bands, scan mode, scan sequence for BG96 only!!! Only just after client.begin()! |
360 | client.setScanSequence(SCANSEQ_CATM1, SCANSEQ_GSM, SCANSEQ_NBIOT ); //left to right priority |
361 | //client.setScanSequence(SCANSEQ_GSM, SCANSEQ_CATM1, SCANSEQ_NBIOT ); |
362 | |
363 | //client.setRegistrationMode(RegisterGSM); //GSM only registration mode allowed |
364 | //client.setRegistrationMode(RegisterCATM1); //CAT-M1 only registration mode allowed |
365 | client.setRegistrationMode(RegisterBOTH); //CAT-M1 or GSM registration mode allowed |
366 | |
367 | client.setBands(GSM_ALL, LTE_CATM1_ALL, LTE_NBIOT_ALL); //definitions in itbpGSMdefinition.h |
368 | #endif |
369 | |
370 | client.enableClockUpdate(1); //update available at next modem boot |
371 | |
372 | client.getIMEI(); //IMEI value in client.resp |
373 | memset(IMEI,0x00, sizeof(IMEI)); |
374 | strcpy(IMEI,client.resp); |
375 | DEBUGP(F("IMEI: "), IMEI); |
376 | |
377 | client.attachGPRS(); |
378 | |
379 | //let's verify the module date&time function |
380 | delay(1000); |
381 | client.getRTC(); |
382 | DEBUGP(F("time and date: "), client.resp); |
383 | |
384 | __itbpDebugPort__.println("connecting..."); |
385 | |
386 | //client.readServerDatagramReply(); |
387 | |
388 | startTime = millis() + (unsigned long)samplingPeriod*1000;//force send on the spot |
389 | } |
390 | |
391 | void loop(){ |
392 | int res=0; |
393 | if (millis() - startTime < (unsigned long)samplingPeriod*1000){//waiting period |
394 | ledState = ! ledState; |
395 | if(ledState != 0) |
396 | digitalWrite(LED_BUILTIN, HIGH); |
397 | else |
398 | digitalWrite(LED_BUILTIN, LOW); |
399 | delay(500); |
400 | |
401 | }else{//start transmission |
402 | startTime = millis(); |
403 | digitalWrite(LED_BUILTIN, HIGH); //show sampling + uploadto cloud |
404 | |
405 | if (client.connected() || client.connect()){ |
406 | __itbpDebugPort__.println(F("send sensor data")); |
407 | |
408 | buildJsonPayload(); |
409 | res = liveObjetsSendSensors(); |
410 | |
411 | if(res == 1){ |
412 | __itbpDebugPort__.println(F("data was sent")); |
413 | //if(samplingPeriod > 30) |
414 | // client.stop(); |
415 | }else{//send datagram command report ERROR |
416 | __itbpDebugPort__.println(F("connectivity error...")); |
417 | } |
418 | }//end connect() |
419 | }//end tranmission |
420 | |
421 | } |
422 |
2020.06.10 update, important! ==> Our pal, Bogdan Calin, doing tests with last version of LiveObjects warned us about some changes in cloud authentication format. In order to be able to upload data to LiveObject cloud, just set #define DEVICE_NAMESPACE "" in above code, line 38. Thank you for your feedback, Bogdan!
e. Make some settings in some files contained inside "IoT REST [transparent socket] support for xyz-mIoT shields" class:
- in "itbpGPRSIPdefinition.h" set the APN value, using the APN value of your GSM provider (Eg: NET for RO Orange)
- in "itbpGPRSIPdefinition.h" set the SERVER_ADDRESS and the SERVER_PORT for LiveObjects cloud
#define SERVER_ADDRESS "liveobjects.orange-business.com"
#define SERVER_PORT "443"
- in "itbpGSMdefinition.h" comment default option for "__itbpModem__" and choose (delete comment sign) option
"#define __itbpModem__ xyzmIoT" (line 51)
- in "itbpGSMdefinition.h" choose the modem type for your xyz-mIoT shield:
"#define __Qmodem__ CATM1" (line 53)
Step3 [LiveObjects - define new IoT device node and x-API-KEY.]
Access https://liveobjects.orange-business.com/#/login and create a new demo account.
Create new IoT device node. Click on "Devices" tab, then click on "Add device" button. Set the "Namespace" as "xyz-mIoT" and "Id" using the IMEI of your xyz-mIoT device [the IMEI is written on top of the BG 96 module].
Create your API KEY Click on "Configuration" tab, then on "Api keys" menu and then select "+Add" button.
Select "Customized" key type and set "DATA_R" and "DATA_W" rights. Fill other mandatory info.
After clicking "Create", a new popup will be shown. Copy the new generated API KEY from the popup at keep it for furter usage.
Step4 [Arduino - set x-API-KEY, compile and upload IOT code]
Open in Arduino[arduino.cc v >= 1.8.5 required] the xyz_mIoT_REST_OrangeLiveObjects_THS.ino project code.
a. Set the x-API-KEY value with the one retained in previous step [created in the LiveObjects]. The IMEI value required in data upload to the cloud automatically provided.
HINT: You may like to set the network scan sequence, set/restrict the LTE and GSM bands used or to select the network registration mode as "RegisterGSM", "RegisterBOTH" or as "RegisterCATM1" (to be able to access LTE CAT-M1 services the mobile network used and the SIM card must support LTE CATM1*) ...see functions calls at lines 359->367, right after client.begin() in function setup().
* we use for tests RO Orange LTE CATM1 enabled SIM.
b. Press twice (fast) the xyz-mIoT shield RESET button [the board will switch into programming mode].
Select "itbrainpower.net xyz-mIoT" board and "itbrainpower.net xyz-mIoT" programming port in Arduino menu.
c. Compile and upload the code.
The xyz-mIoT shield will start to sample the temperature, humidity, Vin and Vbat data (at 1min. rate) and to upload sampled values to your LiveObjects cloud account.
In order to visualize the debug output use the Arduino Serial Monitor or other terminal by selecting the debug port with following settings: 115200bps, 8N, 1.
Logged data can be listed in LiveObject - just access "DATA" section of your account.
Step5 [NODE RED - build / set up a graphic visualizing tool for LiveObjects data]
Download NODE RED from here:https://nodejs.org/en/ and install the package.
Having administrator rights, run cmd.exe. In command prompt window, tyoe following commands:
npm install -g --unsafe-perm node-red
npm install node-red-dashboard
You may start NODE RED typing following command:
node-red
HINT: At first NODE RED run, allow NODE RED connections on localhost in your firewall, but be careful about security of your computer.
The NODE RED admin interface will be available at: http://localhost:1880/ and user interface at: http://localhost:1880/ui/
Copy the NODE RED flow bellow:
1 | [ |
2 | { |
3 | "id": "cb770acb.c93ed8", |
4 | "type": "tab", |
5 | "label": "Flow 3", |
6 | "disabled": false, |
7 | "info": "" |
8 | }, |
9 | { |
10 | "id": "e460e766.b33ed", |
11 | "type": "ui_base", |
12 | "theme": { |
13 | "name": "theme-light", |
14 | "lightTheme": { |
15 | "default": "#0094CE", |
16 | "baseColor": "#0094CE", |
17 | "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", |
18 | "edited": true, |
19 | "reset": false |
20 | }, |
21 | "darkTheme": { |
22 | "default": "#097479", |
23 | "baseColor": "#097479", |
24 | "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", |
25 | "edited": true, |
26 | "reset": false |
27 | }, |
28 | "customTheme": { |
29 | "name": "Untitled Theme 1", |
30 | "default": "#4B7930", |
31 | "baseColor": "#4B7930", |
32 | "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" |
33 | }, |
34 | "themeState": { |
35 | "base-color": { |
36 | "default": "#0094CE", |
37 | "value": "#0094CE", |
38 | "edited": false |
39 | }, |
40 | "page-titlebar-backgroundColor": { |
41 | "value": "#0094CE", |
42 | "edited": false |
43 | }, |
44 | "page-backgroundColor": { |
45 | "value": "#fafafa", |
46 | "edited": false |
47 | }, |
48 | "page-sidebar-backgroundColor": { |
49 | "value": "#ffffff", |
50 | "edited": false |
51 | }, |
52 | "group-textColor": { |
53 | "value": "#1bbfff", |
54 | "edited": false |
55 | }, |
56 | "group-borderColor": { |
57 | "value": "#ffffff", |
58 | "edited": false |
59 | }, |
60 | "group-backgroundColor": { |
61 | "value": "#ffffff", |
62 | "edited": false |
63 | }, |
64 | "widget-textColor": { |
65 | "value": "#111111", |
66 | "edited": false |
67 | }, |
68 | "widget-backgroundColor": { |
69 | "value": "#0094ce", |
70 | "edited": false |
71 | }, |
72 | "widget-borderColor": { |
73 | "value": "#ffffff", |
74 | "edited": false |
75 | }, |
76 | "base-font": { |
77 | "value": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" |
78 | } |
79 | }, |
80 | "angularTheme": { |
81 | "primary": "indigo", |
82 | "accents": "blue", |
83 | "warn": "red", |
84 | "background": "grey" |
85 | } |
86 | }, |
87 | "site": { |
88 | "name": "Node-RED Dashboard", |
89 | "hideToolbar": "false", |
90 | "allowSwipe": "true", |
91 | "allowTempTheme": "true", |
92 | "dateFormat": "DD/MM/YYYY", |
93 | "sizes": { |
94 | "sx": 48, |
95 | "sy": 48, |
96 | "gx": 6, |
97 | "gy": 6, |
98 | "cx": 6, |
99 | "cy": 6, |
100 | "px": 0, |
101 | "py": 0 |
102 | } |
103 | } |
104 | }, |
105 | { |
106 | "id": "d04f9276.cf294", |
107 | "type": "tls-config", |
108 | "z": "", |
109 | "name": "", |
110 | "cert": "", |
111 | "key": "", |
112 | "ca": "", |
113 | "certname": "", |
114 | "keyname": "", |
115 | "caname": "", |
116 | "servername": "", |
117 | "verifyservercert": false |
118 | }, |
119 | { |
120 | "id": "c3ce6859.25688", |
121 | "type": "ui_tab", |
122 | "z": "", |
123 | "name": "Home-test", |
124 | "icon": "dashboard" |
125 | }, |
126 | { |
127 | "id": "258bd191.04d6ce", |
128 | "type": "ui_group", |
129 | "z": "cb770acb.c93ed8", |
130 | "name": "mine", |
131 | "tab": "c3ce6859.25688", |
132 | "disp": true, |
133 | "width": "6", |
134 | "collapse": true |
135 | }, |
136 | { |
137 | "id": "36053808.0e202", |
138 | "type": "ui_group", |
139 | "z": "", |
140 | "name": "Tensiuni", |
141 | "tab": "9543e1de.9e12c8", |
142 | "disp": true, |
143 | "width": "6", |
144 | "collapse": true |
145 | }, |
146 | { |
147 | "id": "9543e1de.9e12c8", |
148 | "type": "ui_tab", |
149 | "z": "", |
150 | "name": "gauges", |
151 | "icon": "dashboard" |
152 | }, |
153 | { |
154 | "id": "c882381b.af42b", |
155 | "type": "ui_group", |
156 | "z": "", |
157 | "name": "Temp-humid", |
158 | "tab": "9543e1de.9e12c8", |
159 | "disp": true, |
160 | "width": "9", |
161 | "collapse": false |
162 | }, |
163 | { |
164 | "id": "b83b0d93.6054a", |
165 | "type": "http request", |
166 | "z": "cb770acb.c93ed8", |
167 | "name": "", |
168 | "method": "POST", |
169 | "ret": "txt", |
170 | "url": "https://liveobjects.orange-business.com/api/v0/data/search/hits", |
171 | "tls": "d04f9276.cf294", |
172 | "x": 430, |
173 | "y": 40, |
174 | "wires": [ |
175 | [ |
176 | "c0805c2f.df0e4" |
177 | ] |
178 | ], |
179 | "outputLabels": [ |
180 | "out" |
181 | ] |
182 | }, |
183 | { |
184 | "id": "481bd64f.946bd", |
185 | "type": "json", |
186 | "z": "cb770acb.c93ed8", |
187 | "name": "", |
188 | "property": "payload", |
189 | "action": "", |
190 | "pretty": true, |
191 | "x": 290, |
192 | "y": 200, |
193 | "wires": [ |
194 | [ |
195 | "c162ed0c.287d28", |
196 | "b0ecc0f.43c0f4", |
197 | "f48a75b1.235448", |
198 | "9252f8ff.3db28", |
199 | "682b38c1.07ca", |
200 | "ac7f0e7c.0185f8" |
201 | ] |
202 | ] |
203 | }, |
204 | { |
205 | "id": "5a56f10a.bea118", |
206 | "type": "inject", |
207 | "z": "cb770acb.c93ed8", |
208 | "name": "", |
209 | "topic": "", |
210 | "payload": "", |
211 | "payloadType": "date", |
212 | "repeat": "15", |
213 | "crontab": "", |
214 | "once": true, |
215 | "onceDelay": "3", |
216 | "x": 110, |
217 | "y": 40, |
218 | "wires": [ |
219 | [ |
220 | "c2a298f7.fd6db" |
221 | ] |
222 | ] |
223 | }, |
224 | { |
225 | "id": "c2a298f7.fd6db", |
226 | "type": "function", |
227 | "z": "cb770acb.c93ed8", |
228 | "name": "set headers", |
229 | "func": "msg.payload = {\"size\" : 1, \"sort\" : [{ \"timestamp\" : {\"order\" : \"desc\"}}], \"query\" : {\"match\":{\"streamId\": \"xyz-mIoTREPLACE_ME_WITH_YOUR_IMEI\"}}};\n\nmsg.headers = {};\nmsg.headers['Accept'] = 'application/json';\nmsg.headers['X-API-KEY'] = 'REPLACE_ME_WITH_YOUR_X_API_KEY';\nreturn msg;", |
230 | "outputs": 1, |
231 | "noerr": 0, |
232 | "x": 270, |
233 | "y": 40, |
234 | "wires": [ |
235 | [ |
236 | "b83b0d93.6054a" |
237 | ] |
238 | ] |
239 | }, |
240 | { |
241 | "id": "c162ed0c.287d28", |
242 | "type": "ui_text", |
243 | "z": "cb770acb.c93ed8", |
244 | "group": "36053808.0e202", |
245 | "order": 3, |
246 | "width": 0, |
247 | "height": 0, |
248 | "name": "", |
249 | "label": "data", |
250 | "format": "{{msg.payload.created}}", |
251 | "layout": "row-left", |
252 | "x": 610, |
253 | "y": 180, |
254 | "wires": [] |
255 | }, |
256 | { |
257 | "id": "b0ecc0f.43c0f4", |
258 | "type": "ui_gauge", |
259 | "z": "cb770acb.c93ed8", |
260 | "name": "", |
261 | "group": "36053808.0e202", |
262 | "order": 1, |
263 | "width": 0, |
264 | "height": 0, |
265 | "gtype": "donut", |
266 | "title": "Vbat", |
267 | "label": "V", |
268 | "format": "{{payload.value.Vbat}}", |
269 | "min": 0, |
270 | "max": "8", |
271 | "colors": [ |
272 | "#00b500", |
273 | "#e6e600", |
274 | "#ca3838" |
275 | ], |
276 | "seg1": "4.4", |
277 | "seg2": "5.2", |
278 | "x": 610, |
279 | "y": 100, |
280 | "wires": [] |
281 | }, |
282 | { |
283 | "id": "c0805c2f.df0e4", |
284 | "type": "function", |
285 | "z": "cb770acb.c93ed8", |
286 | "name": "extrfromArray", |
287 | "func": "msg.payload = msg.payload.replace(\"[\",\"\");\nmsg.payload = msg.payload.replace(\"]\",\"\");\nreturn msg;", |
288 | "outputs": 1, |
289 | "noerr": 0, |
290 | "x": 140, |
291 | "y": 160, |
292 | "wires": [ |
293 | [ |
294 | "481bd64f.946bd" |
295 | ] |
296 | ] |
297 | }, |
298 | { |
299 | "id": "f48a75b1.235448", |
300 | "type": "ui_gauge", |
301 | "z": "cb770acb.c93ed8", |
302 | "name": "", |
303 | "group": "36053808.0e202", |
304 | "order": 2, |
305 | "width": 0, |
306 | "height": 0, |
307 | "gtype": "gage", |
308 | "title": "Vin", |
309 | "label": "V", |
310 | "format": "{{payload.value.Vin}}", |
311 | "min": 0, |
312 | "max": 10, |
313 | "colors": [ |
314 | "#00b500", |
315 | "#e6e600", |
316 | "#ca3838" |
317 | ], |
318 | "seg1": "", |
319 | "seg2": "", |
320 | "x": 610, |
321 | "y": 140, |
322 | "wires": [] |
323 | }, |
324 | { |
325 | "id": "9252f8ff.3db28", |
326 | "type": "ui_text", |
327 | "z": "cb770acb.c93ed8", |
328 | "group": "258bd191.04d6ce", |
329 | "order": 0, |
330 | "width": 0, |
331 | "height": 0, |
332 | "name": "", |
333 | "label": "", |
334 | "format": "{{msg.payload}}", |
335 | "layout": "row-spread", |
336 | "x": 630, |
337 | "y": 20, |
338 | "wires": [] |
339 | }, |
340 | { |
341 | "id": "4de3ef4c.91e3a", |
342 | "type": "ui_chart", |
343 | "z": "cb770acb.c93ed8", |
344 | "name": "", |
345 | "group": "c882381b.af42b", |
346 | "order": 0, |
347 | "width": 0, |
348 | "height": 0, |
349 | "label": "temperature", |
350 | "chartType": "line", |
351 | "legend": "false", |
352 | "xformat": "HH:mm", |
353 | "interpolate": "linear", |
354 | "nodata": "no data", |
355 | "dot": false, |
356 | "ymin": "", |
357 | "ymax": "", |
358 | "removeOlder": 1, |
359 | "removeOlderPoints": "", |
360 | "removeOlderUnit": "3600", |
361 | "cutout": 0, |
362 | "useOneColor": false, |
363 | "colors": [ |
364 | "#1f77b4", |
365 | "#aec7e8", |
366 | "#ff7f0e", |
367 | "#2ca02c", |
368 | "#98df8a", |
369 | "#d62728", |
370 | "#ff9896", |
371 | "#9467bd", |
372 | "#c5b0d5" |
373 | ], |
374 | "useOldStyle": false, |
375 | "x": 650, |
376 | "y": 260, |
377 | "wires": [ |
378 | [], |
379 | [] |
380 | ] |
381 | }, |
382 | { |
383 | "id": "682b38c1.07ca", |
384 | "type": "function", |
385 | "z": "cb770acb.c93ed8", |
386 | "name": "get temperature", |
387 | "func": "var msgout =\"\";\nmsgout= msg.payload.value.tmp;\nmsg.payload=msgout;\nmsg.topic=\"Temp\"; \nreturn msg;", |
388 | "outputs": 1, |
389 | "noerr": 0, |
390 | "x": 460, |
391 | "y": 260, |
392 | "wires": [ |
393 | [ |
394 | "4de3ef4c.91e3a" |
395 | ] |
396 | ] |
397 | }, |
398 | { |
399 | "id": "ac7f0e7c.0185f8", |
400 | "type": "function", |
401 | "z": "cb770acb.c93ed8", |
402 | "name": "get humidity", |
403 | "func": "var msgout =\"\";\nmsgout= msg.payload.value.hum;\nmsg.payload=msgout;\nmsg.topic=\"Humi\"; \nreturn msg;", |
404 | "outputs": 1, |
405 | "noerr": 0, |
406 | "x": 450, |
407 | "y": 300, |
408 | "wires": [ |
409 | [ |
410 | "1d061750.3406a1" |
411 | ] |
412 | ] |
413 | }, |
414 | { |
415 | "id": "1d061750.3406a1", |
416 | "type": "ui_chart", |
417 | "z": "cb770acb.c93ed8", |
418 | "name": "", |
419 | "group": "c882381b.af42b", |
420 | "order": 0, |
421 | "width": 0, |
422 | "height": 0, |
423 | "label": "humidity", |
424 | "chartType": "line", |
425 | "legend": "false", |
426 | "xformat": "HH:mm:ss", |
427 | "interpolate": "linear", |
428 | "nodata": "no data", |
429 | "dot": false, |
430 | "ymin": "", |
431 | "ymax": "", |
432 | "removeOlder": 1, |
433 | "removeOlderPoints": "", |
434 | "removeOlderUnit": "3600", |
435 | "cutout": 0, |
436 | "useOneColor": false, |
437 | "colors": [ |
438 | "#1f77b4", |
439 | "#aec7e8", |
440 | "#ff7f0e", |
441 | "#2ca02c", |
442 | "#98df8a", |
443 | "#d62728", |
444 | "#ff9896", |
445 | "#9467bd", |
446 | "#c5b0d5" |
447 | ], |
448 | "useOldStyle": false, |
449 | "x": 640, |
450 | "y": 300, |
451 | "wires": [ |
452 | [], |
453 | [] |
454 | ] |
455 | } |
456 | ] |
457 |
Save it as "my_LiveObjects_reporting.json". Alternate, you may download it from here (right click & save as):
NODE RED flow definition - aquire and display xyz-mIoT sensor data from Live Objects cloud
Edit the definition file and update :
REPLACE_ME_WITH_YOUR_IMEI using your modem IMEI value
and
REPLACE_ME_WITH_YOUR_X_API_KEY using your LiveObjects API KEY.
Access the NODE RED admin interface and "MENU\Import" "my_LiveObjects_reporting.json" report definition file. The reporting flow will be displayed in NODE RED admin interface.

Now, the reporting interface is available at http://localhost:1880/ui/.

ENJOY!
TUTORIAL PROVIDED WITHOUT ANY WARRANTY!!! USE IT AT YOUR OWN RISK!!!!
Final thoughts
LiveObjects cloud service is really huge! Whith this demo, we just scratch the surface.Read LiveObjects docs and additional references and start you IoT project.
BTW: NODE RED is based on javascript. A good js reference can be found at: https://www.w3schools.com/js/.