Cum putem utiliza prognoza meteo oferită de Weather Underground

Așa cum am arătat și în proiectul „Mini stație meteo”, serviciul Weather Undergroud este un serviciu independent (nu este un serviciu al unei țări sau structuri oficiale) de predicție a vremii. Acest serviciu Internet permite realizarea de prognoze locale pe baza informațiilor provenite de la o stații meteo personale (PWS – Personal Wether Station) sau corelând informațiile de la mai multe stații meteo personale aflate într-o anumită zonă.

Dacă în lecția proiectul „Mini Stație Meteo Weather Underground” am arătat cum să construim o mini stație meteo care să raporteze date către Weather Underground, în lecția de față vom arăta cum putem utiliza datele de prognoză calcultate de Weather Underground. Pentru acest lucru este necesar să solicităm o cheie API (API Key) de conectare de la adresa:

2

Furnizarea prognozei meteo este un serviciu comercial (se plătește) dar pentru dezvoltatori (developers) există un plan tarifar gratuit (Stratus Plan) ce permite efectuarea de 500 de interogări pe zi (maxim 10 interogări pe minut) – limitări ce nu afectează sistemul prezentat în această lecție.

3

Odată obținută cheia de conectare la platforma Weather Underground putem scrie aplicații proprii care să prezinte prognoza meteo fără a fi necesară accesarea site-ului Weather Underground sau utilizarea aplicației mobile Weather Underground. Mai multe informații se pot consulta în pagina de suport a platformei unde sunt prezentate exemple pentru mai multe limbaje de programare. Funcțiile API puse la dispoziție de platforma Weatgher Underground se pot accesa și de pe un sistem embedded simplu. Pentru exemplificarea acestui lucru vom utiliza sistemul descris în proiectul „Ceas IoT cu programare OTA” format dintr-o placă NodeMCU și un ecran LCD grafic 84×48 pixeli:

5

Sistemul va păstra funcționalitatea de ceas și senzor de temperatură IoT dar va afișa și prognoza meteo pentru ziua curentă și ziua următoare.

Vom pleca de la programul scris pentru sistemul Ceas IoT. Programul a fost dezvoltat și testat utilizând Arduino IDE 1.8.3 având instalată extensia ESP8266 Community 2.3.0. Pentru accesarea funcțiilor API Weather Underground vom utiliza bibliotecile Json Streaming Parser 1.0.5 și ESP8266 Weather Station 1.3.2. Programul Ceas IoT se va completa cu următoarele declarații inițiale (este necesară completarea cheii API – constanta WUNDERGROUND_API_KEY și, dacă este cazul, modificarea localității pentru care se dorește obținerea prognozei meteo – constantele WUNDERGROUND_ZMW_CODE și WUNDERGR_UND_CITY):

#include <JsonListener.h>

#include “WundergroundConditions.h”

#include “WundergroundForecast.h”

const String  WUNDERGRROUND_API_KEY = “…”;

const boolean IS_METRIC = true;

const boolean USE_PM = false;

const String WUNDERGROUND_ZMW_CODE = “00000.26.WLRBS”;

const String  WUNDERGRROUND_LANGUAGE = “EN”;

const String  WUNDERGR_UND_STATE_OR_COUNTRY = “RO”;

const String  WUNDERGR_UND_CITY = “Bucharest”;

WundergroundConditions wunderground(IS_METRIC);

WundergroundForecast wundergroundf(IS_METRIC);

WGConditions conditions;

WGForecast forecasts[3];

La sfârșitul secțiunii setup() se vor adăuga următoarele două instrucțiuni:

wunderground.updateConditions(&conditions, WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_ZMW_CODE);

wundergroundf.updateForecast(forecasts, 3, WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGR_UND_STATE_OR_COUNTRY, WUNDERGR_UND_CITY);

În cadrul secțiunii loop() se va modifica partea de afișare astfel încât să avem patru stări de afișare (patru ecrane diferite) care se vor succeda la un interval de 15 secunde (fiecare ecran va rămâne afișat timp de 15 secunde): primul ecran va afișa ora și data (la fel ca și în lecția precedentă), al doilea ecran va afișa temperatura interioară și statusul conexiunii IoT (la fel ca și în lecția precedentă), al treilea ecran va afișa prognoza pentru ziua curentă iar al patrulea pentru ziua următoare.

6

7

Din păcate, chiar dacă funcțiile API Weather Underground au implementată traducerea informațiilor în limba română, vom utiliza în program prognoza în limba engleză deoarece setul de caracter a ecranului LCD nu include și diacritice. În capturile anterioare se poate observa că pentru ziua curentă avem cer senin (Clear) și pentru prognoza zilei următoare se preconizează cer noros și câteva picături de ploaie.

if (second()<15) {

       // codul rămâne neschimbat față de proiectul anterior

    }

else if (second()<30) {

      // codul rămâne neschimbat față de proiectul anterior

    }

