4 ottobre 2018

tutorial: come costruire una stazione meteo casalinga con raspberry pi


Benvenuti!
Questo tutorial vi fornirà una guida su come costruire una piccola stazione meteorologica in casa che tenga monitorate temperatura ed umidità e che mostri in tempo reale i valori all'utente, con anche la possibilità di visualizzare l'andamento delle misurazioni su base temporale. Il tutto sfruttando un Raspberry Pi ed un sensore digitale di temperatura+umidità.

Nota: il presente articolo contiene una infarinatura generale ed alcuni dettagli sono stati tralasciati. Se volete approfondire, non esitate a lasciare un commento o a contattarmi sui canali social.

Prima di addentrarci, vi riporto il video di supporto a questa guida che ho pubblicato sul mio canale YouTube:


Inoltre vi segnalo che nella pagina dedicata troverete tutti i tutorial da me pubblicati.


1. l'attrezzatura minima per iniziare


Per creare la nostra stazione meteo occorrono:
  • un Raspberry Pi qualsiasi (in questo tutorial viene utilizzato il modello Zero) + una microSD + un alimentatore (da almeno 2.1A)
  • Raspbian OS Lite
  • un PC per accedere via SSH al pi e sviluppare
  • un router a cui connettere pi e dispositivi che si vogliono utilizzare per accedere da remoto alla dashboard
Per quanto riguarda il primo punto non occorre altro: il Raspberry Pi verrà acceduto via SSH per la configurazione generale. Sarà invece indispensabile avere un alimentatore che eroghi in uscita almeno 2.1A.
Raspbian OS Lite è probabilmente la scelta migliore poiché è un sistema leggero, consuma poche risorse ed ha la possibilità di farci installare dai repository tutto il necessario.
Il PC sarà lo strumento con cui interagiremo da remoto con il pi, quindi un router sarà necessario per interrogare il pi ed ottenere le misurazioni (il pi, invece, non deve essere connesso a nulla una volta avviato il processo di misurazione di temperatura e umidità).


2. l'idea e gli attori in gioco


L'idea alla base di questo progetto è quella di creare una dashboard grafica in cui vengono mostrate le rilevazioni di temperatura ed umidità effettuate in un luogo (es: una stanza di casa) nel corso del tempo. Inoltre, l'obiettivo è anche quello di spiegare che creare da zero un sistema del genere non sia una cosa completamente banale come potrebbe invece apparire utilizzando piattaforme e sistemi già pronti all'uso out-of-the-box (come Bolt, di cui ho parlato in un focus dedicato).

Che cos'è e che cosa deve fare una stazione meteorologica? Essa è composta da diversi attori:
  1. uno o più sensori che rilevano i dati: il RILEVATORE
  2. un dispositivo a cui i sensori sono collegati (in questo caso un raspberry pi) che sa interpretarli e li memorizza per usi futuri: il MEMORIZZATORE
  3. un tool che sia in grado di recuperare i dati memorizzati e fornirli a chi li richiede: il GESTORE
  4. infine, qualcuno che sia interessato a leggere queste informazioni e che quindi esse possano essergli mostrate in modo utile: l'UTILIZZATORE
Ognuna di queste figure non è da sottovalutare poiché ci sono dettagli da specificare con cura...


2.1 il RILEVATORE


Iniziando dal primo attore, si può utilizzare un unico sensore in grado di catturare i dati relativi sia alla temperatura che all'umidità.

Nota: ovviamente si potrebbero aggiungere innumerevoli altre variabili, come: pressione atmosferica, velocità del vento, servizi via rest API per le previsioni meteo, e quant'altro... Lo scopo di questo tutorial, però, è quello di creare un punto di inizio, la cattura di dati locali, e poi magari in futuro si potrà eventualmente ampliare il tutto aggiungendo nuovi sensori e/o servizi desiderati.


