IFTTT Weather ESP8266

E’ giunto il momento di proseguire il con il progetto IFTTT Weather ESP8266 passando al codice che ti permette di recuperare le informazioni meteo da uno spreadsheet Google.

Nei precedenti articoli hai letto come configurare il servizio WU ( Weather Underground ) su IFTTT perché scrivesse in un tuo spreadsheet i dati meteo ad una data ora: IFTTT weather google spreadsheet

Successivamente hai letto come creare uno script google ( Google script ) che vada a recuperare le informazioni e te le restituisca in formato JSON o CSV: IFTTT weather google scripts

In questo articolo IFTTT Weather ESP8266 scriverai lo sketch che ti permette di collegarti via https al servizio Google e recuperare i dati nel formato che preferisci.

IFTTT Weather ESP8266

Inizia dalla libreria che ti permette di eseguire la chiamata al servizio: HTTPSRedirect in quanto la richiesta per un GS ( Google Script ) deve avvenire in HTTPs ed utilizza un sistema di callback decritto molto bene nel readme del progetto HTTPSRedirect:

Sec. II HTTPSRedirect Library API

in cui è necessario tener conto che sia in GET sia in POST la chiamata viene diretta, via GET, ad altri url prima di fornirti la risposta finale.

La libreria HTTPSRedirect ti semplifica di molto la strada per arrivare alla risposta finale senza preoccuparti di tali redirect a cui pensa lei stessa.

Nella documentazione della libreria trovi i principali comandi che puoi usare per eseguire la chiamata al tuo GS ed anche un esempio di Google Script che puoi usare per i tuoi progetti e da cui ho derivato quello che hai già visto nel precedente articolo IFTTT weather google scripts.

Download della HTTPSRedirect e installazione

Per usare il ptogetto IFTTT Weather ESP8266 devi scaricare dal sito github la libreria HTTPSRedirect, io ho usato il comando git clone:

# git clone git@github.com:electronicsguy/ESP8266.git HTTPSRedirect

dopo essermi posizionato nella dir delle librerie Arduino.

Ho quindi seguito le istruzioni presenti nel README.md ( Sez. III ) per generare il certificato fingerprint necessario alla libreria, ed allo sketch, per connettersi ed autenticare il sito di destinazione:

# echo | openssl s_client -connect script.google.com:443 | openssl x509 -fingerprint -noout |sed -e 's/:/ /g'

a cui ho aggiunto il comando “sed” per avere l’output formattato come ti serve per lo sketch.

otterrai qualcosa di simile alla seguente linea:

SHA1 Fingerprint=A5 D7 -- -- -- -- BF 7D FC 44 77 8B EE -- -- -- -- EA 50 --

dove ho sostituito con i “–” i valori originali per indurti a creare la tua fingerprint, non potendo utilizzare questa.

Url da chiamare

Nell’url del Google Script che dovrai chiamare è presente una chiave univoca che viene generata per ciascuno script:

Tale chiave puoi recuperarla in fase di Deploy del tuo script nell’Url della web app, come in figura sopra.

Lo sketch IFTTT Weather ESP8266

Ora che possiedi tutte le componenti passiamo allo sketch:

/*  HTTPS on ESP8266 with follow redirects, chunked encoding support
 *  Version 3.0
 *  Author: Sujay Phadke
 *  Github: @electronicsguy
 *  Copyright (C) 2018 Sujay Phadke <electronicsguy123@gmail.com>
 *  All rights reserved.
 *
 *  Example Arduino program
 */

#include <ESP8266WiFi.h>
#include "HTTPSRedirect.h"
#include "DebugMacros.h"

#include <ArduinoJson.h>

// Fill ssid and password with your network credentials

const char* ssid = "[il tuo SSID]";
const char* password = "[la tua Passord per WiFi]";

const char* host = "script.google.com";
// Replace with your own script id to make server side changes
const char *GScriptId = "AK***************************************3ZmOR4";

const int httpsPort = 443;

// echo | openssl s_client -connect script.google.com:443 |& openssl x509 -fingerprint -noout
const char* fingerprint = "A5 D7 -- -- -- -- BF 7D FC 44 77 8B EE -- -- -- -- EA 50 --";

// Read from Google Spreadsheet
String url = String("/macros/s/") + GScriptId + "/exec?readJson";

HTTPSRedirect* client = nullptr;
// used to store the values of free stack and heap
// before the HTTPSRedirect object is instantiated
// so that they can be written to Google sheets
// upon instantiation
unsigned int free_heap_before = 0;
unsigned int free_stack_before = 0;

StaticJsonDocument<200> jsonBuffer;
char json[800];

void setup() {
  Serial.begin(115200);
  Serial.flush();
  
  free_heap_before = ESP.getFreeHeap();
  free_stack_before = ESP.getFreeContStack();
  Serial.printf("Free heap: %u\n", free_heap_before);
  Serial.printf("Free stack: %u\n", free_stack_before);
  
  Serial.println();
  Serial.print("Connecting to wifi: ");
  Serial.println(ssid);
  // flush() is needed to print the above (connecting...) message reliably, 
  // in case the wireless connection doesn't go through
  Serial.flush();

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  // Use HTTPSRedirect class to create a new TLS connection
  client = new HTTPSRedirect(httpsPort);
  client->setInsecure();
  client->setPrintResponseBody(true);
  client->setContentTypeHeader("application/json");
  
  Serial.print("Connecting to ");
  Serial.println(host);

  // Try to connect for a maximum of 5 times
  bool flag = false;
  for (int i=0; i<5; i++){
    int retval = client->connect(host, httpsPort);
    if (retval == 1) {
       flag = true;
       break;
    }
    else
      Serial.println("Connection failed. Retrying...");
  }
  if (!flag){
    Serial.print("Could not connect to server: ");
    Serial.println(host);
    Serial.println("Exiting...");
    return;
  }

  if (client->setFingerprint(fingerprint)) { Serial.println("Certificate match."); } 
  else { 
    Serial.println("Certificate mis-match");
  }
  delete client;
  client = nullptr; 
}

void loop() {
  static int error_count = 0;
  static int connect_count = 0;
  const unsigned int MAX_CONNECT = 20;
  static bool flag = false;
  
  if (!flag){
    free_heap_before = ESP.getFreeHeap();
    free_stack_before = ESP.getFreeContStack();
    client = new HTTPSRedirect(httpsPort);
    client->setInsecure();
    flag = true;
    client->setPrintResponseBody(false);
    client->setContentTypeHeader("application/json");
  }
  
  if (client != nullptr){
    if (!client->connected()){
      client->connect(host, httpsPort);
    }
  }
  else{
    DPRINTLN("Error creating client object!");
    error_count = 5;
  }
  
  if (connect_count > MAX_CONNECT){
    //error_count = 5;
    connect_count = 0;
    flag = false;
    delete client;
    return;
  }

  Serial.println("GET Data from Google: " + String(url));
  if (client->GET(url, host)){
    String jsonString = client->getResponseBody();
    jsonString.toCharArray(json, jsonString.length());
    Serial.println("Halting: " + jsonString); 

    DeserializationError error = deserializeJson(jsonBuffer, json);
  
    if (error) {
      Serial.print(F("deserializeJson() failed: "));
      Serial.println(error.c_str());
      return;
    } else {
      const char* CheckTime =                jsonBuffer["CheckTime"];
      const char* Current =                  jsonBuffer["Current"];
      const char* CurrentCondition =         jsonBuffer["CurrentCondition"];
      const char* CurrentTempCelsius =       jsonBuffer["CurrentTempCelsius"];
      byte LowTempCelsius =           jsonBuffer["LowTempCelsius"];
      byte HighTempCelsius =          jsonBuffer["HighTempCelsius"];
      const char* WindDirection =            jsonBuffer["WindDirection"];
      byte WindSpeedMph =             jsonBuffer["WindSpeedMph"];
      //const char* PollenCount =              jsonBuffer["PollenCount"];
      const char* TodaysCondition =          jsonBuffer["TodaysCondition"];
      byte Humidity =                 jsonBuffer["Humidity"];
      byte UvIndex =                  jsonBuffer["UvIndex"];
      const char* SunriseAt =                jsonBuffer["SunriseAt"];
      const char* SunsetAt =                 jsonBuffer["SunsetAt"];
      //const char* ForecastUrl =              jsonBuffer["ForecastUrl"];
      //const char* CurrentConditionImageURL = jsonBuffer["CurrentConditionImageURL"];
      //const char* TodaysConditionImageURL =  jsonBuffer["TodaysConditionImageURL"];
      
      Serial.print(F("CheckTime: "));                Serial.println(CheckTime);
      Serial.print(F("Current: "));                  Serial.println(Current);
      Serial.print(F("CurrentCondition: "));         Serial.println(CurrentCondition);
      Serial.print(F("CurrentTempCelsius: "));       Serial.println(CurrentTempCelsius);
      Serial.print(F("LowTempCelsius: "));           Serial.println(LowTempCelsius);
      Serial.print(F("HighTempCelsius: "));          Serial.println(HighTempCelsius);
      Serial.print(F("WindDirection: "));            Serial.println(WindDirection);
      Serial.print(F("WindSpeedMph: "));             Serial.println(WindSpeedMph);
      //Serial.print(F("PollenCount: "));              Serial.println(PollenCount);
      Serial.print(F("TodaysCondition: "));          Serial.println(TodaysCondition);
      Serial.print(F("Humidity: "));                 Serial.println(Humidity);
      Serial.print(F("UvIndex: "));                  Serial.println(UvIndex);
      Serial.print(F("SunriseAt: "));                Serial.println(SunriseAt);
      Serial.print(F("SunsetAt: "));                 Serial.println(SunsetAt);
      //Serial.print(F("ForecastUrl: "));              Serial.println(ForecastUrl);
      //Serial.print(F("CurrentConditionImageURL: ")); Serial.println(CurrentConditionImageURL);
      //Serial.print(F("TodaysConditionImageURL: "));  Serial.println(TodaysConditionImageURL);
    }
    ++connect_count;
  }
  else{
    ++error_count;
    DPRINT("Error-count while connecting: ");
    DPRINTLN(error_count);
  }

  if (error_count > 3){
    Serial.println("Halting processor..."); 
    delete client;
    client = nullptr;
    Serial.printf("Final free heap: %u\n", ESP.getFreeHeap());
    Serial.printf("Final stack: %u\n", ESP.getFreeContStack());
    Serial.flush();
    ESP.deepSleep(5 * 1000000);
    
  }
  
  //delay(4000);                        
} // loop close