else if (second()<45) {

      display.drawLine(0,0,83,0,BLACK);

      display.drawLine(0,47,83,47,BLACK);

      display.setTextColor(BLACK);

      display.setTextSize(1);

      display.setCursor(10,2);

      display.print(“Vremea acum”);

      display.drawLine(0,11,83,11,BLACK);

      display.setCursor(30,14);

      display.print(conditions.currentTemp);

      display.print((char)247);

      display.print(“C”);

      display.setCursor(35,22);

      display.print(conditions.humidity);

      display.setCursor(27,30);

      display.print(conditions.pressure);

      display.setCursor((84-(5*conditions.weatherText.length()))/2,38);

      display.print(conditions.weatherText);

      display.display();

    }

    else {

      display.fillScreen(BLACK);

      display.drawLine(0,1,83,1,WHITE);

      display.drawLine(0,46,83,46,WHITE);

      display.setTextColor(WHITE);

      display.setTextSize(1);

      display.setCursor(5,3);

      display.print(“Vremea maine”);

      display.drawLine(0,12,83,12,WHITE);

      display.setCursor(20,14);

      display.print(“Min:”);

      display.print(forecasts[2].forecastLowTemp);

      display.print((char)247);

      display.print(“C”);

      display.setCursor(20,22);

      display.print(“Max:”);

      display.print(forecasts[2].forecastHighTemp);

      display.print((char)247);

      display.print(“C”);

      String temp = forecasts[2].forecastText;

      int prim = temp.indexOf(‘.’);

      display.setCursor((84-(5*prim))/2,30);

      display.print(temp.substring(0,prim));

      display.setCursor(0,38);

     display.print(temp.substring(prim+2, temp.indexOf(‘.’,prim+2)));

      display.display()     }

if (millis() – lastConnectionTime > postingInterval) {

      wunderground.updateConditions(&conditions,

WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_ZMW_CODE);

      wundergroundf.updateForecast(forecasts, 3, WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGR_UND_STATE_OR_COUNTRY,  WUNDERGR_UND_CITY);

      // codul rămâne neschimbat față de proiectul anterior

    }

Prognoza meteo oferită de Weather Underground are o acoperire de zece zile. În cadrul sistemului prezentat nu am utilizat decât ziua curentă și ziua următoare dar programul poate fi ușor modificat pentru afișarea unei prognoze pe mai multe zile. În plus există mai multe facilități ce nu au fost utilizate în exemplul nostru dar pot constitui dezvoltări ulterioare interesante: avertizări de vreme extremă (a se vedea exemplul WundergroundAlertsDemo din cadrul bibliotecii ESP8266 Weather Station) sau elemente de evoluție astronomică (a se vedea exemplul WundergroundAstronomyDemo).

O altă posibilă direcție de îmbunătățire posibilă a sistemului de predicție meteo este utilizarea simbolurilor grafice oferite de Weather Underground pentru o reprezentare sugestivă a evoluției meteo.

8

Un exemplu de proiect ce utilizează aceste simboluri este „ESP8266 Weather Widget”, proiect ce utilizează un ecran grafic OLED de rezoluție mai mare.

Mini Stație Meteo Weather Underground

Serviciul Weather Undergroud este un serviciu independent (nu este un serviciu al unei țări sau strucutură oficială) de predicție a vremii. Acest serviciu Internet permite realizarea de prognoze locale pe baza informațiilor provenite de la o stație meteo personală (PWS – Personal Wether Station) sau corelând informațiile de la mai multe stații meteo personale aflate într-o anumită zonă. Serviciul Weather Underground permite completarea funcționalității unei stații meteo personale cu partea de istoric și predicție a evoluției vremii și oferă o modalitate convenabilă în care utilizatorul poate consulta informațiile furnizate de stația meteo de oriunde prin Internet.

În cadrul proiectului de față vom prezenta realizarea unei mini stații meteo (măsoară doar temperatura, umiditatea și presiunea atmosferică) ce raportează datele măsurate către serviciul Weather Underground. Pentru partea de achiziție vom utiliza un singur senzor capabil să măsoare toți cei trei parametrii: BME280. Bineînțeles, sistemul prezentat poate fi extins și cu alți senzori specifici unei stații meteo: senzori pentru viteza și direcția vântului, senzor pentru cantitatea de precipitații sau senzor pentru indexul radiațiilor ultraviolete.

2

Ca placă de dezvoltare vom utiliza Adafruit HUZZAH ESP8266 breakout ce ne va permite o conectare simplă la Internet prin WiFi la un cost scăzut. Schema de interconectare între placa de dezvoltare și senzorul BME280 este următoarea:

3

