ESP8266 Geolocation

Localizarea geografică este o problemă rezolvată cu ajutorul mai multor tehnologii actuale. Tehnologia GPS, cea mai cunoscută și utilizată modalitate de localizare, necesită funcționarea în spații deschise și utilizarea unui receptor specializat. În dispozitivele actuale această tehnologie se utilizează în combinație cu alte metode pentru a crește acuratețea localizării și pentru a oferi posibilitatea de localizare inclusiv în interiorul clădirilor. De exemplu, în telefoanele mobile inteligente, tehnologia GPS este combinată cu triangularizarea semnalelor radio GSM  și WiFi. În cadrul materialului de față vom arăta cum putem utiliza tehnica de localizare bazată pe triangularizarea semnalului WiFi utilizând o placă de dezvoltare WiFi ESP8266 și pe serviciul cloud Google Geolocation API. O astfel de soluție permite realizarea unui dispozitiv de localizare mult mai ieftin decât dispozitivele comerciale bazate pe soluții GPS și care permite localizarea inclusiv în interiorul clădirilor. Singura cerință de funcționare mai specială a dispozitivului este prezența unor rețele WiFi cartografiate de compania Google (cerință îndeplinită cu siguranță în mediul urban).

Pentru implementare am ales o placă de dezvoltare Adafruit Feather HUZZAH dar poate fi utilizată orice placă bazată pe ESP8266. Unul dintre avantajele oferite de placa de dezvoltare aleasă este posibilitatea de alimentare directă de la un acumulator LiPo de 3.7V permițând astfel realizarea unui sistem portabil. Pentru partea de afișare, utilă mai ales în cazul utilizării mobile a dispozitivului implementat, am utilizat un afișaj SHARP Memory Display ce are un consum extrem de redus (4µA @ 3.3V) și un contrast foarte bun.

Cele două componente ale sistemului vor comunica prin intermediul unei conexiuni SPI unidirecționale (doar 3 fire). Asftel, vom conecta:

  • Pinii 3.3V și GND al ecranului la pinii corespondenți ai plăcii de dezvoltare;
  • Pinul CLK al ecranului la pinul 14 al plăcii de dezvoltare;
  • Pinul DI al ecranului la pinul 13 al plăcii de dezvoltare;
  • Pinul CS al ecranului la pinul 5 al plăcii de dezvoltare.

2

Pinul 16 (Wake) al plăcii va fi conectat la pinul RST dacă dorim ca sistemul să intre în modul de consum redus între două localizări (util pentru funcționarea mobilă cu alimentare bazată pe acumulator). Conexiunea trebuie realizată după încărcarea programului pe placa de dezvoltare.

Programul sistemului a fost testat utilizând mediul de dezvoltare Arduino IDE 1.8.5 având instalate extensia ESP8266 Community 2.4.1 și bibliotecile Adafruit GFX 1.2.3, Adafruit SHARP Memory Display 1.0.6 (pentru controlul ecranului) și WifiLocation 1.2.2 (pentru interacțiunea cu Geolocation API).

#include <ESP8266WiFi.h>

#include <WifiLocation.h>

#include <Adafruit_GFX.h>

#include <Adafruit_SharpMem.h>

În cadrul programului trebuie personalizate datele de acces la o rețea WiFi (ssid și passwd) și cheia de acces la serviciile cloud Google (googleApiKey). Pentru a obține cheia de acces trebuie să vă înregistrați ca dezvoltator pe site-ul Google Developers, să activați funcționalitatea GeoLocation API și să solicitați cheia de acces. Accesul este gratuit până la o limită de 2500 de interogări pe zi (evitați o utilizare la un interval mai mic de 30 de secunde).

const char* googleApiKey = “…”;

const char* ssid = “…”;

const char* passwd = “…”;

WifiLocation location(googleApiKey);

#define SHARP_SCK  14

#define SHARP_MOSI 13

#define SHARP_SS   5

Adafruit_SharpMem display(SHARP_SCK, SHARP_MOSI, SHARP_SS, 96, 96);

#define BLACK 0

#define WHITE 1

Directiva debug poate fi utilizată (decomentată) dacă doriți să vedeți mesajele generate de sistem în consola serială.

//#define debug

3

În cadrul secțiunii setup() se va realiza scanarea rețelelor WiFi din zona în care se află sistemul, obținerea coordonatelor geografice de la serviciul cloud de localizare pe baza puterii semnalului fiecărei rețea WiFi observate și afișarea coordonatelor pe ecran (și în consola serială dacă este activată directiva debug). După un ciclu de localizare sistemul va intra în stare de consum redus (deepSleep) pentru 10 minute. Datele afișate vor conține latitudinea și longitudinea precum și acuratețea localizării (exprimată în metri).