Come catturare temperatura ed umidità? Sfruttando un sensore DHT11 o DHT22!
Di tali sensori ne esistono alcune varianti, quelle più diffuse hanno 3 o 4 PIN e, a seconda di tale fattore, la connessione tra sensore e raspberry pi può richiedere o meno la presenza di resistenze. Nella versione a 3 PIN che è quella utilizzata in questa guida non è richiesto alcun resistore.

Il primo PIN (a sinistra) è dedicato al segnale catturato, il secondo PIN (centrale) alimenta a 5V il sensore stesso, ed infine il terzo PIN (a destra) è il ground che chiude il circuito. Questo componente può essere collegato direttamente al raspberry pi ma, anche con la previsione di aggiungere nuove componenti in futuro, può aver senso passare per una breadboard.


Il PIN centrale andrà collegato con un PIN a 5V, il PIN a destra con un relativo PIN ground, ed il PIN a sinistra per il segnale con un qualsiasi altro PIN dati (ad esempio il numero 4) del pi.
Ciò rappresenta fisicamente il primo attore: cioè lo strumento che cattura i dati. Ovviamente questa è solo la parte hardware: occorre il software di controllo. E per questo ci viene in aiuto una libreria rilasciata da Adafruit (Adafruit_DHT) che consente proprio di gestire i segnali inviati da sensori del genere. Tale libreria è compatibile con tutti i modelli (4 o 3 PIN) del sensore e funziona sia con python 2 che 3, quindi è di sicuro un ottimo punto di partenza!

La libreria può essere facilmente prelevata con il comando sudo pip install Adafruit_DHT che si occuperà di download ed installazione automatica.

Nota: se non avete pip, potete facilmente prelevarlo dai repository di Raspbian OS Lite tramite il comando sudo apt-get install python-pip ed ovviamente se volete utilizzare python3 dovrete scaricare pip3.

Una volta scaricata la libreria, utilizzarla sarà un gioco da ragazzi: sarà sufficiente importarla nel nostro codice python ed associare a due variabili (la prima per l'umidità e la seconda per la temperatura) il risultato della funzione Adafruit_DHT.read_retry(11, 4) dove 11 è il modello del sensore (11 o 22) e 4 è il PIN a cui avete collegato tale sensore.

# esempio di codice che legge i dati dal sensore DHT11 su PIN 4
import sys
import Adafruit_DHT

while True:
    hum, tmp = Adafruit_DHT.read_retry(11, 4)
    print 'Temperature: {0:0.1f} C | Humidity: {1:0.1f} %'.format(tmp, hum)


2.2 il MEMORIZZATORE


A questo punto entra in gioco il secondo attore: un sistema in grado di immagazzinare i dati misurati nel corso del tempo cosicché possano essere poi visualizzati. Visto che il sensore è collegato direttamente al raspberry pi, ha senso pensare che sia lo stesso dispositivo a tenere traccia delle rilevazioni di temperatura ed umidità effettuate. Per fare ciò si può utilizzare un database locale in cui archiviare il tutto e da cui, in futuro, estrapolare le informazioni.
Una buona soluzione è quella di adoperare mongodb poiché questo particolare database non relazionale (noSQL) memorizza i dati in oggetti JSON (cosa che ci tornerà particolarmente utile in seguito), ed essendo non relazionale è fondamentalmente un repository documentale in cui inserire dati senza preoccuparsi di gestirne formati e quant'altro.

Per installare mongodb su Raspbian OS Lite basta un semplice sudo apt-get install mongodb che si occuperà di scaricare e configurare tutto per noi.

Nota: mongodb permetterà di salvare senza problemi tutti i dati rilevati ed ovviamente anche di recuperarli, ma servirà necessariamente un orchestratore che sia in grado di essere richiamato per effettivamente richiedere il salvataggio dei dati sul database e, allo stesso tempo, capace di avvisare chi sta visualizzando questi dati che è presente una nuova rilevazione!


2.3 il GESTORE e l'UTILIZZATORE


Quanto considerato alla fine del precedente paragrafo ci porta a pensare a come realizzare questo tool di orchestrazione e come mostrare all'utente i dati rilevati nel tempo. L'idea è quella di utilizzare l'accoppiata node.js + socket.io (lato server, l'orchestratore) più una semplice web-app (lato client) per avere una dashboard visuale completa che mostri le rilevazioni effettuate sotto forma di grafico.