Comunicația între senzor și placa de dezvoltare se bazează pe protocolul I2C prin urmare legărutile sunt:

  • Pinul SDA al senzorului se conectează la pinul #4 al plăcii de dezvoltare;
  • Pinul SCL al senzorului se conectează la pinul #5 al plăcii de dezvoltare;
  • Pinii 3.3V și GND ai senzorului se conectează la pinii 3V și GND ai plăcii.

Placa va trimite la un interval de 60 de minute (1 oră) datele achiziționate către serviciul Weather Underground iar între două postări se va afla în mod de consum redus. Din acest motiv există o legătură între pinul #16 și pinul RST al plăcii. Pinul #16 are funcționalitate de Wake ce permite resetarea plăcii pentru a ieși din modul de consum redus. Pentru mai multe informații despre modul de consum redus al circuitului ESP8266 puteți consulta materialul „ESP8266 Deep Sleep with Arduino IDE”.

Pentru alimentarea plăcii puteți un regulator de 3.3V sau una sau mai multe baterii ce furnizează între 4V și 6V (alimentarea se va face în acest caz prin intermediul pinului Vbat). Având în vedere consumul redus al sistemului funcționarea pe baterii se poate face pe perioade lungi de timp.

Pentru programarea plăcii Adafruit HUZZAH ESP8266 este nevoie de un programator FTDI sau de un cablu FTDI. Pentru mai multe informații despre funcționarea plăcii de dezvoltare Adafruit HUZZAH ESP8266 puteți consulta materialul „Adafruit HUZZAH ESP8266 breakout – Adorable bite-sized WiFi microcontroller, at a price you like!”.

Programul a fost dezvoltat și testat utilizând Arduino IDE 1.8.3 având instalate extensia ESP8266 Community 2.3.0 și biblioteca Sparkfun BME280 1.1.0. În cadrul programului trebuie personalizate datele de conectare la rețeaua WiFi (ssid și pass) precum și datele de autentificare la platforma Weather Underground (ID și PASSWORD).

#include <ESP8266WiFi.h>

#include <ESP8266HTTPClient.h>

#include “SparkFunBME280.h”

#include “Wire.h”

BME280 mySensor;

char ssid[] = “…”;

char pass[] = “…”;

WiFiClient client;

void setup() {

  WiFi.begin(ssid, pass);

  while (WiFi.status() != WL_CONNECTED) {

      delay(500);

    }

  mySensor.settings.commInterface = I2C_MODE;

  mySensor.settings.I2CAddress = 0x77;

  mySensor.settings.runMode = 3;

  mySensor.settings.tStandby = 0;

  mySensor.settings.filter = 0;

  mySensor.settings.tempOverSample = 1;

  mySensor.settings.pressOverSample = 1;

  mySensor.settings.humidOverSample = 1;

  mySensor.begin();

  delay(100); }

void loop() {

  float tempC = mySensor.readTempC();

  float humidity = mySensor.readFloatHumidity();

  float pressure = mySensor.readFloatPressure();

  delay(100);

  String url = “http” + “://weatherstation.wunderground.com/”

  url += “weatherstation/updateweatherstation.php?”

  url += “ID=…&PASSWORD=…”

  url += “&dateutc=now&=&action=updateraw&”;

  url += “baromin=” + String(pressure * 29.92 / 101325);

  url += “&tempf=” + String(9/5.0 * tempC + 32.0);

  url += “&humidity=” + String(humidity);

  url += “&dewptf=” + String(9/5.0 * (tempC – (100.0 –

humidity) /5.0) + 32.0);

  HTTPClient http;

  http.begin(url);

  int httpCode = http.GET();

  delay(1000);

  http.end();

  delay(1000);

  ESP.deepSleep(3600L*1000000L);  }

Așa cum am precizat și anterior, serviciul Weather Underground este un serviciu independent de prognoză meteorologică bazată în principal de rețele de stații meteo proprii (în SUA) sau private (PWS – Personal Weather Station, în restul lumii). Platforma online a acestui serviciu permite înregistrarea datelor provenite de la orice PWS și realizarea de prognoze de evoluție a vremii zonale ca o alternativă mult mai rafinată la serviciile naționale de meteorologie. Serviciul este gratuit dar necesită înregistrare. Un utilizator poate înregistra mai multe stații meteo aflate în locații diferite. Imediat după înregistrarea stației meteo personale se poate porni sistemul și datele înregistrate vor apărea imediat în fereastra asociată stației respective.

4

După o perioadă de 24 – 48 de ore, dacă datele raportate sunt valide – se încadrează într-un interval de valori decent de apropiat ca celelalte stații meteo din zonă, stația meteo va apărea și în WunderMap și va putea fi văzută de orice alt utilizator al serviciului. WunderMap poate fi consultată în varianta web sau în varianta de mobil (la fel și prognozele meteorologice furnizate de Weather Underground).

5

Buton Sonerie WiFi