4

void setup() {

#ifdef debug

Serial.begin(115200);

#endif

display.begin();

display.clearDisplay();

display.refresh();

WiFi.mode(WIFI_STA);

WiFi.begin(ssid, passwd);

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

#ifdef debug

Serial.print(“Attempting to connect to WPA SSID:“);

Serial.println(ssid);

Serial.print(“Status = “);

Serial.println(WiFi.status());

#endif

delay(500);

}

location_t loc = location.getGeoFromWiFi();

#ifdef debug

Serial.println(“Location request data”);

Serial.println(location.getSurroundingWiFiJson());

Serial.println(“Latitude: ” + String(loc.lat, 7));

Serial.println(“Longitude: ” + String(loc.lon, 7));

Serial.println(“Accuracy: ” + String(loc.accuracy));

#endif

display.setTextColor(BLACK);

display.setCursor(12,10);

display.setTextSize(1);

display.println(“Latitude:”);

display.setCursor(5,19);

display.setTextSize(2);

display.println(String(loc.lat, 3));

display.setCursor(12,37);

display.setTextSize(1);

display.println(“Longitude:”);

display.setCursor(5,46);

display.setTextSize(2);

display.println(String(loc.lon, 3));

display.setCursor(12,64);

display.setTextSize(1);

display.println(“Accuracy:”);

display.setCursor(5,73);

display.setTextSize(2);

display.println(String(loc.accuracy));

display.refresh();

ESP.deepSleep(600L*1000000L);

}

void loop() {

}

 

Cum să realizăm un repetor WiFi utilizând ESP8266

Dispozitivele de tip repetor sau amplificator WiFi (WiFi Repeater sau WiFi Extender) sunt utilizate pentru a crește aria de acoperire a rețelelor WiFi. În case sau clădiri de dimensiuni mai mari un singur dispozitiv de acces WiFi, oricât de performant ar fi, este greu să asigure semnal radio WiFi pe toată aria. Utilizarea mai multor dispozitive de acces WiFi independente ridică probleme destul de mari de administrare de rețea și de management a datelor de acces. Soluția mult mai simplă și mai ieftină este utilizarea dispozitivelor de tip repetor WiFi. Aceste dispozitive sunt diponibile comercial la prețuri variate și cu funcționalități și perfomanțe diverse. Totuși, dorim să vă propunem realizarea unui astfel de dispozitiv într-o manieră personală (și extrem de ieftină) utilizând o placă de dezvoltare bazată pe circuitul WiFi ESP8266. Acest circuit are marele avantaj de a putea funcționa simultan atât ca AP cât și ca client WiFi permițând implementarea funcționalității de repetor într-o manieră foarte simplă. Testele au fost realizate utilizând o placă de dezvoltare Adafruit Feather HUZZAH dar la fel de bine pot fi utilizate plăci precum NodeMCU, Sparkfun ESP8266 Thing sau chiar ESP-01.

Pentru a implementa funcționalitatea de repetor WiFi vom utiliza pachetul software esp_wifi_repeater. Pachetul poate fi modificat și recompilat după propriile dorințe înainte de încărcarea pe placa de dezvoltare dar pentru început se pot utiliza fișierele binare oferite de dezvoltatori pachetului (fișierele 0x00000.bin, 0x10000.bin și esp_init_data_default_v08_vdd33.bin din directorul firmware). Pentru încărcarea fișierelor binare se va utiliza utilitarul ESP8266 DOWNLOAD TOOL. După copierea utilitarului și a fișierelor binare pe calculatorul local se poate trece la încărcarea acestora în memoria plăcii. Înainte de apăsarea butonului START aveți grijă să completați adresele de încărcare a fișierelor binare, să selectați portul serial pe care este conectată placa de dezvoltare și, specific plăcii Adafruit Feather HUZZAH, să selectați dimensiunea memoriei flash 32Mbit.

3

După încărcarea în memorie a software-ului putem verifica inițializarea corectă a dispozitivului repetor WiFi cu ajutorul unui utilitar de conectare pe serială (de exemplu Putty). Viteza de conectare serială este 115200 baud.

4

Cea mai simplă modalitate de configurare a acestuia este prin intermediul interfeței web. Dispozitivul se poate configura și prin consola serială sau printr-o conexiune telnet pe portul 7777 dar aceste modalități sunt puțin mai dificile. Pentru a accesa interfața web este necesar să ne conectăm la AP-ul creat de dispozitiv (MyAP) și să accesăm adresa 192.168.4.1.