Per ottenere tutti gli strumenti sarà necessario lanciare il comando sudo apt-get install nodejs npm git che si occuperà di scaricare e configurare tutti e tre i tool. Ed una volta installato il tutto, potete clonare dal mio account github il progetto weather_station con il comando git clone https://github.com/geekonerd/weather_station che scaricherà il codice che ho preparato abbinato a questo tutorial.

Nota: il codice comprende già tutto al suo interno e nel prossimo paragrafo vi spiegherò passo passo il funzionamento generale.

Una volta clonato il progetto da github, potete entrare nella cartella weather_station e lanciare il comando npm install che si occuperà di scaricare sul raspberry pi tutte le librerie aggiuntive necessarie al funzionamento del codice. Tali librerie non sono altro che: per il GESTORE il driver per accedere a mongodb, socket.io, il framework express + ent, e per l'UTILIZZATORE varie librerie che analizzeremo tra poco. Una volta completato il download di tutto il necessario, con il comando npm start potrete avviere node.js e quindi l'applicazione di monitoraggio.
A questo punto da browser web su PC o smartphone/tablet collegato alla stessa rete a cui è connesso il raspberry pi, andando sull'indirizzo IP del pi con porta 8080 (es: http://192.168.1.2:8080) potrete visualizzare la dashboard con tutti i dati finora catturati dal sensore. Ovviamente al primo avvio essa sarà completamente vuota!


3. weather_station


Arrivati a questo punto tutti gli attori del progetto sono stati portati in campo: chi misura i dati (il sensore), chi li immagazzina (mongodb), chi è in grado di leggere e scrivere i dati rilevati (node.js + socketio) e chi li mostra all'utente (la dashboard). Aldilà di mongodb, però, che essendo un database è grosso modo trasparente per noi, come funzionano le altre 3 componenti?

  • Il GESTORE (node.js + socket.io) è il cuore di tutto ed il collante che dà un senso alla presenza degli altri attori. Esso è scritto in javascript e quello che fa è creare una nuova applicazione (un server web in ascolto sulla porta 8080) in grado di collegarsi a mongodb utilizzando un database chiamato weather_station con una sola collection di dati che conterrà tutte le misurazioni effettuate (sia umidità che temperatura).
    La prima cosa che viene eseguita all'avvio del server è accedere a mongodb e, se non esistono, creare il database weather_station e la collection per le misurazioni. Quindi rimanere in ascolto per eventuali richieste che possono arrivare.
    Le richieste possibili sono 3: start (quando un nuovo UTILIZZATORE si aggancia e cerca di capire se il database è attivo oppure no), insert (quando un RILEVATORE invia una nuova misurazione da inserire su db, misurazione composta da tempo + tipo di rilevazione + valore rilevato), ed infine find (quando un UTILIZZATORE chiede al server di trovare ed inviargli le misurazioni presenti sul database). In più, la insert quando eseguita non solo risponde a chi la ha richiamata con lo stato dell'operazione, ma avvisa con un evento inserted ogni altro UTILIZZATORE connesso al GESTORE che c'è un nuovo dato potenzialmente interessante da tenere in considerazioni (tra poco capiremo a cosa servirà). La find, invece, filtra le misurazioni recuperando e ordinando temporalmente solo quelle rilevate in un intervallo di interesse specificato dall'UTILIZZATORE che le sta richiedendo.

  • il RILEVATORE è una piccolissima applicazione python che sfrutta la libreria Adafruit_DHT (descritta nel paragrafo 2.1) per le rilevazioni e la libreria socketIO_client per dialogare con il GESTORE: sarà proprio grazie a questa seconda libreria che i dati di umidità e temperatura rilevati arriveranno al GESTORE il quale, come visto, li memorizzerà sul database. Questa seconda libreria va installata sempre tramite pip, con il comando sudo pip install socketIO_client ed una volta scaricata può essere utilizzata con semplicità:

    # esempio di codice che usa socketIO_client per inviare le misurazioni
    import sys
    import time
    import Adafruit_DHT
    from socketIO_client import SocketIO

    socketIO = SocketIO('localhost', 8080)

    while True:
        h, t = Adafruit_DHT.read_retry(11, 4)
        ts = int(time.time())
        print '{0} - Temp: {1:0.1f} C  Humidity: {2:0.1f} %'.format(ts, t, h)

        socketIO.emit('insert', {'time': ts, 'type': 'humidity', 'value': h})
        socketIO.emit('insert', {'time': ts, 'type': 'temperature', 'value': t})

        time.sleep(1800)


    Una volta importato SocketIO dalla libreria, a parte la connessione al server su localhost:8080, l'unico metodo da sfruttare è emit che scatena un evento insert (descritto nel punto relativo al GESTORE), evento a cui viene passato l'oggetto JSON con i tre attributi: tempo di rilevazione, tipo di rilevazione e valore rilevato dal sensore.
    Visto che non ha molto senso misurare temperatura ed umidità ogni pochi secondi (o almeno non in casa), il ciclo di cattura ed inserimento viene effettuato ogni 30 minuti (cioè 1800 secondi) così da avere un massimo di 48 rilevazioni giornaliere.

  • l'UTILIZZATORE è colui che si occupa di mostrare a video all'utente i dati rilevati sotto forma di grafico (quindi utili e leggibili all'occhio umano). Anche esso viene "servito" da node.js e consiste fondamentalmente in una somma di librerie più un pochino di logica per tenerle assieme. Nello specifico sono stati utilizzati: jQuery come collante generico, il client di socket.io che -come succede per il RILEVATORE- anche qui consente di parlare con il GESTORE, c'è bootstrap per la resa grafica ed infine l'accoppiata chart.js + moment.js per mostrare i dati sotto forma di grafico con date ed orari di rilevazione in base al nostro fuso orario.
    Tutta la web-app è all'interno della cartella public che node.js espone al browser web che richiama l'IP del raspberry pi sulla porta 8080 (come già visto nel paragrafo 2.1). Escludendo le librerie (bootstrap, chart, jQuery, moment e qualche istruzione CSS personalizzata) tutta la logica applicativa risiede nei file index.html e script.js.
    Il file index.html contiene lo scheletro della web-app: include tutte le librerie usate, è composto da una sezione superiore con i controlli che consentono di spostarsi indietro ed avanti nel tempo per leggere le misurazioni, ed ovviamente ha un'area dedicata al grafico che mostra l'andamento temporale delle rilevazioni.
    Il file script.js è, invece, l'effettivo client che tiene aggiornato in tempo reale il grafico (opzione che l'utente può o meno attivare) e gestisce le varie interazioni. Al caricamento della pagina esso lancia un evento di start (come descritto nel punto del GESTORE) dopodiché imposta tutti i parametri di configurazione come: l'intervallo temporale di interesse (impostato di default su mezza giornata, cioè 12 ore), le etichette da mostrare sul grafico con i relativi colori per distinguere umidità e temperatura, la configurazione generica di chart.js (nello specifico un grafico lineare con sulla X il tempo e sulla Y le misurazioni relative). All'interno del file javascript sono poi indicate tutte le azioni che l'utente può fare, cioè cercare le misurazioni in un intervallo specifico tramite l'evento find e muoversi avanti ed indietro nel tempo di quanto configurato con l'intervallo (cioè 12 ore o mezza giornata).
    Infine ci sono le funzioni che vengono automaticamente richiamate da socket.io quando il GESTORE emette il relativo evento: started è richiamata quando il GESTORE risponde all'evento start (appena ricevuto, il client istanzierà il grafico e richiederà i dati delle ultime 12 ore, esattamente ciò che fa anche quando l'utente clicca sul bottone RESET). L'evento inserted, invece, è rilanciato dal GESTORE ogni qual volta un RILEVATORE completerà l'inserimento di una nuova rilevazione sul database (se è attiva l'opzione real-time il grafico si auto aggiornerà richiedendo al GESTORE gli ultimi dati rilevati, mentre se l'opzione non è attiva l'evento sarà semplicemente scartato). Infine, l'evento found è eseguito quando una ricerca verso il GESTORE ha effettivamente restituito nuovi dati (quindi l'UTILIZZATORE li analizzerà ed inserirà nel grafico sostituendo quelli presenti, aggiornandolo).

Nota: il grafico risultante mostrerà sotto forma lineare tutte le rilevazioni effettuate nell'arco temporale specificato, con la possibilità di mostrare anche solo umidità o la temperatura rilevate. Chart.js può essere configurato in diversi modi per mostrare i dati e, chiaramente, tutta l'interfaccia e le operazioni che l'utente può compiere possono essere ampliate, modificate e quant'altro poiché arrivati fin qui abbiamo tutti gli attori funzionanti e attivi.

Nota: per verificare l'aggiornamento in tempo reale del grafico, è possibile alterare i tempi di misurazione sul RILEVATORE riducendo i secondi di attesa tra una rilevazione e l'altra: il GESTORE aggiungerà con più frequenza dati sul database e scatenerà più eventi inserted. A quel punto l'UTILIZZATORE reagirà in tempo reale sul browser web senza necessità di alcuna azione da parte dell'utente per mostrargli le ultime misurazioni effettuate. Accumulare rilevazioni molto vicine è comunque sconsigliato visto che la variazione di temperatura ed umidità in luoghi come le stanze di casa non è così frequente, per lo meno non in stagioni primaverili, estive o autunnali... Il discorso può cambiare in inverno con gli sbalzi di temperatura dovuti alle accensioni e spegnimenti dei termosifoni durante la giornata.


4. conclusioni ed ultime note


Quello descritto è un possibile approccio per la creazione da zero di una stazione meteorologica domestica, utile anche per capire la differenza che c'è nel creare da sé tutta l'infrastruttura invece che utilizzarne una già funzionante out-of-the-box. Per gli smanettoni è chiaramente molto più divertente progettarne una nuova piuttosto che usare API o wizard già pronti, ma non sempre ha senso reinventare la ruota...

Nota: al momento, poi, quanto realizzato non è esposto su internet, quindi non è possibile accedervi da remoto... Configurando il proprio router affinché ruoti le richieste dall'esterno verso il raspberry pi, impostando un dominio online e quant'altro (procedure ampiamente discusse in precedenti tutorial) è chiaramente possibile esporre tutto sul web. Come, allo stesso modo, è anche possibile separare la memorizzazione dei dati (mongodb) dalla rilevazione (i sensori) e dalla gestione + uso (node.js + webapp) magari predisponendo uno o più raspberry pi in più stanze/luoghi che spediscono le proprie rilevazioni verso un server centralizzato installato su un altro raspberry pi, e poi tali dati possano essere interrogati da un'interfaccia che a sua volta è fornita da un altro dispositivo ancora... Insomma l'architettura finale può essere "complicata" a piacere!

Il progetto può essere ampliato aggiungendo un display locale collegato direttamente al raspberry pi che mostri in tempo reale l'ultima rilevazione effettuata. E, come detto nei primi paragrafi, aggiungere ulteriori sensori per ampliare i possibili dati a disposizione della nostra stazione meteo (vento, pressione atmosferica, previsioni future e quant'altro).


Bene: come indicato ad inizio guida, alcuni aspetti sono stati lasciati in secondo piano, ma se siete interessati ad approfondirli non esitate a contattarmi nei commenti di questo tutorial o del video ad esso correlato, oppure sui canali social!

Spero che la guida vi sia stata utile. Al prossimo tutorial!

Nessun commento:

Posta un commento