Chiar dacă vechile sonerii (manuale sau cu fir) își au un farmec aparte, totuși, soneriile fără fir au devenit un lucru obișnuit la momentul actual datorită ușurinței de instalare. În cadrul lecției de față vă propunem realizarea unei sonerii formată doar din butonul de afară – avertizarea sonoră specifică va fi înlocuită, grație unei conexiuni WiFi, cu trimiterea unui email sau o avertizare pe telefonul mobil. Utilizând o astfel de sonerie puteți fi anunțat că este cineva la ușă chiar dacă nu sunteți acasă.

Pentru implementare vom utiliza un modul WiFi ESP8266-01S. Acest tip de modul bazat pe circuitul ESP8266 are un cost foarte mic dar este destul de dificil de utilizat spre deosebire de alte plăci de dezvoltare bazate pe același circuit. Modulul este gândit să fie utilizat ca modul WiFi serial pentru alte plăci de dezvoltare, vine preprogramat cu un firmware ce acceptă comenzi de tip AT. În plus, forma conectorului de pe acest modul nu dă posibilitatea de a fi folosit cu un breadboard. În cadrul lecției de față vom programa direct modulul fără a utiliza facilitatea de comunicație serială AT, vom utiliza modulul ca placă de dezvoltare. Pentru interconectarea modulului recomandăm utilizarea unor fire de interconectare de tip mamă-tată pentru a putea face legăturile cu un breadboard. Pentru mai multe informații legate de funcționarea modulului puteți consulta și materialul „ESP8266 WiFi Module for Dummies”.

3

Programarea modulului necesită un modul FTDI de 3.3V și instalarea extensiei ESP8266 Community sub Arduino IDE. Pentru mai multe detalii puteți consulta și materialul „How to Install the ESP8266 Board in Arduino IDE”. Conexiunile dintre modulul ESP8266-01S și programatorul FTDI necesare încărcării programului sunt următoarele:

  • Pinii GND și GPIO0 ai modulului ESP8266 se conectează la pinul GND al programatorului. Pinul GPIO0 este necesar să fie conectat la masă pentru programare.
  • Pinii VCC și CH_PD ai modulului ESP8266 se conectează la pinul VCC (adică 3.3V!!!) al programatorului. Pinul CH_PD se va menține conectat la VCC pe tot parcursul funcționării modulului – este pinul de activare al modulului.

4

  • Pinul RX al modulului ESP8266 se conectează la pinul TX al programatorului.
  • Pinul TX al modulului ESP8266 se conectează la pinul RX al programatorului.

Programul a fost dezvoltat și testat utilizând Arduino IDE 1.8.3 cu extensia ESP8266 Community 2.3.0 instalată. Pentru programare se selectează placa de dezvoltare ”Generic ESP8266 Module”. În cadrul programului trebuie personalizate datele de conectare WiFi (variabilele ssid și pass).

#include <ESP8266WiFi.h>

#include <ESP8266HTTPClient.h>

char ssid[] = “…”;

char pass[] = “…”;

WiFiClient client;

void setup() {

  WiFi.begin(ssid, pass);

  delay(5000);

  if (WiFi.status() != WL_CONNECTED) {

    delay(60000);

    ESP.restart();

  }

  IFTTTpublish();

  ESP.deepSleep(0);

}

void loop() {

}

Programul transmite prin Internet către serviciul IFTTT evenimentul de apăsare a butonului (procedura IFTTTpublish). Serviciul IFTTT permite redirectarea evenimentului către utilizator sub forma unui email, a unui avertizări pe telefonul mobil sau orice altă variantă dorită. În cadrul procedurii trebuie personalizate datele de identificare a evenimentului IFTTT Webhooks (variabilele KEY și EVENT) prin care se fac legătura între sistemul nostru și platforma IFTTT.