nelle prime linee includi le librerie necessarie alla connessione WiFi ed HTTPSRedirect, oltre alla Arduino Json che userai per il parsing della stringa restituita dallo script Google.

linee 19-20: inserisci le credenziali di accesso alla tua rete WiFi;

linea 22: definisci l’host da inserire nell’Url da chiamare per lo script Google;

linee 24-26-29: inserisci l’Id dello script, ricavato nel paragrafo precedente, la porta di connessione https ( 443 ) e la fingerprint del sito di destinazione.

linea 32: componi la stringa ( Url ) da chiamare concatenandoVi alla fine la chiamata exec?readJson in modo che lo script sappia quale metodo richiamare e cosa restituirti;

linea 34: definisci l’istanza della classe HTTPSRedirect denominata client;

linee 39-40: imposta le variabili free_heap_before e free_stack_before necessarie allo script Google;

linee 42-43: imposta l’istanza jsonBuffer ed la variabile char che conterrà il risultato della chiamata;

Nelle linee successive esegui la chiamata al sito google script come indicato nella libreria HTTPSRedirect e convalidi la risposta avendo cura di aver impostato le configurazioni corrette fino a questo punto.

Alla linea 142: definisci una striga al cui interno memorizzerai la risposta del client;

linee 143-144: converti in un array di tipo char il valore di stringa jsonString e popoli la variabile char scrivedo poi il risultato sul monitor seriale.

linea 146: deserializzi l’array json in modo che la libreria ArduinoJson si preoccupi di eseguire il parse dell’array e la corretta composizione dell’array jsonBuffer ( creato alla linea 42 );

linee 153-196: crei ed assegni in tempo reale i valori presenti nell’array json alle variabili corrispondenti e nelle linee successive 171-187 le scrivi sul monitor seriale.

Il restante codice dello sketch serve alla gestione dell’errore e provvede a controllare che in caso di errori lo sketch stesso si fermi e indichi all’ESP8266 di mettersi in sleep per:

5 * 1000000 = 5 secondi

essendo il timer dell’ESP 8266 per i il deepSleep in microsecondi ossia 10000000 di secondo.

Potrai utilizzare tutte le informazioni che hai ricevuto per controllare ciò che preferisci o per realizzare il tuo progetto di centralina con ESP8266.

Prima di inserire un commento, per favore, leggi il regolamento

Permanent link to this article: http://www.mauroalfieri.it/elettronica/ifttt-weather-esp8266.html

Lascia un commento

Your email address will not be published.

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.