5

Conectarea la AP-ul dispozitivului se poate observa imediat în consola serială:

6

Interfața web de configurare permite introducerea datelor de acces a rețelei WiFi a cărui semnal dorim să-l extindem ca arie de acoperire (STA Settings) și datele de acces ale noului punct de acces (AP Settings). La finalizarea configurării dispozitivului vom putea face diferența între AP-ul inițial și noul AP (AP-ul repetor) deoarece vor avea denumiri și date de acces diferite. Performanțele oferite de AP-ul bazat pe circuitul ESP8266 nu sunt spectaculoase (maxim 8 clienți și rată de transfer de maxim 5Mbps) dar oferă o modalitate de extindere a ariei de acoperire WiFi extrem de simplă și ieftină. Aceaste dispozitive repetoare WiFi pot fi utilizate pentru rețele IoT sau pentru extinderea semnalului WiFi în spații deschise (parcele mari de teren de exemplu).

7

Dacă dorim ca repetorul WiFi să fie transparent (să nu definim un nou AP cu nume și date de acces proprii) se poate bifa opțiunea Automesh. În acest caz nu mai este necesar să completăm informațiile din secțiunea AP Settings – dispozitivul repetor va propaga semnalul WiFi sub numele rețelei WiFi originale (și datele de conectare vor fi identice). Facilitatea de Automesh permite crearea unei rețele de repetoare WiFi cu până la 5 niveluri permițând extinderea ariei de acoperire într-o manieră impresionantă.

8

Dacă apelăm la configurarea în consolă a dispozitivului repetor WiFi putem accesa funcții avansate implementate în pachetul software utilizat (a se vedea documentația oficială). Printre aceste funcții putem enumera:

  • Configurarea statică a setărilor pentru interfețele de rețea;
  • Definirea de rute (routes) personalizare – facilități de rutare avansată;
  • Definirea de reguli de dirijare a traficului (ACLs) – facilități de firewall;
  • Redirecționarea unor porturi – port mapping;
  • Facilități de raportare prin intermediul protocolului MQTT;

Controlul consumului – dacă tensiunea de alimentare scade sub un anume prag putem configura dispozitivul să intre într-o stare de consum redus.

Managementul conexiunii WiFi la ESP8266

Circuitul WiFi Espressif ESP8266 a reprezentat o evoluție importantă în universul plăcilor de dezvoltare în general dar și a comunității Arduino. Permițând conectivitate WiFi la un preț derizoriu și ușurință de programare și utilizare în cadrul sistemelor DIY, ESP8266 are o popularitate și o răspândire din ce în ce mai mare echipând plăci de dezvoltare diverse.

Facilitățile oferite de extensia ESP8266 pentru Arduino IDE includ conectarea la Internet prin intermediul unui AP, activarea ca un AP, funcționarea duală client WiFi și AP, utilizarea protocoalelor http și https, funcționarea ca server web sau server DNS ș.a.m.d. Toate aceste facilități oferite de circuitul ESP8266 fac din acesta un instrument extrem de util în cadrul proiectelor proprii. Una dintre problemele care apare totuși la construirea unui dispozitiv bazat pe circuitul ESP8266 este managementul datelor de conectarea la rețea. Dacă se dorește schimbarea datelor de acces la rețeaua WiFi (mutăm dispozitivul în altă locație sau modificăm infrastructura de acces la Internet) dispozitivul trebuie reprogramat. Chiar dacă luăm în calcul programarea OTA tot este necesară utilizarea unui sistem de calcul (PC sau laptop cu Arduino IDE instalat) pentru a modifica datele de acces (SSID și parolă).

Bineînțeles, este posibilă programarea unei interfețe web care să permită modificarea variabilelor corespunzătoare datelor de acces dar acest lucru este destul de consumator de timp și adaptarea codului pentru proiecte diverse este o sarcină repetitivă destul de anevoioasă. Din fericire există o bibliotecă software ce permite automatizarea acestui proces și ușurează foarte mult dezvoltarea noilor proiecte în care avem nevoie de modificarea parametrilor de funcționare fără a reprograma dispozitivul.

Această bibliotecă se numește WiFiManager și permite configurarea, prin intermediul unei interfețe web, a conexiunii WiFi în cazul în care configurarea implicită nu funcționează (interfața web de configurare se activează doar în cazul în care conectarea la rețea nu este posibilă cu datele de autentificare curente). Instalarea bibliotecii se face din Library Manager a mediului Arduino IDE. Testele au fost realizate pe o placă NodeMCU utilizând Arduino IDE 1.8.5 și extensia ESP8266 Community 2.4.1.