void IFTTTpublish() {

  String KEY = “…”;

  String EVENT = “ButonSonerie”;

  HTTPClient http;

  String data = String(“http” + “://maker.ifttt.com/trigger/” + EVENT + “/with/key/”) + KEY ;

  http.begin(data);

  int httpCode = http.GET();

  delay(500);

  http.end();

}

Sistemul nostru va funcționa în regim de consum redus în mod continuu (indus de instrucțiunea ESP.deepSleep(0) din secțiunea setup). Butonul va fi conectat pe pinul de RESET al modulului și va activa sistemul doar pentru a semnaliza evenimentul către platforma IFTTT după care va intra din nou în regim de consum redus. Acest lucru este extrem de util dacă dorim să alimentăm sistemul nostru de la o baterie sau un acumulator. Pentru buton vom utiliza un brick pentru o interconectare mai ușoară. Brick-ul este gândit să tragă linia OUT la VCC dar pinul de RESET al modulului ESP8266 necesită tragerea la masă din acest motiv vom inversa conectarea brick-ului (vom conecta VCC la GND și GND la VCC):

5

Atenție!!! Modulul ESP8266-01S nu are regulator de tensiune integrat. El trebuie alimentat fix la 3.3V. În plus necesită un curent destul de mare, nu poate fi alimentat din programatorul FTDI decât pe perioada programării. Pentru testare recomandăm o sursă de breadboard și pentru funcționarea autonomă un regulator Step-Down de 3.3V (de exemplu) pentru o alimentare de la un acumulator LiPo de 3.7V sau 7.2V.

Utilizarea serviciului IFTTT necesită înregistrare (gratuită). După înregistrare și conectarea la platforma IFTTT vom crea o nouă aplicație (New Applet) în care evenimentul this va fi de tipul Webhooks iar efectul that va fi de tipul Android Device / play a specific song.

6 7 8

Bineînțeles, așa cum am precizat anterior, efectual poate fi modificat după dorință în Email, SMS, postare web etc. sau ce vi se pare potrivit ca avertizare pentru butonul de sonerie. Este adevărat că sistemul propus în această lecție poate servi la fel de bine ca buton de panică, buton de recepție sau buton pentru servitori 😊. În cadrul efectului Play a specific song se va specifica denumirea unei piese aflată pe mobilul care trebuie să aibă aplicație IFTTT instalată. Dacă melodia nu este găsită se va deschide în mod automat pagina Youtube și va fi căutată. Vă recomandăm să încercați ”jinjer words of wisdom”.

Ceas IoT cu programare OTA

Realizarea unui ceas electronic este un proiect simplu dacă utilizăm o placă de dezvoltare programabilă dar asta nu înseamnă că nu există anumite provocări și în acest caz. Una din provocările majore ale implementării unui ceas electronic este acuratețea menținerii orei (asigurarea orei exacte). Acest lucru se realizează de obicei prin utilizarea unui modul RTC dar în cadrul proiectului de față vom utiliza o placă de dezvoltare NodeMCU care prin conectivitatea WiFi de care dispune ne va permite să realizăm o sincronizare de timp în rețea de tip NTP. Astfel nu vom avea nevoie de nici o componentă suplimentară pentru a asigura ora exactă.

Pentru afișare vom utiliza un ecran LCD grafic cu rezoluția de 84×48 pixeli de tip PCD8544 (LCD de Nokia 5110).

2

Funcționalitatea specială propusă în această lecție este adăugarea sistemului unei funcționalități de tip IoT adică raportarea prin Internet a unui parametru de mediu către o platformă IoT. Sistemul va măsura (și afișa) temperatura și o va trimite pentru înregistrare către platforma Robofun IoT. Astfel sistemul nostru nu va avea simpla funcționalitate de ceas/termometru electronic ci și de senzor IoT. Pentru măsurarea temperaturii vom utiliza un senzor digital brick DS18B20. Acest senzor are o acuratețe mare în măsurarea temperaturii, nu necesită decât 3 fire pentru conectarea la placa de dezvoltare și, cel mai interesant, permite de conectarea (pe aceleași trei fire) a mai multor senzori – sistemul poate fi extins foarte ușor prin adăugarea de noi senzori ce măsoară temperatura în zone diferite (interior / exterior, diverse camere etc.).

3

Ultima funcționalitate interesantă propusă pentru sistemul prezentat în această lecție este posibilitatea de programare la distanță – programare OTA (Over-The-Air). Pentru a reprograma / reîncărca programul nu este nevoie să desfacem carcasa sistemului și să conectăm placa de dezvoltare la calculator ci acest lucru poate fi făcut prin conexiunea WiFi a plăcii de dezvoltare. Singura limitare a acestei metode este că dimensiunea programului nu poate depăși jumătate din memoria program a plăcii de dezvoltare dar nu este cazul sistemului nostru.

4

Interconectarea componentelor (placă de dezvoltare, senzor de temperatură și ecran LCD) este prezentată în diagrama următoare:

5

Senzorul de temperatură va avea pinul D0 conectat la pinul D2 al plăcii de dezvoltare iar pinii 5V și GND la pinii 3.3V și GND ai plăcii de dezvoltare (senzorul funcționează la tensiuni între 3V și 5.5V). Chiar dacă programul prezentat în cele ce urmează este gândit pentru un sistem cu un singur senzor, sistemul acceptă adăugarea pe aceleași 3 fire a mai multor senzori de temperatură pe distanțe de până la 200 de metri.

Ecranul LCD comunică cu placa de dezvoltare prin protocoul SPI și implică următoarele conexiuni:

  • Pinul 1 (RST) al ecranului LCD se conectează la pinul D0 al plăcii de dezvoltare;
  • Pinul 2 (CE) al ecranului la pinul D1 al plăcii de dezvoltare;
  • Pinul 3 (DC) al ecranului la pinul D6 (HMISO) al plăcii de dezvoltare;
  • Pinul 4 (DIN) al ecranului la pinul D7 (HMOSI) al plăcii de dezvoltare;
  • Pinul 5 (CLK) al ecranului la pinul D5 (HSCLK) al plăcii de dezvoltare;
  • Pinul 6 (VCC) al ecranului la un pin de 3.3V al plăcii de dezvoltare;
  • Pinul 7 (LIGHT) al ecranului nu este conectat în schema noastră. Acest pin comandă aprinderea luminii de fundal a ecranului. Se poate conecta la un pin digital liber pentru comandă digitală sau PWM;
  • Pinul 8 (GND) al ecranului la un pin GND al ecranului.

Programul a fost dezvoltat și testat utilizând Arduino IDE 1.8.3 cu extensia ESP8266 Community 2.3.0 instalată. Prima încărcare a programului necesită conectarea plăcii la calculator prin intermediul cablului USB dar ulterior placa va putea fi reprogramată prin OTA (calculatorul de pe care se face programarea trebuie să fie în aceiași rețea ca placa NodeMCU):

6

#include <ESP8266WiFi.h>

#include <ESP8266HTTPClient.h>

#include <ESP8266mDNS.h>

#include <WiFiUdp.h>

#include <ArduinoOTA.h>

Programul utilizează și următoarele biblioteci: Time 1.5.0 – pentru a menține data și ora, sincronizarea NTP se face automat de către bibliotecă la intervale mai mari de timp; OneWire 2.3.3 și DallasTemperature 3.7.6 – pentru comunicația cu senzorul de temperatură; Adafruit GFX 1.2.2 și Adafruit PCD8544 versiune modificată pentru ESP8266 – pentru partea de afișare pe ecranul LCD. În cadrul programului trebuie personalizate datele de conectarea WiFi (variabilele ssid[] și pass[]). Constanta timeZone indică fusul orar (2 sau 3 pentru România, oră de iarnă sau vară).

#include <TimeLib.h>

const int timeZone = 3;

WiFiUDP Udp;

const int NTP_PACKET_SIZE = 48;

byte packetBuffer[NTP_PACKET_SIZE];

unsigned int localPort = 2390;   

char ssid[] = “…”;

char pass[] = “…”;

WiFiClient client;

#define HOSTNAME “ESP8266-OTA-“

#include <DallasTemperature.h>

#include <OneWire.h>

#define ONE_WIRE_BUS D2

OneWire oneWire(ONE_WIRE_BUS);

DallasTemperature sensors(&oneWire);

#include <SPI.h>

#include <Adafruit_GFX.h>

#include <Adafruit_PCD8544.h>

const int8_t RST_PIN = D0;

const int8_t CE_PIN = D1;

const int8_t DC_PIN = D6;

Adafruit_PCD8544 display =

Adafruit_PCD8544(DC_PIN, CE_PIN, RST_PIN);

Secțiunea setup() se va ocupa cu inițializarea conexiunii WiFi (în cazul unei erori pe ecranul LCD va apărea mesajul !WiFi și sistemul va intra într-o buclă de reinițializarea până la conectarea la rețea), inițializarea rețelei OneWire de senzori, inițializarea ecranului și a rutinei de programare OTA. În cazul unei inițializări normale pe ecranul LCD vor apărea informații legate de inițializarea sistemului (denumirea OTA, denumirea rețelei WiFi, adresa IP).

void setup() {

  sensors.begin();

  delay(100);

  display.begin();

  display.setContrast(60);

  display.setTextColor(BLACK);

  display.setCursor(0,0);

  display.setTextSize(1);

  display.clearDisplay();

  display.display();

  String hostname(HOSTNAME);

  hostname += String(ESP.getChipId(), HEX);

  WiFi.hostname(hostname);

  display.println(“Hostname:” + hostname);

  display.display();

  WiFi.begin(ssid, pass);

  delay(5000);

  if (WiFi.status() != WL_CONNECTED) {

    display.clearDisplay();

    display.setCursor(17,16);

    display.setTextSize(2);

    display.print(“!WiFi”);

    display.display();

    delay(10000);

    ESP.restart();

  }

  display.print(“Connected to “);

  display.println(ssid);

  display.print(“IP:”);

  display.println(WiFi.localIP());

  Udp.begin(localPort);

  setSyncProvider(getNtpTime);

  ArduinoOTA.setHostname((const char *)hostname.c_str());

  ArduinoOTA.begin();

  display.display();

  delay(1000);

  display.clearDisplay();

  display.display(); }

Secțiunea loop() se va ocupa cu achiziția parametrului temperatură și cu partea de afișare. Apelarea rutinei de postare IoT se va face la un interval de o oră (constanta postingInterval). Pe ecranul LCD vor apărea alternativ (câte 30 de secunde fiecare) ora/data și temperatura. Ecranul de afișare al temperaturii va conține și mesajul OK/Fail indicând dacă ultima postare IoT s-a efectuat cu succes sau nu (astfel sistemul are și o funcționalitate de supraveghere a bunei funcționări a serviciului IoT).

7

8

unsigned long lastConnectionTime = 0;

const unsigned long postingInterval = 60L * 60L * 1000L;

boolean myiot_ok;

void loop() {

    ArduinoOTA.handle();

    sensors.requestTemperatures();

    delay(100);

    float temperature = sensors.getTempCByIndex(0);

    display.clearDisplay();

    if (second()<30) {

      display.drawRoundRect(2,2,80,44,3,BLACK);

      display.setTextColor(BLACK);

      display.setCursor(13,10);

      display.setTextSize(2);

      printDigits(hour(),false);

      printDigits(minute(),true);

      display.println();

      display.setTextSize(1);

      display.setCursor(13,26);

      display.print(day());

      display.print(“/”);

      display.print(month());

      display.print(“/”);

      display.print(year());

      display.println();

      display.display(); 

    }

    else {

      display.fillScreen(BLACK);

      display.drawRoundRect(2,2,80,44,3,WHITE);

      display.setTextColor(WHITE);

      display.setTextSize(2);

      display.setCursor(22,10);

      display.print((int)temperature);

      display.print((char)247);

      display.println(“C”);

      display.setTextSize(2);

      if (myiot_ora!=-1) {

        if (myiot_ok) { 

          display.setCursor(30,26);

          display.println(“OK”);    }

        else {

          display.setCursor(20,26);

          display.println(“FAIL”); }

      }

      display.display();

    }

    if (millis() – lastConnectionTime > postingInterval) {

      if (IoTpublish(temperature)>0)

        { myiot_ok=true; }

      else

        { myiot_ok=false; }

    }

}

Funcția IoTpublish este responsabilă cu postarea IoT și returnează codul returnat de operația HTTP GET efectuată. În cadrul acesteia trebuie completată cheia de autentificare oferită de serviciul Robofun IoT (variabila SENSOR_TOKEN). Procedura printDigits este folosită pentru afișarea mai simplă a orei.

int IoTpublish(float temperature) {

  String SENSOR_TOKEN = “…”;

  HTTPClient http;

  String data = String(“http://iot.robofun.ro/api/v1/senzor/&#8221;) + SENSOR_TOKEN + “/input?value=” + String(temperature, DEC);

  http.begin(data);

  int httpCode = http.GET();

  delay(100);

  if(httpCode > 0) {

      if(httpCode == HTTP_CODE_OK) {

        String payload = http.getString();

      }

   }

  http.end();

  lastConnectionTime = millis();

  return httpCode;

}

void printDigits(int digits, boolean dots){

   if (dots) display.print(“:”);

  if(digits < 10)

    display.print(‘0’);

  display.print(digits);

}

Funcțiile getNtpTime și sendNTPpacket sunt apelate automat de către biblioteca Time și asigură partea de comunicație NTP. Constanta ntpServerName indică serverul NTP care este utilizat pentru sincronizarea de timp.

IPAddress timeServerIP;

const char* ntpServerName = “time.nist.gov”;

time_t lastsyncr;

time_t getNtpTime() {

  WiFi.hostByName(ntpServerName, timeServerIP);

  while (Udp.parsePacket() > 0) ;

  sendNTPpacket(timeServerIP);

  uint32_t beginWait = millis();

  while (millis() – beginWait < 2500) {

    int size = Udp.parsePacket();

    if (size >= NTP_PACKET_SIZE) {

      Udp.read(packetBuffer, NTP_PACKET_SIZE);

      unsigned long secsSince1900;

      secsSince1900 =  (unsigned long)packetBuffer[40]

<< 24;

      secsSince1900 |= (unsigned long)packetBuffer[41]

<< 16;

      secsSince1900 |= (unsigned long)packetBuffer[42]

<< 8;

      secsSince1900 |= (unsigned long)packetBuffer[43];

      lastsyncr = (time_t) (secsSince1900 – 2208988800UL +

timeZone * SECS_PER_HOUR);

      return lastsyncr;

    }

  }

  return 0;

}

void sendNTPpacket(IPAddress &address) {

  memset(packetBuffer, 0, NTP_PACKET_SIZE);

  packetBuffer[0] = 0b11100011;  

  packetBuffer[1] = 0; 

  packetBuffer[2] = 6; 

  packetBuffer[3] = 0xEC; 

  packetBuffer[12]  = 49;

  packetBuffer[13]  = 0x4E;

  packetBuffer[14]  = 49;

  packetBuffer[15]  = 52;

  Udp.beginPacket(address, 123);

  Udp.write(packetBuffer, NTP_PACKET_SIZE);

  Udp.endPacket(); }

După o oră după punerea în funcțiune a sistemului se pot consulta datele înregistrate în cadrul platformei Robofun IoT. Mai jos este o captură de ecran cu datele înregistrate de către sistemul de test în decursul unei zile.

9

Cum să realizăm un gateway LoRaWAN

Acoperirea rețelelor LoRaWAN la noi în țară este destul de scăzută (atât a rețelelor comerciale cât și a rețelei TTN). Din acest motiv, pentru a testa un sistem IoT LoRaWAN (ca cel descris în „Cum să realiză un sistem IoT LoRaWAN”) uneori este necesară realizarea unui sistem gateway LoRaWAN propriu. Sistemele profesionale de acest tip sunt destul de scumpe reprezentând o variantă de lux, a se vedea studiul comparativ a celor de la LorIoT.

O altă variantă este construirea unui sistem gateway propriu utilizând o placă de dezvoltare de genul Raspberry Pi. Problema în acest caz este generată de complexitatea modulației radio LoRa – sistemele gateway fiind sisteme care ascultă frecvențe radio multiple simultan (sunt denumite și concentratoare). Din acest motiv un modul radio LoRa obișnuit nu poate echipa un sistem gateway LoRaWAN fiind necesar un modul de tip concetrator, de exemplu: iC880A – LoRaWAN Concentrator 868MHz – modul cel mai adesea folosit în sisteme gateway LoRaWAN bazate pe Raspberry Pi.

2

Pentru mai multe detalii despre cum puteți construi un sistem gateway LoRaWAN bazat pe un modul de tip concentrator puteți consulta și materialele:

Chiar dacă prețul unui modul concentrator este mai mic decât a unui gateway profesional construirea unui astfel de sistem implică totuși un buget destul de mare.

Singura alternativă, accesibilă ca buget, este realizarea unui sistem gateway LoRaWAN de tipul One Channel (sau Single Channel). Adică vom un utiliza un modul radio LoRa obișnuit împreună cu o placă de tipul Raspberry Pi pentru realizarea unui sistem gateway. Dezavantajul unui astfel de gateway este faptul că ascultă pe o singură frecvență radio neputând comunica simultan cu mai multe sisteme IoT LoRaWAN. Acest tip de sisteme sunt considerate sisteme de tip ”forwarder” (Single Channel Forwarder) neavând o funcționalitate gateway LoRaWAN completă. Totuși, un astfel de sistem poate fi utilizat în locații izolate (fără acoperire LoRaWAN) pentru a testa comunicația LoRaWAN. Rețeaua TTN permite accesul acestor sisteme în rețea dar nu încurajează și nu asigură suport pentru ele fiind considerate compatibile dar neconforme cu specificațiile LoRaWAN.

Pentru implementare vom utiliza o placă de dezvoltare Raspberry Pi 3 și un hat LoRa/GPS. Testele au fost realizate sub Raspbian 9 (stretch) Lite, kernel 4.9.41-v7+.

Placa Raspberry Pi trebuie să aibă protocolul SPI activat (cu ajutorul utilitarului raspi-config) și pachetul wiringpi instalat.

$sudo raspi-config

4

$sudo apt-get update

$sudo apt-get install wiringpi

Pentru a implementa funcționalitatea de redirecționare a comunicației LoRa către platforma TTN vom utiliza software-ul single_chan_pkt_fwd.

$ wget https://github.com/tftelkamp/single_chan_pkt_fwd/archive/master.zip

$ unzip master.zip

$ cd single_chan_pkt_fwd-master

$ nano main.cpp

În fișierul main.cpp vom personaliza următoarele linii:

int ssPin = 6;

int dio0  = 7;

int RST   = 0;

sf_t sf = SF7;

uint32_t  freq = 868100000;

// opțional, dacă dorim să declarăm

// poziția și altitudinea sistemului

float lat=…;

float lon=…;

int   alt=…;

static char platform[24] = “Single Channel Gateway”;

static char email[40] = “…”;

static char description[64] = “…”;

#define SERVER1 “52.169.76.203”

#define PORT 1700

Salvăm (CTRL+O, CTRL+X), compilăm programul și îl lansăm în execuție:

$ make

$ sudo ./single_chan_pkt_fwd

5

Următorul pas necesită înregistrarea sistemului gateway în cadrul platformei TTN. În momentul înregistrării sistemului gateway este foarte important să bifăm opțiunea ”I’m using the legacy packet forwarder” (nu se poate modifica ulterior) și să copiem Gateway ID din consola ssh în consola de înregistrare.

6

După terminare înregistrării vom putea observa în consola TTN conexiunea dintre sistem și platforma TTN (Gateway Overview):

7

Pentru a face ca programul single_chan_pkt_fwd să ruleze automat la repornirea sistemului de operare adăugăm următoarea linie în fișierul /etc/rc.local (înainte de linia cu exit 0):

sudo /home/pi/single_chan_pkt_fwd-master/single_chan_pkt_fwd &

presupunând că am salvat și realizat compilarea în directorul utilizatorului pi.

Pentru mai multe informații legate de realizarea unui gateway LoRaWAN TTN Single Channel se pot consulta și următoarele materiale: