Una stazione meteo con ESP8266, sensore BME280 e schermo OLED SH1106

Costruire una piccola stazione meteo con Arduino è davvero semplice. Se poi vogliamo aggiungere la possibilità di comunicare con un servizio web, sia per scaricare che inviare dati, allora lo ESP8266 collegato alla rete WiFi diventa elemento fondamentale. Vediamo come realizzarlo.

 Quella della stazione meteo è una esperienza relativamente molto semplice per approcciarsi al mondo dei micro-controllori come Arduino. In questo caso però abbiamo bisogno di gestire anche dati in rete, quindi ci affidiamo alla basetta ESP8266 per raggiungere il nostro scopo.

Cosa ci occorre

– una basetta EPS8266. Se ne trovano a 6/7 euro su Amazon e una manciata di euro in meno su Aliexpress e Wish.  Fate attenzione se ha o meno i piedini dei pin già saldati o meno, nel caso ovviamente non siate in grado di fare con facilità saldature a stagno. Nelle foto viene usata una basetta di marca Amica v2, reperibile su un noto negozio di Amazon, molto affidabile e con pin già assemblati.

– Una breadboard. In foto c’è la versione ridotta da 400 buchi, ma la classica da 830 va benissimo, non occorre molto spazio.

– Un sensore BME/BPM 280. Costa approssimativamente 7/10 euro, lo trovate su Amazon e le solite app “cinesi”. Ho scelto questo sensore per la versatilità di misurazioni che effettua e la precisione discreta. L’alternativa sarebbe quella di acquistare tre sensori singoli per effettuare le stesse misurazioni. Rassegnatevi all’ide di dover saldare i 4 pin necessari, sensori così piccoli, difficilmente vengono venduti già saldati. Esistono anche BME e BMP separati: il secondo è più economico perché triangola l’umidità da tempertura e pressione, il primo la misura direttamente.

– Un display OLED. In commercio ce ne sono diversi, il più diffuso è lo SSD1306, semplice da utilizzare e con molte librerie, documentazione ed esempi già preconfezionati. Qui usiamo invece un più economico monocromatico e retroilluminato SH1106.

– 8 cavetti per breadboard maschio/maschio.

 L’alternativa è quella di comprare un kit con i componenti necessari già raccolti in una scatoletta con istruzioni magari cartacee o online. Di questi kit se ne trovano sempre più spesso come elementi educativi per la IoT. Occhio però che spesso sono inseriti sensori o componenti non perfettamente rispondenti alle specifiche ma vengono venduti secondo disponibilità e anche le istruzioni possono essere scritte in cinese o con siti web non di facile utilizzo.

Prepariamo l’IDE

Prepariamo prima l’ambiente di sviluppo ovvero il nostro IDE Arduino per riconoscere la scheda.

Selezioniamo la porta. Su linux la porta di default è la /dev/ttyUSB0. Su Windows, a second della versione che usate, potrebbe essere una tra le COM1, COM2 o la più probabile COM3. Selezionatela nella voce di menù Strumenti->Porta avendo cura di provare anche le altre se lo sketch desse errore in fasi di upload. In genere, su Windows, la porta viene riconosciuta automaticamente o compare una voce di fianco nella tendina che indica il nome del dispositivo.

Selezioniamo il dispositivo, ovvero scheda, corretto. Qui ci sono due possibilità: o usiamo la versione ESP8266 nuda e creda che fa uso di una architettura GPIO per la numerazione dei pin oppure ricorriamo ad una semplificazione con scheda NodeMCU 1.0 (ESP12E-Module) che nomina i pin con le lettere che sono stampate direttamente sulla basetta. Lasciamo ad altri post la spiegazione, vantaggi e svantaggi dei due approcci.

Per la nostra esperienza ci basta configurare al meglio il tutto in modo semplice. Usate la voce di menù sull’IDE Strumenti->Scheda e cercate nella tendina la voce indicata NodeMCU 1.0 (ESP12E-Module). Notiamo che alcune librerie aggiuntive sono spesso scritte per ragionare con la scheda ESP8266, altre volte con la NodeMCU: il discrimine si evidenzia in fase di compilazione poiché avremo un errore sul nome utilizzato per pilotare i pin. Se tra le schede non compare il nome indicato, niente paura, dobbiamo aggiungerlo da libreria. Andate quindi  Strumenti->Scheda->Gestore Schede e nella finestrella in alto a destra cercate ESP8266, vi comparirà un risultato ESP8266 by ESP8266 Community che, mentre scriviamo, è arrivata alla versione 2.6.3.

Assemblaggio

L’assemblaggio è piuttosto semplice. Inseriamo lo ESP8266 al centro della nostra breadboard. Cerchiamo di lasciare una o due file di buchi sul lato dei pin digitali D, dove andremo ad inserire i cavetti necessari. In foto vedete che le file a e b della breadboard sono lasciate libere mentre la scheda è schiacciata immediatamente sopra.

Colleghiamo poi l’alimentazione 3v3 (sulla breadboard A10 e B10 ) ai poli VCC e VIN dello schermo e del sensore. In foto i cavetti sono rossi. Stesso lavoro con i cavetti neri che vanno da GND della basetta (in foro A9 e B9) ai GND di sensore e schermo. Con i cavetti arancioni colleghiamo il pin D1 ai pin SCL di scheda e monitor, mentre con i cavetti verdi il pin D2 coni pin SDA dei due componenti. I colori dei cavetti sono a titolo esemplificativo per meglio “leggere” la nostra basetta di prova.

Tensione e terra potevano essere presi anche dal l’altro lato del nostro ESP8266. I componenti sono scelti appositamente con interfaccia IIC che minimizza il numero di collegamenti da  effettuare. Occhio se volete fare  la stessa esperienza su Arduino i pin standard sono i pin analogici A5 e A4, che qui su ESP vengono rimappati in modo un po’ caotico sui citati D1 e D2.

 Uno schema di massima realizzato con fritzing

Il codice

Per questo esperimento abbiamo deciso di scrivere il codice in c/c++ sfruttando gli esempi forniti dai manutentori delle librerie che stiamo andando ad utilizzare. Esistono codici esistenti per diverse tipologie di stazioni meteo analoghe alla nostra, ma nessuna che sfrutti le stesse componenti, ma si può far tesoro di quanto esistente e assemblarlo per i nostri scopi. Vediamo frammento per frammento cosa scrivere e perché. Tra le librerie, tralasciando le prime due standard, aggiungiamo la U8x8lib per il monitor e le adafruit per gestire il sensore. Dichiariamo globalmente la variabile bme del sensore e la u8x8 per lo schermo. I valori dello schermo sono presi da un elenco di tutti i monito compatibili con i nostri arduino. La define è suggerita dal manuale del sensore per impostare lo “zero” del sensore in riferimento al livello del mare. outstr è invece un buffer testuale che useremo più avanti.

#include <Wire.h>
#include <Arduino.h>

#include <U8x8lib.h>

#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>


#define SEALEVELPRESSURE_HPA (1013.25)

U8X8_SH1106_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE);
Adafruit_BME280 bme; // I2C

static char outstr[8];

Il setup del nostro software. Prima cosa da fare accendiamo/prepariamo il nostro schermo, quindi andiamo a sincronizzare il monitor seriale. Nel terzo blocco, controlliamo che il sensore sia attivo e diamo una serie di messaggi di errore che potete trovare tra gli esempi forniti dal produttore.

void setup() 
{
    u8x8.begin();
    u8x8.setPowerSave(0);
  
    Serial.begin(9600);
    while(!Serial);    // time to get serial running
    Serial.println(F("BME280 test"));

    unsigned status;
    
    status = bme.begin();  
    if (!status) {
        Serial.println("Non è stato trovato un sensore BME280 valido, controlla i filamenti, pin, modello del sensore!");
        Serial.print("SensorID è: 0x"); 
        Serial.println(bme.sensorID(),16);
        Serial.print("        ID di 0xFF probabilmente significa che il chip sia a BMP 180 o BMP 085\n");
        Serial.print("   ID 0x56-0x58 è il codice di un BMP 280,\n");
        Serial.print("        ID 0x60 è il codice di un BME 280.\n");
        Serial.print("        ID 0x61 è il codice di un BME 680.\n");
        while (1);
    }
}

 

Veniamo al loop. E’ molto semplice in quanto richiamiamo due semplici funzioni senza parametri che si limitano a leggere e stampare a video o console i valori. Un piccolo ritardo di un secondo per ripetere le misurazioni e stampe in tempi idonei.

void loop() 
{ 
   printSerialValues();
   printOLEDValues();

   delay(1000);
  
}

Vediamo finalmente la funzioncina che si occupa di scrivere sullo schermo. Il funzionamento è spartano ma efficace: si sceglie un font e si scrive un testo fornendo come parametro le coordinate x,y da dove si vuole far cominciare il testo. La coordinata (0,0) rappresenta il bordo in alto a sinistra.

Nel nostro codice non scriviamo solo testo ma anche i valori numerici in virgola. In c purtroppo le cose non sono esattamente agevoli ed abbiamo bisogno di una funzione che ci converta in modo spartano il numero in stringa e qui ci aiuta la dtostrf che prende una variabile decimale float, il numero totale di cifre da stampare della stessa e quante sono le cifre decimali, infine la variabile di buffer testuale su cui scrivere il valore desiderato da stampare.

dtostrf(float name, #digits, #decimalplaces, save it to buffer);

E’ relativamente semplice prelevare i valori dall’oggetto bme che ci siamo dichiarati globalmente.

void printOLEDValues() 
{
  u8x8.setFont(u8x8_font_chroma48medium8_r);
  u8x8.clearDisplay();
  u8x8.drawString(0,0,"Temp= ");
  //dtostrf(float name, #digits, #decimalplaces, save it to buffer);
  u8x8.drawString(6,0,dtostrf(bme.readTemperature(),3,1,outstr));
  u8x8.drawString(10,0," *C");

  u8x8.drawString(0,1,"BAR= ");
  u8x8.drawString(6,1,dtostrf(bme.readPressure() / 100.0F,3,1,outstr));
  u8x8.drawString(10,1," hPa");

  u8x8.drawString(0,2,"Altezza= ");
  u8x8.drawString(8,2,dtostrf(bme.readAltitude(SEALEVELPRESSURE_HPA),4,0,outstr));
  u8x8.drawString(12,2," m");

  u8x8.drawString(0,3,"Umidita'= ");
  u8x8.drawString(9,3,dtostrf(bme.readHumidity(),3,1,outstr));
  u8x8.drawString(13,3,"%");
    
  u8x8.refreshDisplay();    //solo per SSD1606/7  
}

 

La stampa sulla console seriale, non è obbligatoria, ma è sempre bene utilizzarla per testare i nostri sketch. Vi ricordo che potete aprila da Strumenti->Monitor seriale.

void printSerialValues() 
{
    Serial.print("Temperatura = ");
    Serial.print(bme.readTemperature());
    Serial.println(" *C");

    Serial.print("PRessione = ");

    Serial.print(bme.readPressure() / 100.0F);
    Serial.println(" hPa");

    Serial.print("Altitudine approssimata = ");
    Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
    Serial.println(" m");

    Serial.print("Umidità = ");
    Serial.print(bme.readHumidity());
    Serial.println(" %");

    Serial.println();
}

Ultima modifica 24 Maggio 2022