2

După instalarea bibliotecii facilitățile de configurare se includ în program inserând în cod secvențele următoare. La începutul programului se include biblioteca WiFiManager și dependințele acesteia:

#include <ESP8266WiFi.h>

#include <DNSServer.h>   

#include <ESP8266WebServer.h> 

#include <WiFiManager.h>

WiFiManager wifiManager;

În cadrul secțiunii setup() se înlocuiește codul obișnuit de conectare cu instrucțiunea:

wifiManager.autoConnect();

Execuția acesteia va conduce la conectarea la rețea dacă procesul de configurare a fost deja parcurs sau la blocarea programului și la pornirea interfeței de configurare web. Aceasta va fi accesibilă prin intermediul unui AP propriu sistemului, AP denumit ESPXXXXXX, la adresa IP 192.168.4.1 .

3

Interfața web permite configurarea și salvarea rețelei în care va funcționa dispozitivul, vizualizarea informațiilor hardware specifice plăcii de dezvoltare și resetarea acesteia.

4

5

Dacă conectivitatea de rețea nu este esențială în funcționarea dispozitivului – dispozitivul trebuie să funcționeze și fără conexiunea de rețea – putem adăuga următoarea instrucțiune pentru a defini un timp în care se așteaptă configurarea după care se trece mai departe la execuția normală a programului.

wifiManager.setConfigPortalTimeout(180);

În cazul în care dorim totuși să reluăm periodic tentativa de conectare la rețea, utilă de exemplu în cazul în care repornirea sistemului coincide cu indisponibilitatea AP-ului – datele de conectare sunt corecte dar în momentul în care se încearcă conectarea nu este disponibilă infrastructura, se poate insera o secvență de instrucțiuni, ca în exemplul de mai jos, în secțiunea loop():

if (WiFi.status() != WL_CONNECTED) wifiManager.autoConnect();

Pagina web de configurare poate fi pornită și în mod voit (la apăsarea unui buton de exemplu) prin intermediul instrucțiunii:

wifiManager.startConfigPortal();

O altă facilitate interesantă oferită de bibliotecă este posibilitatea de a adăuga în cadrul paginii de configurare a unor parametrii proprii ce vor fi salvați în memoria nevolatilă a circuitului ESP8266. Instrucțiunile de mai jos definesc o variabilă server ce permite memorarea adresei de rețea a unui server MQTT. Datele de configurat vor apărea în interfața web alături de configurația de acces în rețeaua WiFi.

WiFiManagerParameter custom_mqtt_server(“server”, “mqtt server”, mqtt_server, 40);

wifiManager.addParameter(&custom_mqtt_server);

mqtt_server = custom_mqtt_server.getValue();

6

Control WiFi pentru luminițele de Crăciun

Farmecul sărbătorilor de iarnă este dat, printre altele, de instalațiile de lumini. În sute de variante, în zeci de culori, aflate undeva între plăcerea de a decora și obsesie, jucării sau elemente de iluminat, instalațiile de luminițe sunt cu siguranță un element important în cadrul pregătirilor pentru sărbătoarea de Crăciun. Dacă ați experimentat suficiente instalații și simțiți că vreți să treceți la un nivel următor (adică să începeți să le personalizați), în cadrul proiectului de față vă propunem modificarea unei instalații existente prin adăugarea unei comenzi de pornire prin WiFi.

Comanda de pornire se poate realiza simplu prin intermediul unui element de tip releu. Pentru a realiza comanda de la distanță prin intermediul WiFi este necesar să conectăm elementul de tip releu la o placă de dezvoltare ce permite comunicație WiFi. Realizarea unui ansamblu compact, placă de dezvoltare WiFi plus releu de comandă, este însă o sarcină destul de anostă. Din acest motiv vă propunem utilizarea unei platforme integrate: ESP8266-EVB-BAT-BOX.

2

Această platformă este echipată cu un microprocesor WiFi ESP8266 și un releu 220VAC/10A. Platforma se poate alimenta de la alimentator de rețea de 5V sau de la un acumulator LiPo de 3.7V. Platforma se comercializează împreună cu o carcasă de protecție HAMMOND și conține un conector WAGO cu șurub de tip mufă (din două părți) pentru integrarea facilă a releului într-un montaj. Datorită facilităților oferite de această platformă este ideală pentru o aplicație de comandă de tip WiFi.

3

ATENȚIE!!! Chiar dacă releul de pe platforma ESP8266-EVB-BAT-BOX suportă comanda unor circuite conectate la tensiune de rețea (220V), NU este recomandată realizarea de montaje proprii la această tensiune fără supravegherea sau îndrumarea unui specialist. Pericol de electrocutare, moarte, scurtcircuit, incendiu!!! Pentru montaje personale de tip hobby se recomandă comanda unor circuite de luminițe alimentate de la baterii sau cu alimentator de rețea (tensiunea din firele conectate la releu să fie de 3V-12V) eliminând astfel riscul de accidentare.

Conectarea instalației se va face prin secționarea firului de curent (nu cel de masă) și intercalarea releului. Astfel, platforma ESP8266-EVB-BAT-BOX va putea controla alimentarea instalației și vom putea programa când aceasta să se aprindă. Sistemul propus va putea să aprindă instalația la o anumită oră sau în urma unei comenzi directe.

4

Programarea platformei necesită un programator FTDI de 3.3V. Pentru mai multe detalii despre programarea platformei EVB-BAT-BOX este necesară consultarea materialului „HOW TO USE ESP8266 WITH ARDUINO IDE”. Programul a fost dezvoltat și testat utilizând Arduino IDE 1.8.3 având instalată extensia ESP8266 Community 2.3.0.

#include <ESP8266WiFi.h>

#include <ESP8266mDNS.h>

#include <WiFiUdp.h>

#include <ESP8266WebServer.h>

#include <TimeLib.h>

const int timeZone = 2;

WiFiUDP Udp;

const int NTP_PACKET_SIZE = 48;

byte packetBuffer[NTP_PACKET_SIZE];

unsigned int localPort = 2390;   

WiFiServer server(80);

În cadrul programului trebuie personalizate datele de conectare la rețeaua WiFi (variabilele ssid și pass).

char ssid[] = “…”;

char pass[] = “…”;

WiFiClient client;

#define HOSTNAME “ESP8266-“

#define RELAY_PIN 5

int start_hour = 20;

int stop_hour = 23;

boolean automatic = true;

În cadrul secțiunii setup() se va inițializa conexiunea WiFi și se va raporta în consola serială adresa IP a sistemului. Aceasta trebuie folosită pentru a accesa sistemul dintr-un client web (browser) din aceiași rețea locală. Programul va utiliza biblioteca Time și conexiunea de rețea pentru a menține ora exactă (prin sincronizare NTP).

void setup() {

  Serial.begin(115200);

  Serial.println();

  Serial.println();

  pinMode(RELAY_PIN, OUTPUT);

  digitalWrite(RELAY_PIN,LOW);

  String hostname(HOSTNAME);

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

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

  WiFi.mode(WIFI_STA);

  WiFi.hostname(hostname);

  WiFi.begin(ssid, pass);

  delay(5000);

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

    delay(10000);

    ESP.restart();

  }

  Serial.print(“IP:”);

  Serial.println(WiFi.localIP());

  server.begin();

  Udp.begin(localPort);

  setSyncProvider(getNtpTime);

}

Secțiunea loop() implementează comunicația HTTP ce ne va permite acționarea releului de la distanță. Interfața de comandă (ce poate fi accesată la adresa IP a sistemului) prezintă ora curentă, modul de funcționare (automat sau manual), ora de pornire și oprire automată precum și comenzile manuale de pornire / oprire. La pornire sistemul este în mod automat și are setate ca oră de pornire ora 20:00 și ca oră de oprire ora 23:00. Prin acționarea semnelor +/- din dreptul celor două ore presetate acestea pot fi modificate.

5

Prin acționarea comenzilor manuale de oprire sau pornire se va comuta în modul manual de comandă în care intervalul de funcționare automată este ignorat și sistemul răspunde doar la comenzile manuale de pornire și oprire. Reactivarea modului de funcționare automat se poate face apelând orice modificare (+/-) a orelor de pornire / oprire automată.

6

void loop() {

  if (automatic)

    if ((hour()>start_hour) && (hour()<stop_hour))

digitalWrite(RELAY_PIN, HIGH);

    else digitalWrite(RELAY_PIN, LOW);

  WiFiClient client = server.available();

  if (!client) return;  

  Serial.println(“new client”);

  while(!client.available()){ delay(1); }

  String request = client.readStringUntil(‘\r’);

  Serial.println(request);

  client.flush();

  if (request.indexOf(“/COMMAND=ON”) != -1) {

    digitalWrite(RELAY_PIN, HIGH); 

    automatic = false;

  }

  else if (request.indexOf(“/COMMAND=OFF”) != -1) {

    digitalWrite(RELAY_PIN, LOW); 

    automatic = false;

  }

  else if (request.indexOf(“/COMMAND=PLUS_START”) != -1) {

    start_hour++;

    if (start_hour>23) start_hour=0;

    automatic = true;

  }

  else if (request.indexOf(“/COMMAND=MIN_START”) != -1) {

    start_hour–;

    if (start_hour<0) start_hour=23;

    automatic = true;

  }

  else if (request.indexOf(“/COMMAND=PLUS_STOP”) != -1) {

    stop_hour++;

    if (stop_hour>23) stop_hour=23;

    automatic = true;

  }

  else if (request.indexOf(“/COMMAND=MIN_STOP”) != -1) {

    stop_hour–;

    if (stop_hour<0) stop_hour=23;

    automatic = true;

  }

  client.println(“HTTP/1.1 200 OK”);

  client.println(“Content-Type: text/html”);

  client.println(“”);

  client.println(“<!DOCTYPE HTML>”);

  client.println(“<html>”);

  client.println(“<h1>WiFi Christmas Lights</h1>”);

  client.println(“<br>”);

  client.print(“Time now: “);

  client.print(hour());

  client.print(“:”);

  client.print(minute());

  client.print(“:”);

  client.println(second());

  client.println(“<br>”);

  if (automatic) client.print(“Mode: Automatic”);

  else client.print(“Mode: Manual”);

  client.println(“<br>”);

  client.println(“Click <a href=\”/COMMAND=ON\”>here</a> to turn ON the Christmas Lights<br>”);

  client.println(“Click <a href=\”/COMMAND=OFF\”>here</a> to turn OFF the Christmas Lights<br>”);

  client.print(“Autostart at <a href=\”/COMMAND=PLUS_START\”>(+)</a>”);

  client.print(start_hour);

  client.println(“<a href=\”/COMMAND=MIN_START\”>(-)</a> hour.”);

  client.println(“<br>”);

  client.print(“Autostop at <a href=\”/COMMAND=PLUS_STOP\”>(+)</a>”);

  client.print(stop_hour);

  client.println(“<a href=\”/COMMAND=MIN_STOP\”>(-)</a> hour.”);

  client.println(“</html>”);

  delay(1);

  Serial.println(“Client disonnected”);

  Serial.println(“”);

}

Funcțiile getNtpTime() și sendNTPpacket() sunt apelate periodic automat de biblioteca Time pentru sincronizarea timpului.

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();

}

Pentru alte variante de realizare puteți consulta și proiectele:

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

Proiect Blynk IoT

Sparkfun Blynk ESP8266 este o placă de dezvoltare care, pe lângă avantajele oferite de serviciul cloud Blynk (prezentate în cadrul proiectului Cum să realizăm un sistem IoT fără să scriem nici o linie de cod?), oferă o combinație de componente extrem de interesante.

1

Procesorul WiFi ESP8266, senzorul digital de temperatură și umiditate Si7021 integrat și posibilitatea de alimentare de la un acumulator fac din placa Sparkfun Blynk ESP8266 o excelentă platformă pentru experimente IoT. În cadrul proiectului de față vom programa această placă utilizând Arduino IDE și nu vom utiliza platforma cloud specifică ci noul serviciu Robofun IoT.

2

Pentru punerea în funcțiune și programarea plăcii Blynk se recomandă parcurgerea materialului Blynk Board Arduino Development Guide. Pentru prima parte a proiectului nu avem nevoie decât de placa de dezvoltare și cablul USB de încărcare a programului – senzorul de temperatură și umiditate ce va furniza informațiile transmise către serviciul IoT este integrat pe placă.

Programul a fost dezvoltat și testat utilizând Arduino IDE 1.8.1, extensia esp8266 2.1.0 și bibliotecile Sparkfun Si7021 Humidity and Temperature Sensor 1.0.0 și Adafruit NeoPixel 1.1.1 (pe placa de dezvoltare se află și un led RGB WS2812 pe care îl vom comanda utilizând această bibliotecă). Declarațiile inițiale ale programului sunt următoarele:

#include <Wire.h>
#include “SparkFun_Si7021_Breakout_Library.h”
#include <Adafruit_NeoPixel.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>

În cadrul programului trebuie personalizate datele de conectare WiFi:

char ssid[] = ““;

char pass[] = ““;

WiFiClient client;

float humidity = 0;

float tempC = 0;

Weather sensor;

#define PIN  4

#define NUMPIXELS 1

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

Varialbila postingInterval indică intervalul de timp între două raportări IoT (în milisecunde, în cazul dat ca exemplu: 10 minute)

unsigned long lastConnectionTime = 0;

const unsigned long postingInterval = 600L * 1000L;

Dacă doriți ca programul să afișeze informații despre execuție se va decomenta următoarea linie:

//#define debug

3

În cadrul secțiunii setup() se va inițializa conexiunea WiFi – pe durata inițializării led-ul RGB de pe placă va lumina roșu.

void setup() {

  pixels.begin();

  pixels.setPixelColor(0, pixels.Color(150,0,0));

  pixels.show();

  #ifdef debug

    Serial.begin(9600);

  #endif

  int status = WL_IDLE_STATUS;

  while (status != WL_CONNECTED)

  {

    status = WiFi.begin(ssid, pass);

    delay(10000);

  }

  #ifdef debug

    Serial.println(“Connected to wifi”);

  #endif

  sensor.begin();

  pixels.setPixelColor(0, pixels.Color(0,0,0));

  pixels.show();

}

Secțiunea loop() va verifica timpul scurs de la ultima raportare și dacă este depășit intervalul specificat se va apela procedura IoTPublish() care se ocupă de citirea senzorului Si7021 și de trimiterea datelor către Robofun IoT. Pe durata execuției procedurii led-ul de pe placă va lumina verde.

void loop() {

  if (millis() – lastConnectionTime > postingInterval)

  {

    pixels.setPixelColor(0, pixels.Color(0,150,0));

    pixels.show();

    IoTpublish();

    pixels.setPixelColor(0, pixels.Color(0,0,0));

    pixels.show();

  }

}

Procedura IoTpublish() va posta datele oferite de senzorul de temperatură și umiditate către serviciul IoT. Postarea se face prin două apeluri HTTP GET, câte unul pentru fiecare parametru trimis (temperatură și umiditate). Apelurile HTTP vor returna răspunsul 1 dacă totul este în regulă sau alte valoare dacă a apărut o eroare. Pentru utilizarea serviciului Robofun IoT este necesară înregistrarea gratuită a unui cont.

4

După înregistrare și conectare este necesară definirea a doi noi senzori.

5

Fiecare senzor va avea propria cheie de autentificare (token) și pentru fiecare dintre cei doi senzori este necesar să copiem această cheie în program (SENZOR_TOKEN1 și SENZOR_TOKEN2).

void IoTpublish() {

  String SENSOR_TOKEN1=”“;

  String SENSOR_TOKEN2=”“;

  HTTPClient http;

  humidity = sensor.getRH();

  tempC = sensor.getTemp();

  String data =

    “http” + “://iot.robofun.ro/api/v1/senzor/” + SENSOR_TOKEN1 + “/input?value=” + String(tempC, DEC);

  http.begin(data);

  int httpCode = http.GET();

  #ifdef debug

    if(httpCode > 0) {

      Serial.printf(“[HTTP] GET… code: %d\n”, httpCode);

      if(httpCode == HTTP_CODE_OK) {

        String payload = http.getString();

        Serial.println(payload);

      }

    } else {

      Serial.printf(“[HTTP] GET… failed, error: %s\n”, http.errorToString(httpCode).c_str());

    }

  #endif

  http.end();

  data = “http” + “://iot.robofun.ro/api/v1/senzor/” + SENSOR_TOKEN2 + “/input?value=” + String(humidity, DEC);

  http.begin(data);

  httpCode = http.GET();

  #ifdef debug

    if(httpCode > 0) {

      Serial.printf(“[HTTP] GET… code: %d\n”, httpCode);

      if(httpCode == HTTP_CODE_OK) {

        String payload = http.getString();

        Serial.println(payload);

      }

    } else {

      Serial.printf(“[HTTP] GET… failed, error: %s\n”, http.errorToString(httpCode).c_str());

    }

  #endif

  http.end();

  lastConnectionTime = millis();

}

După încărcarea programului pe placa de dezvoltare și scurgerea unui interval de funcționare mai lung decât postingInterval vom putea vedea deja pe graficul oferit de interfața serviciului valorile achiziționate de senzorul plăcii. În capturile de ecran următoare putem vedea un grafic pe o perioadă mai lungă de timp (temperatura – vizualizare a tuturor valorilor, umiditate – vizualizare a valorilor medii la nivel de săptămână).

6

7

Un atu foarte important al plăcii de dezvoltare Sparkfun Blynk ESP8266 este posibilitatea de alimentare utilizând un acumulator LiPo de 3.7V. Asta conferă sistemului portabilitate – vom dispune de un sistem de achiziție IoT de dimensiunea a două cutii de chibrituri. Totuși, o problemă majoră care se naște în acest caz este evaluarea autonomiei de funcționare a sistemului – determinarea nivelului de încărcare a acumulatorului. Utilizarea convertorului analog-numeric al plăcii în acest scop presupune construirea unui divizor de tensiune (convertorul poate măsura tensiuni între 0V și 3.3V) dar această soluție conduce la descărcarea mai rapidă a acumulatorului. O soluție mult mai elegantă pentru rezolvarea acestei probleme este utilizarea unei componente specializate de măsurare: LiPo Fuel Gauge. Conectarea acestei componente, între conectorul de alimentare al plăcii și acumulator, este prezentată în diagrama următoare:

8

Circuitul integrat MAX17043G+U al componentei de măsurare va supraveghea nivelul de tensiune al acumulatorului și îl va raporta către placa de dezvoltarea prin intermediul magistralei I2C – placa de dezvoltare are un conector special I2C la care se poate conecta componenta de măsurarea. Introducerea componentei de măsurare nu va influența în nici un fel posibilitatea de încărcarea a acumulatorului prin intermediul mufei microUSB de pe placă și nici nu va scădea durata de viață a acumulatorului.

În cadrul programului vom adăuga următoarele funcții și proceduri necesare lucrului cu circuitul MAX17043:

unsigned int vcellMAX17043() {

unsigned int vcell;

vcell = i2cRead16(0x02);

vcell = vcell >> 4;

return vcell; }

float percentMAX17043() {

unsigned int soc;

float percent;

soc = i2cRead16(0x04);

percent = (byte) (soc >> 8);

percent += ((float)((byte)soc))/256;

return percent; }

void qsMAX17043() {

i2cWrite16(0x4000, 0x06); }

unsigned int i2cRead16(unsigned char address) {

int data = 0;

Wire.beginTransmission(MAX17043_ADDRESS);

Wire.write(address);

Wire.endTransmission();

Wire.requestFrom(MAX17043_ADDRESS, 2);

while (Wire.available() < 2) ;

data = ((int) Wire.read()) << 8;

data |= Wire.read();

return data; }

void i2cWrite16(unsigned int data, unsigned char address) {

Wire.beginTransmission(MAX17043_ADDRESS);

Wire.write(address);

Wire.write((byte)((data >> 8) & 0x00FF));

Wire.write((byte)(data & 0x00FF));

Wire.endTransmission(); }

La începutul programului vom defini adresa I2C a circuitului MAX17043:

#define MAX17043_ADDRESS 0x36

Iar în cadrul procedurii IoTpublish() vom adăuga:

float batVoltage;

float batPercentage;

String SENSOR_TOKEN3=”…”;

String SENSOR_TOKEN4=”…”;

….

qsMAX17043();

batPercentage = percentMAX17043();

batVoltage = (float) vcellMAX17043() * 1/800;

data = “http” + “://iot.robofun.ro/api/v1/senzor/” + SENSOR_TOKEN3 + “/input?value=” + String(batVoltage, DEC);

http.begin(data);

httpCode = http.GET();

#ifdef debug

if(httpCode > 0) {

Serial.printf(“[HTTP] GET… code: %d\n”, httpCode);

if(httpCode == HTTP_CODE_OK) {

String payload = http.getString();

Serial.println(payload);

}

} else {

Serial.printf(“[HTTP] GET… failed, error: %s\n”, http.errorToString(httpCode).c_str());

}

#endif

http.end();

data = “http” + “://iot.robofun.ro/api/v1/senzor/” + SENSOR_TOKEN4 + “/input?value=” + String(batPercentage, DEC);

http.begin(data);

httpCode = http.GET();

#ifdef debug

if(httpCode > 0) {

Serial.printf(“[HTTP] GET… code: %d\n”, httpCode);

if(httpCode == HTTP_CODE_OK) {

String payload = http.getString();

Serial.println(payload); }

} else {

Serial.printf(“[HTTP] GET… failed, error: %s\n”, http.errorToString(httpCode).c_str());   }

#endif

http.end();

Înainte de încărcarea noii versiuni de program este necesar să definim doi noi senzori (unul pentru tensiunea acumulatorului altul pentru procentul de încărcare) și să copiem cheile de autentificare în variabilele asociate: SENZOR_TOKEN3 și SENZOR_TOKEN4. După rularea programului vom putea vedea evoluția parametrilor acumulatorului folosit:

9

10