Coviclock: Un calendario da tavolo che fornisce informazioni in tempo reale sul coronavirus (COVID-19) nella tua regione

I PCB di questo progetto sono stati offerti da PCB Way – il servizio di realizzazione PCB più completo che c’è: realizzano PCB fino a 30 layers! (in tutti i colori, compreso il viola!), PCB flessibili, stencil SMD, PCB con base in alluminio o rame (e altri materiali). Hanno un prezzo molto vantaggioso che parte da $5 per 10 PCB standard (1 o 2 layers fino a 100x100mm). PCBWay ha un sistema che premia con punti (beans) e la possibilità di inviare inviti che permettono di avere sconti e regali. Assistenza istantanea anche tramite chat, lavorazione velocissima, precisione e qualità ai massimi livelli. Leggerete poi al paragrafo PCB altre cose su questo fantastico servizio on line che ha il migliore rapporto qualità+assistenza/prezzo.

Vi consiglio di mettervi comodi per leggere perchè è tanta roba e alcune cose le spiego nei minimi dettagli e torneranno utili in mille altre occasioni o possono essere oggetto di discussione o basi per nuove idee ma anche per approfondire cose che credevamo di sapere bene.

Coviclock

Uso tantissimo i NodeMCU, che si trovano a poco prezzo e possono essere programmati mediante Arduino IDE in maniera abbastanza facile. Ho pubblicato tanti altri articoli con progetti utilizzanti questa scheda (andateli a leggere seguendo il link se non sapete come programmarli con Arduino IDE). La possibilità di avere un collegamento alla rete Wi-Fi ci permette di eseguire anche il download dei dati da server remoti piuttosto che residenti solo sulla stessa rete. Dal momento che stiamo vivendo una situazione di emergenza a causa del coronavirus e personalmente mi informo continuamente sull’andamento dell’epidemia da Covid-19 dal momento che ho bambini e persone malate in casa e cerco di tutelarli, ho pensato subito a questo progetto: un calendario da tavolo (cioè che illustra data e ora) con l’aggiunta di informazioni prese da internet, in particolar modo dati che illustrano l’andamento del contagio da Covid-19 nella mia regione in particolare. E quando parlo di regione, intendo regione italiana (Campania, Puglia, Lombardia ecc) e non lo stato come fanno altri progetti già presenti su internet.

So benissimo che ben presto questo progetto diverrà inutile dal momento che i dati prima o poi arriveranno a zero, ma rimarrà comunque una buona base di partenza per poter realizzare altri progetti che si prestano a fare le stesse cose (datario preciso, download di informazioni via internet per mostrarle su un display). Procediamo quindi con ordine dato che il progetto lato software ha un livello di complessità non semplicissimo e che mi ha fatto incontrare alcune difficoltà.

Gli Open Data

Mi tengo aggiornato (numericamente parlando) affidandomi alle cifre mostrate dal Ministero della Salute sulla pagina che hanno predisposto allo scopo. In particolare, da quella pagina si arriva ad un’altra che mostra una mappa con le statistiche del contagio. Dalla pagina con la mappa si risale ad un repository Github, chiaramente indicato nella pagina stessa, in cui il Dipartimento della Protezione Civile carica ogni giorno i dati aggiornati sul contagio intorno alle 18:00.

I dati sono disponibili a tutti (open data) con Licenza Creative Commons BY 4.0 e organizzati per Regioni e Province ed è possibile scaricarli in formato CSV o JSon. Questa è una cosa che apprezzo tantissimo e ho visto che in tanti stanno realizzando applicazioni per cellulare e siti internet che si aggiornano prendendo i dati proprio da questa pagina. Ho pensato di fare una cosa diversa portando questi dati su uno schermo a sè stante (dato che già sono fissato con queste cose: [1] [2] [3] [4] [5]). Per poter utilizzare i dati è necessario scaricarli in formato grezzo, ma cosa vuol dire? Se si accede alla pagina di Github cliccando sul file CSV, questo verrà renderizzato per renderlo più facilmente leggibile nel browser (Github, anche se è fatto per archiviare codice sorgente, fa così per molti tipi di files, ad esempio PDF, immagini ecc: non mostra il contenuto del file grezzo ma cerca di farlo visualizzare per quello che è) ma se sapete usare Github sapete che è possibile arrivare all’url in cui il dato viene mostrato “grezzo”, senza rendering (basta premere il pulsante raw che si trova in alto a destra nella pagina che visualizza il file): questa è la base di partenza per questa applicazione.

In particolare il mio progetto prende i dati da questa pagina in cui sono riportati i dati giorno per giorno di tutte le regioni (totale per tutte le province).

I singoli files CSV in questa pagina sono nominati secondo il pattern dpc-covid19-ita-regioni-AAAAMMGG.csv (anno a 4 cifre – mese a 2 cifre – giorno a 2 cifre) presentano, cioè, nel nome il giorno a cui sono relativi e in più c’è un file che porta l’indicazione latest al posto della data nel nome (dpc-covid19-ita-regioni-latest.csv) che replica il file dell’ultimo giorno di aggiornamento, e infine un file che in cui nel pattern nome manca la parte finale con la data (dpc-covid19-ita-regioni.csv) che riporta, in sequenza, i dati di tutti i giorni per tutte le regioni (fatto prendendo tutti i file precedenti e incollandoli uno dietro l’altro).

Nel mio caso utilizzo soltanto il file latest, cioè l’ultimo aggiornamento disponibile il cui nome non cambia mai, estraendo da esso soltanto la riga della regione che mi interessa (Campania nel mio caso, ma poi vedremo come potete modificare il sorgente per estrarre il dato della vostra: basta solo cambiare una stringa) per avere l’ultimo dato aggiornato della mia regione.

Le aggiunte che si potrebbero fare sarebbero tantissime: sarebbe ad esempio interessante eseguire la lettura anche del file regioni (quello con dentro tutti i giorni) per realizzare, ad esempio, un grafico con l’andamento giornaliero per una singola regione, in cui vengono riportati, in base al giorno, solo alcuni dati (es.: numero di nuovi contagi).

Funzionamento

Il Coviclock è costituito da un NodeMCU (modello Lolin), un display SPI a colori 240×320 ILI9341 da 2.8 pollici e una manciata di altri componenti. Il sistema per prima cosa, all’avvio si collega alla rete Wi-Fi impostata nel codice, dopodichè, condizione necessaria al funzionamento di tutto, si collega ad un server NTP (Network Time Protocol) per settare l’orologio interno in maniera precisa. Il collegamento al server NTP permette di scaricare l’orario in formato UTC e la libreria utilizzata permette di settare già da subito la differenza di orario (noi ci troviamo nella zone UTC+1). Un’altra routine controlla costantemente se bisogna o meno applicare l’orario DST (Daylight Saving Time, che noi in Italia chiamiamo Ora Legale) e quindi aggiungere un’ulteriore altra ora. Se ci sono errori di collegamento al server NTP (solo all’avvio), il sistema si ferma e non prosegue mostrandolo sul display. Successivamente si collega alla pagina di Github, tramite server sicuro, e scarica il file dal nome:

Se il file non viene trovato, ancora una volta il sistema si blocca indicandolo sullo schermo. Questo perchè potremmo avere sbagliato a scrivere il nome del file. In caso di errore di connessione a Github, ma solo per la prima volta all’avvio, il sistema viene anche stoppato. Se il file viene trovato, viene eseguita la lettura del CSV riga per riga, per evitare di scaricarlo tutto. Se nella riga è presente il nome della regione di interesse, impostato precedentemente in una stringa, la riga viene salvata in RAM e l’elaborazione viene interrotta. La riga viene quindi esplosa per recuperare i singoli campi.

Dopo aver settato orario e scaricato per la prima volta il file CSV, il sistema parte e sul display vengono indicati alcuni dati relativi al contagio, l’orario con la data e la temperatura ambiente presa da un sensore di temperatura analogico.

A regime, l’orario viene aggiornato costantemente una volta al giorno dopo la mezzanotte per fare in modo che non ci sia nessuno scarto di secondi e l’orario mostrato sia sempre preciso; in questo caso, se il collegamento al server NTP non va a buon fine, in automatico il sistema riprova dopo alcuni minuti e così via fino a che non riesce. Viene quindi controllata la data attuale per eseguire l’aggiornamento DST/no DST (ora legale/ora solare): una routine controlla se ci troviamo tra le 2 di notte dell’ultima domenica di Marzo e le 3 di notte dell’ultima domenica di Ottobre; in questo caso la routine restituisce un flag impostato a true che indica, appunto, che ci troviamo nel periodo di ora legale e quindi va aggiunta un’ora all’orario fornito dal server NTP.

Alle 18:00 (configurabile, ma è l’orario ottimale dato che il dato aggiornato viene sempre pubblicato tra le 18:00 e le 18:20 anche se nei dati ufficiali scrivono sempre 17:00), il sistema si collega al server di Github e scarica il CSV in ogni caso analizzandolo riga per riga per estrarre solo quella che ci interessa. Dopo aver ottenuto la riga coi dati della nostra regione viene controllato il campo dell’ultimo aggiornamento: se questo è uguale a quello dei dati già presenti e visualizzati sul display, viene impostato un ri-controllo dopo 10 minuti (e così via fino a che non viene trovato un dato aggiornato), altrimenti il display si aggiorna con i nuovi dati e viene emesso un Beep.

Per debug ho incluso dei messaggi sulla porta seriale, che potete controllare tenendo il NodeMCU collegato alla porta USB del PC e aprendo un software terminale impostato a 115200Baud, va bene anche quello che fornisce Arduino IDE stesso.

Collegamento ad un server sicuro

Il primo scoglio che ho dovuto superare è stato quello di un collegamento verso un server sicuro. Github ospita i files su una pagina a cui si accede tramite protocollo HTTPS. Questo vuol dire che non ci si collega al server mediante la porta 80 (http) ma mediante la 443. Nel momento in cui proviamo a collegarci in maniera classica (cioè su porta 80) su una pagina che invece viene servita tramite https, il server restituisce un messaggio di tipo HTTP 301 – Moved Permanently : questo errore sta ad indicare, la maggior parte delle volte, che quella pagina c’è ma si trova da un’altra parte; nella fattispecie è accessibile da una porta diversa da quella che stiamo utilizzando.

Errore HTTP 301

Utilizzando tutti i vari esempi su ESP8266 che fanno uso della libreria WiFiClient non otterremo mai nulla puntando a quella pagina perchè ci viene appunto restituito l’header HTTP 301 come detto prima:

Risposta dal server sicuro se si accede in modo non sicuro

Se si prova a cambiare la porta di comunicazione, da 80 a 443 capita invece che la comunicazione va a buon fine, ovvero l’Host accetta la connessione ma ci viene restituita una pagina completamente vuota, senza headers nè dati. Perchè? Proprio perchè la comunicazione è su server sicuro: il server fornisce una chiave di accesso (il certificato SSL) e il client deve accettarla fornendo un’impronta calcolata in base al certificato SSL fornito, detta FingerPrint: il server riceve quindi il FingerPrint e se questo è corretto vuol dire che il client ha accettato il certificato e l’autenticazione va a buon fine consentendo lo scambio di dati attraverso un canale criptato. La libreria WiFiClient dell’ESP8266, però, non supporta questa modalità di accesso, per cui è necessario utilizzare piuttosto la libreria WiFiClientSecure che possiede il metodo setFingerPrint il quale permette di specificare un’impronta. Tale metodo va richiamato prima di eseguire la connessione. Nel caso in cui il fingerprint sia sbagliato, la connessione non va a buon fine. 

Ottenere il FingerPrint

Il Fingerprint è una sequenza di bytes che viene calcolata con degli algoritmi in base alla chiave fornita dal certificato di sicurezza del sito. Nel momento in cui cambia il certificato di sicurezza del sito, il Fingerprint va ricalcolato, e questo si verifica sempre quando un certificato viene rinnovato.

Il Fingerprint da utilizzare con Github l’ho già incluso nel codice sorgente ma attualmente varrà fino al 03/06/2020 10/05/2022, data di scadenza del certificato attuale di Github, quindi vi sarà utile sapere come si fa ad ottenere.

La data di scadenza non è da tenere in considerazione in senso stretto: Il certificato precedente scadeva il 3 Giugno ma Github l’ha rinnovato il 5 Maggio, per cui da quella data, l’orologio non funzionava più perchè la connessione non andava a buon fine. Per cui, se vi succede la stessa cosa, date un occhio al certificato di sicurezza!

Uno dei tanti sistemi è quello di aprire la pagina a cui vogliamo accedere da Google Chrome. Nel nostro caso specifico dobbiamo andare sulla pagina raw di github (i certificati sono differenti tra raw.github.com e github.com) Premere insieme CTRL+SHIFT+C (su Windows, Linux e Chrome OS) o COMMAND+OPTION+C (su Mac) : si apre la finestra DevTools che permette di ispezionare il sito. Nel menù di questa finestra premere il tasto » per visualizzare le voci di menù nascoste e selezionare Security:

Compare la seguente finestra:

Premere il pulsante View Certificate.  Si apre la finestra con le proprietà del certificato di Sicurezza. Cliccare sulla scheda Dettagli e scorrere verso il basso fino a visualizzare l’ultima voce che si chiama Identificazione Personale:

Se ci clicchiamo sopra, nella textbox sottostante compare una serie di cifre in formato esadecimale: selezionare tutto e copiare. Questo è il fingerprint, unico per il sito che avete visualizzato, e da utilizzare con il metodo setFingerPrint della libreria WiFiClientSecure dell’ESP8266.

Estrazione dati

Il CSV che utilizzo, quello coi dati relativi alle regioni, possiede attualmente i seguenti campi:

   /* csvRowField[] array:
   * 
   * 0 data (esempio: 2020-03-25T17:00:00)
   * 1 stato
   * 2 codice_regione
   * 3 denominazione_regione
   * 4 lat
   * 5 long
   * 6 ricoverati_con_sintomi
   * 7 terapia_intensiva
   * 8 totale_ospedalizzati
   * 9 isolamento_domiciliare
   * 10 totale_positivi 
   * 11 variazione_totale_positivi (variato dal 30/03/2020 => era nuovi_attualmente_positivi)
   * 12 nuovi_positivi (aggiunto dal 30/03/2020, era il campo 11)
   * 13 dimessi_guariti
   * 14 deceduti
   * 15 totale_casi
   * 16 tamponi
   * 17 note_it (aggiunto dal 25/03/2020)
   * 18 note_en (aggiunto dal 25/03/2020)
   */

Come dicevo più in alto, leggo il CSV riga per riga e memorizzo solo la riga che contiene la stringa che ho definito nella costante char csvFind. Quando c’è match (controllato con il metodo .indexOf, che restituisce 0 se la stringa non c’è) i singoli campi vengono memorizzati in un array di String che si chiama csvRowField ed è costituito da CSV_nFIELDS (ovvero 19) elementi:

#define CSV_nFIELDS  19 // number of CSV Fields (columns)
String csvRowField[CSV_nFIELDS]; // separate fields of the row above

Il tipo String, per chi programma solo in C (esempio abituati solo ai microcontrollori PIC) è una cosa che appartiene al C++ : non è un tipo di dato (può essere facile confondersi tra un array di Char e String) bensì una classe, un oggetto. Quindi essendo una classe, ha molti metodi (ovvero quelle funzioni che scriviamo dopo un punto che segue il nome dell’oggetto).

Strutture CSV che cambiano

Gli ultimi due campi (le note) sono stati aggiunti solo dal 25 Marzo e questo, per me, è stato un altro motivo di impazzimento dal momento che ho cominciato a lavorare su questa cosa da ben prima e avevo previsto un array di soli 16 elementi. Il 25 Marzo accadde che l’estrazione andava avanti corrompendo la memoria RAM e causando malfunzionamenti di cui non riuscivo a venirne a capo perchè in effetti il codice era corretto e prima di allora funzionava tutto bene! Il problema era causato proprio da questi due campi in più, che facevano continuare la procedura di estrazione ma… non c’era spazio per gli ulteriori due elementi anche se erano vuoti. Nelle prime versioni del codice, infatti, eseguivo una scansione char per char della stringa per cercare i punti in cui erano presenti le virgole e la scansione andava avanti fino a fine stringa. Dal 30 Marzo poi, alcuni campi hanno cambiato significato: quello che prima indicava i nuovi positivi è diventato variazione totale positivi (e indica quindi la differenza tra il totale positivi di oggi e il totale positivi di ieri) ed è quindi stato aggiunto il campo dei nuovi positivi e i campi quindi da 18 sono diventati 19 e altri si sono invece spostati.

Adesso ho predisposto un array di 19 elementi ed esco dal ciclo quando ho contato 18 virgole (la diciottesima è l’ultima: 19 elementi sono separati da 18 virgole). In questo caso l’ultimo elemento conterrà tutta la parte di stringa dall’ultima virgola fino alla fine: se verranno aggiunti ulteriori campi in futuro, non ci saranno errori, ma l’ultimo elemento conterrà più roba del dovuto. Ad ogni modo, ad oggi, i due campi delle note appaiono sempre vuoti. Sul display non mostro tutti gli elementi estratti ma solo alcuni.

Per evitare problemi su files CSV che possono subire modifiche, sarebbe meglio utilizzare i dati formattati in JSON ma lascio ai lettori il divertimento.

Lettura temperatura

Sul display viene anche visualizzato il valore di temperatura della stanza in cui si trova l’orologio. Ho utilizzato un sensore analogico della Microchip che abbiamo già incontrato in queste pagine: l’MCP9700 per cui per ulteriori approfondimenti vi rimando all’articolo dedicato. Questo sensore legge temperature sotto lo zero senza bisogno di alimentazione negativa (ma non penso ce ne sia bisogno dato che il progetto è fatto per stare in casa, nemmeno se abitate in un igloo): ha un offset di 500mV (corrispondenti a 0°C) e fornisce in uscita 10mV per grado. Può essere alimentato anche a 3.3V, per cui ho preso l’alimentazione dal NodeMCU stesso da uno dei tanti pin con l’indicazione 3V e leggo l’uscita tramite l’unico ingresso analogico disponibile sull’ESP8266. L’ESP8266 nudo e crudo ha un ingresso analogico che tollera massimo 1V ma sui NodeMCU, sul pin contrassegnato con A0, come chiaramente visibile nello schema elettrico, è presente un partitore di tensione:

Dalla formula del partitore di tensione si ricava facilmente che, con R1=220KΩ e R2=100KΩ, abbiamo che la tensione sul pin analogico dell’ESP8266 varrà Vin*0.3125. È quindi possibile applicare su A0 del NodeMCU una tensione massima di 3.3V, che arriveranno come 1.03V sul pin analogico dell’ESP8266. L’MCP9700 arriva a leggere una temperatura fino a 150°C che corrispondono ad una uscita di 2V, quindi perfettamente utilizzabile con l’ingresso A0. Per avere la lettura più precisa possibile opero con i float, tanto memoria e potenza di elaborazione sull’ESP8266 ne abbiamo in abbondanza. L’ADC è a 10bit, vengono quindi restituiti valori da 0 a 1023 per tensioni lette da 0 a 1V sull’ADC => da 0 a 3.3V su A0.

Non utilizzo subito il valore restituito dalla prima lettura dell’ADC, ma eseguo 32 letture. Le letture non le eseguo subito una dietro l’altra per non bloccare il main, ma lascio che il contatore di misure aumenti di 1 ad ogni iterazione del loop e arrivati alla 32esima volta che il loop viene eseguito, faccio una media: in questo modo ho sicuramente un valore più stabile. Il risultato della media, che rappresenta ancora il valore numerico letto dall’ADC, lo memorizzo in una variabile float che ho chiamato temp e che assumerà man mano un significato diverso. Quindi dopo aver diviso per 32 (potevo fare uno shift a destra di 5 posizioni, ma tanto sto operando con i float per cui chissene) la prima operazione che faccio è risalire alla tensione letta in mV sull’ingresso analogico crudo, con una proporzione, ovvero: valore letto da ADC sta a 1024 come valore in mV sta a 1000mV:

temp=(temp/1024)*1000;

Qui sul fatto di dividere per 1023 o per 1024 ci sono due scuole di pensiero: il modulo ADC fornisce un valore da 0 a 1023, per cui si tratta di un range di 1024 valori e il numero 1024 non può essere restituito perchè 1024 è un valore a 11 bit, fin qui siamo tutti d’accordo. Chi divide per 1023 dice che si fa proprio perchè il valore 1023 è il massimo ammissibile e corrisponde a 1000mV. Chi divide per 1024 fa un altro tipo di ragionamento: l’uscita digitale è divisa in 1024 intervalli e se ti viene restituito, ad esempio, il valore 1023 non è detto che stai misurando proprio 1V (corrispondente al valore massimo in tensione che può essere letto in questo caso) ma hai in effetti che la tensione letta potrebbe oscillare tra 1V (compreso) e 0.9975 (non compreso) e il modulo ADC sia che sia 1V, sia che sia 0.998 (ad esempio) restituirebbe sempre il valore 1023: ineccepibile anche questo senza dubbio dal momento che la tensione analogica assume valori continui mentre l’uscita digitale è quantizzata. Gli intervalli si restringono man mano che la risoluzione in bit del modulo ADC aumenta: gli intervalli sono 1024 e quindi è giusto dividere per 1024: nel momento in cui il modulo ADC mi da zero, ancora un altro esempio, non è detto che il valore analogico sull’ingresso sia davvero 0mV ma potrebbe anche essere 0,97mV, un valore molto basso certamente, ma sicuramente maggiore di zero. Su questa cosa, comunque, ognuno la pensa a modo suo e non sto a discutere, io ho deciso di fare così perchè mi pare più corretto.

Dopo aver trovato il valore in mV sul pin analogico dell’ESP8266, risalgo al valore in mV in ingresso al partitore (cioè la tensione applicata su A0). Con le resistenze utilizzate dal NodeMCU, tenendo conto che siano ultra-precisissime (in realtà dovremmo misurarle), il rapporto (R2/R2+R1) vale 0.3125 come già visto più in alto, per cui dato che VOUT=VIN*0.3125, la tensione in uscita dal sensore ovvero applicata su A0 ovvero in ingresso al partitore (VIN) facendo la formula inversa vale:

temp=temp*0.09765625;

a questo valore sottraggo 500mV per riportarmi a zero (perchè, ricordo, quel sensore fornisce 500mV a 0°C) e divido quindi per 10mV/°C dal momento che 10mV=1°C. Il valore finale è la temperatura espressa in °C che verrà mostrata sul display utilizzando un solo decimale: la libreria utilizzata per il display supporta infatti la scrittura di numeri float con un numero a scelta di decimali.

Arduino usa una classe dedicata: print, che viene sfruttata da tutte le librerie per scrivere stringhe, numeri interi, float ecc. L’avrete già sicuramente incontrata, se usate Arduino, quando inviate cose sulla porta seriale e scrivete Serial.print o Serial.println (per aggiungere un ritorno a capo costituito da \r\n alla fine di un dato): ecco anche in questo caso, sul display, stiamo utilizzando la stessa, indentica funzione (metodo) che è contenuta, se siete curiosi come me, in C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino\Print.cpp su Windows.

Scrittura stringhe sul display

Sempre riguardo alla scrittura di stringhe/numeri sul display ci ho messo un po’ per capire un’altra funzionalità della libreria utilizzata. La libreria utilizza il metodo setTextColor per impostare il colore del testo (come è facilmente intuibile dal nome!). Inizialmente facevo una cosa così:

tft.setTextColor(ILI9341_WHITE); // imposta il colore bianco per la scrittura
tft.print(second()); // scrive il valore dei secondi

cosa succedeva facendo così? Che i secondi, man mano che andavano avanti, si accavallavano uno sull’altro: ovvero i pixel dei numeri visualizzati non venivano cancellati, bensì si sommavano. Spulciando il sorgente della libreria mi sono accorto che il metodo in questione ha due overloads:

void setTextColor(uint16_t c)
void setTextColor(uint16_t c, uint16_t bg)

Leggendo le note e il resto si capisce che quel metodo può anche essere richiamato specificando un secondo parametro che imposta il colore di sfondo (mettete nero se lo sfondo è nero, in modo che il numero precedente venga cancellato), mentre se viene richiamato con un solo parametro si assume che lo sfondo (ovvero: i pixel non accesi) sia trasparente e quindi si vede quello che c’era prima/sotto.

Una cosa semplice certo, ma è solo per farvi capire che spesso la soluzione è a portata di mano. Magari la documentazione in giro c’è: ho comunque visto sul sito della Adafruit e sul repo Github ma sta cosa non mi è venuta fuori subito, mentre leggendo il sorgente della libreria mi è saltato subito all’occhio e lo scrivo qui, magari torna utile ad altri alle prime prese con questa libreria.

Schema elettrico

Il display è collegato a 3 GPIO (Chip Select, Reset, Data/Command) che possono essere scelti a piacere + altri 2 che sono l’SPI hardware (Master Output – Slave Input e Clock) e che sull’ESP8266 si trovano sui pin GPIO13 e GPIO14  e sono quindi obbligati.

Lato software, la libreria utilizzata chiede, difatti, solo di specificare i 3 GPIO perchè gli altri 2 sono impliciti dato che viene utilizzato il modulo SPI hardware. Il display ha dei led per la retroilluminazione e li collego direttamente alla linea dei 3.3V: sullo schema ho previsto una resistenza (R1) per questioni sociali perchè mi capita sempre quello che mi scrive inorridito dicendo «eh ma la resistenzaaah?!1», così a prescindere anche quando è evidente che non ce ne sia bisogno: il posto della resistenza può essere occupato da un ponticello o da una resistenza da 0Ω, i fan della resistenza possono divertirsi a mettere quella che vogliono. Il display nella parte posteriore ha un jumper siglato J1 che permette di selezionare il funzionamento a 3.3V: non c’è bisogno di chiuderlo perchè alimentiamo già a 3.3V.

I due pulsanti S1 e S2 sono due switch tattili verticali da 6-7mm e sono collegati a due GPIO dotati di interrupt e resistenze di pullup integrate: attualmente non hanno funzione ma nel codice sono definiti i due vettori di interrupt collegati ad essi. La scheda l’ho difatti concepita come una scheda di sviluppo per NodeMCU+Display a colori per poter realizzare calendari da tavolo personalizzati e il coviclock è stata una scusa in più per terminare questo progetto. Il GPIO15 è un pin che ha di serie una resistenza di pull-down per consentire la programmazione e il normale boot del NodeMCU, per cui deve stare a GND durante l’avvio e l’ho quindi scelto per pilotare il gate di un piccolo mosfet che a sua volta aziona un cicalino di tipo auto-oscillante. Il cicalino è alimentato a 5V e attualmente nel codice del coviclock emette 3 beep in sequenza nel momento in cui il display viene aggiornato con nuovi dati.

Sono liberi i GPIO 1,2 e 3 a cui sono collegati rispettivamente l’RX dell’adattatore seriale (pin TX dell’ESP8266), il piccolo Led blu che si trova sul modulo ESP12F saldato sul NodeMCU, e il TX dell’adattatore seriale (pin RX dell’ESP8266). La linea TX (GPIO 1) attualmente è utilizzata per inviare messaggi di debug, volendo gli altri due GPIO possono essere utilizzati per altro tenendo conto che però sul 2 c’è il piccolo led blu che si accenderà quando sul pin transiterà un livello logico basso.

Il sensore di temperatura utilizzato, come già detto è un MCP9700. Se non volete alimentare la scheda dal connettore micro-usb a bordo del Node-MCU, è possibile alimentarlo con 5V sul connettore CON1, facendo attenzione a non invertire la polarità, nè a sbagliare la tensione perchè non ci sono protezioni. In questo caso bisogna spostare il jumper J1 per fare in modo che la linea dei 5V sul resto del circuito non sia più presa dalla porta USB ma dalla linea VIN.

Il Jumper J2, che è un jumper a saldare, serve per alimentare una zona millefori con la 5V o con la 3.3V a piacere, dato che sulla scheda, essendoci spazio dovuto al fatto che il display utilizzato è bello grande, ho incluso una zona millefori sulla quale si possono fare altri esperimenti.

Questo è l’elenco dei componenti:

  • R1: 0Ω ¼W o ponticello
  • R2: 10KΩ ¼W
  • C1: 100nF multistrato, passo 5mm
  • C2: 100nF multistrato, passo 5mm
  • Q1: BS170
  • SENS: MCP9700
  • U1: NodeMCU Lolin (con CH340G)
  • U2: Display SPI ILI9341 da 2.8 pollici
  • S1: pulsante tattile 6mm verticale
  • S2: pulsante tattile 6mm verticale
  • BUZZER: cicalino autooscillante (anche detto attivo)
  • CON1: a piacere, passo 2.54mm
  • J1: pinstrip maschio da 3pin, passo 2.54mm + Jumper

Il NodeMCU Lolin misura circa 31x57mm e ha bordo ha il convertitore seriale modello CH340G. Questo modello ha i pin GND1 e VUSB (VU sulla serigrafia) come indicato nello schema e che su altri modelli di NodeMCU sono invece indicati come reserved (RSV). In giro esistono tantissimi tipi di NodeMCU che si somigliano tutti: in particolare c’è un modello che a bordo ha il convertitore seriale CP2102: non l’ho mai usato e non penso che vada bene. Se volete utilizzare il mio PCB è assolutamente necessario predendere il modello indicato come Lolin. Ho preparato qui un’immagine per aiutarvi a distinguere questo modello specifico dagli altri:

Consiglio di prendere anche dei pinstrip femmina per poterli usare come zoccolo per il display. Il display in particolare deve essere del tipo SPI (perchè esistono anche in versione parallela) e da 2.8″ (perchè esistono anche da 2.4 e da 3.2). Per il display ho notato una cosa, dato che ne ho acquistati un paio da venditori diversi senza fare caso a quello che c’era scritto nell’inserzione:

I due display in foto sono perfettamente uguali come dimensioni e pinout ma differiscono nella parte cerchiata in giallo: in particolare quello di sopra ha un chip e quello di sotto no. Il chip in questione è un HR2046 / TSC2046 ed è un controller per il touch screen. Questo display, infatti, oltre ai collegamenti per il controller del display (ILI9341) ha affianco i collegamenti per il touch screen e in cima quelli per la scheda SD. Touch e SD non vengono utilizzati dal mio progetto, ma magari vi interessa acquistare questo display completo anche del touch per futuri esperimenti. Quando il controller per il touch non c’è, il display dovrebbe costare di meno perchè manca il controller e manca il touch screen e questa cosa dovrebbe essere chiaramente segnalata nell’inserzione. Io in realtà non mi sono accorto di questa dicitura: c’era anche se scritta in maniera microscopica e solo su una delle tante immagini a corredo dell’inserzione. E ho pagato il modello senza touch di più di quello completo!(!!) Quindi vi segnalo questa cosa, ripeto: totalmente inutile ai fini di questo progetto, nel caso foste interessati al display in questione per altre applicazioni.

PCB

Come dicevo ad inizio articolo, i PCB sono stati realizzati da PCB-Way in tempo record. La qualità è davvero alta e sono veramente soddisfatto del risultato e del servizio. Ho scelto il colore giallo e le serigrafie bianche sono davvero ben nitide, leggibili e senza zigrinature a differenza di come mi si è verificato in passato con altri servizi. Addirittura sono presenti le serigrafie lato saldature: a dire il vero non avevo la speranza che venissero realizzate perchè tutti i servizi che ho utilizzato in passato non prevedevano questa feature, invece con mia sorpresa ho anche la serigrafia dall’altro lato, che avevo comunque incluso nei gerbers e che illustra il piazzamento del display.

Il PCB, come anticipato, è concepito più come una scheda di sviluppo che per un progetto a sé stante. Le dimensioni sono dovute al fatto che ho scelto di utilizzare un display da 2.8″ a colori che è molto comune e diffuso e in aggiunta permettono di alloggiarlo in una struttura LEGO® da 10×15 studs come vedremo dopo nelle immagini. Il display viene ospitato nella parte posteriore (lato saldature), come ho anche indicato sulla serigrafia, e il resto dei componenti, tutti in formato through-hole, sul lato componenti. Conviene montare il display su uno zoccolo realizzato con pinstrip femmina, perchè se intendete fare modifiche, potete rimuovere il display e accedere al lato saldature.

Nella parte superiore, dato che c’era molto spazio, ho inserito un’area millefori che presenta a sinistra una serie di pad collegati a GND e e a destra un’altra serie che può essere collegata alla linea dei 5V o a quella dei 3.3V a seconda della selezione fatta sul jumper a saldare J2: questa cosa è indicata anche dalla serigrafia.

Ci sono due piccolissime aree millefori anche nella parte sinistra per poter collegare i GPIO rimasti liberi: GPIO1 e 3 più in alto e GPIO2 più in basso. I pin relativi possono essere messi in comunicazione coi vias adiacenti mediante una piazzola a saldare posta nel lato inferiore del PCB, frapponendo tra loro una goccia di stagno:

Sul lato inferiore ho messo le indicazioni TX/RX/D4 che erano più corte da scrivere, mentre per le linee 3.3V e GND potete seguire le piste o vedere l’immagine sopra. A sapere che sarebbero state realizzate le serigrafie anche lato saldature, avrei messo delle indicazioni più chiare anzichè realizzare le scritte col layer del rame, ma la prossima volta approfitterò di questo servizio.

Se siete interessati ad avere un PCB (SOLO PCB, senza componenti), potete scrivermi: ne ho da dare via alcuni con un piccolo contributo per pagare le spese del sito, ma vi chiedo soltanto di non scrivermi dicendo “non sono esperto / potresti vendermi i componenti / potresti farlo tu / sono alle prime armi / ecc”: scrivetemi solo ed esclusivamente se volete il PCB, sapete dove comprare i componenti, sapete come caricare e modificare il sorgente e sapete come assemblare il tutto senza nessun intervento da parte mia.

La prossima volta che dovete realizzare un PCB, se non avete provato mai il servizio fornito da PCBWay, provatelo e se non siete iscritti, potreste farlo utilizzando un mio invito.

Librerie

Per poter compilare coviclock è necessario installare le seguenti librerie in Arduino IDE (Strumenti->Gestione Librerie):

  • Adafruit_ILI9341.h (Adafruit ILI9341 by Adafruit) – ho usato la 1.5.4
  • Adafruit_GFX.h  (Adafruit GFX Library by Adafruit) – ho usato la 1.7.5
  • EasyNTPClient.h (EasyNTPClient by Harsha Alva) – ho usato la 1.1.0
  • TimeLib.h (Time by Michael Margolis) – ho usato la 1.5.0

Dovete chiaramente avere installata anche la toolchain di sviluppo per ESP8266, chiamata ESP8266 Core for Arduino IDE, se non l’avete ancora fatto e non sapete come fare, ho scritto un articolo tempo fa. Personalmente utilizzo ancora una versione vecchia, la 2.5.0, nel momento in cui scrivo c’è la 2.6.3. Un amico che ha fatto un’installazione fresca di Arduino IDE, con l’ultima versione del core per ESP8266, non ha trovato problemi e funziona tutto correttamente.

Setup software

Se volete utilizzare il mio codice, ci sono alcune modifiche da fare per poterlo adattare alle vostre esigenze.

Parametri rete

Dovete cambiare le due variabili:

const char* ssid="[YOUR SSID]";
const char* password="[YOUR SSID PASSWORD]";

mettendo, rispettivamente, il nome e la password della vostra rete wifi.

Indirizzo IP

Normalmente utilizzo un indirizzo IP statico. Se anche a voi piace utilizzare un indirizzo IP statico, dovete accertarvi che la riga 

#define USE_STATIC_IP // comment for using DHCP or if you have errors on NTP server connection

non abbia il commento, e quindi cambiare gli indirizzi IP in questa parte:

IPAddress deviceIP(192, 168, 1, 166); // ESP8266 static address
IPAddress gateway(192, 168, 1, 1); // router address
IPAddress subnet(255, 255, 255, 0); // subnet mask
IPAddress dns1 (8, 8, 8, 8); // first DNS, required for easyntp with static ip (https://github.com/aharshac/EasyNTPClient/issues/4)
IPAddress dns2 (8, 8, 4, 4); // second DNS, required for easyntp with static ip

In particolare la libreria EasyNTP pare abbia dei problemi di funzionamento quando si utilizza un IP statico (issue 4) e si risolve specificando anche gli indirizzi IP del DNS, che nel mio esempio sono impostati entrambi a quelli forniti da Google. Una volta su 1000, però, mi è capitato che il collegamento al server NTP non sia andato a buon fine: se capita anche a voi, sapete già come fare: commentate la riga USE_STATIC_IP in modo che il sistema utilizzi il DHCP, l’indirizzo IP alla scheda verrà assegnato dal vostro router e non ci saranno problemi di collegamento.

Impostazione regione

La variabile da cambiare è la seguente:

const char* csvFind="Campania"; // change this for download data of your region, according the name as written in the CSV

Al posto di Campania, dovrete scrivere il nome della vostra regione esattamente così come è scritto nel CSV, l’elenco dei nomi delle regioni si trova nel codice proprio al di sotto di questa variabile. Avrei potuto utilizzare il codice regione eseguendo la ricerca di una stringa del tipo ITA-virgola-codice: nel CSV difatti non avrebbe senso cercare solo il codice regione o il codice seguito/preceduto da virgola dato che quel numero si potrebbe trovare anche come valore in un campo. Non ho usato questo metodo perchè per il Trentino Alto Adige, che ha codice regione 04, i dati sono riportati separatamente per le provice autonome di Trento e di Bolzano e il codice regione è lo stesso.

Fingerprint

Quello che c’è, come già detto sopra, varrà fino a Giugno, nella speranza che non si arrivi a monitorare il Corona Virus fino a quel mese. In caso contrario, sapete come ricavarlo e cambiarlo.

Altri parametri

#define USESECURECLIENT // comment if client is on http, uncomment if https
#define CSV_UPDATE_HOUR 18 // CSV is usually updated first than that time (6:00PM) - don't use AM/PM format!
#define CSV_RETRY_MINUTES 10 // if CSV is not updated at defined hour, I'll retry after those minutes

Il primo define fa in modo da utilizzare il client sicuro e quindi porta 443 e metodo setFingerPrint. Nel caso in cui utilizzate il codice per collegarvi ad altri CSV in rete per altre cose, potete commentare il define in oggetto, commentare #include <WiFiClientSecure.h> in cima al codice e decommentare #include <WiFiClient.h>.

CSV_UPDATE_HOUR definisce l’orario (le 6 di pomeriggio) a cui cominciare a cercare. Anche se sul sito della protezione civile scrivono sempre “aggiornato alle 17:00”, il CSV non viene mai aggiornato prima delle 18:00.

CSV_RETRY_MINUTES imposta dopo quanti minuti tornare a cercare se la prima ricerca non ha restituito niente/nessun file aggiornato. Non esagerate riducendo i tempi per non ingolfare servizi che ci vengono dati a titolo gratuito, altrimenti gli IP vengono bloccati nel migliore dei casi.

Fotografie

Come dicevo sopra, il PCB, dato che le dimensioni c’erano, l’ho allungato di pochi mm giusto per farlo entrare in una struttura realizzata con i LEGO®. Sono anche le dimensioni del display che si prestano ad essere richiuso perfettamente in una cornice senza lasciare bordi o chiudere parti visibili. Nelle foto potete vedere la qualità del PCB e la struttura realizzata con i mattoncini. Nell’ultima foto vedete collegato un tester USB che ho messo per controllare l’assorbimento del dispositivo.

Licenza

Per questo progetto, nella sua interezza (schema, PCB, sorgente) ho scelto la licenza Creative Commons BY-SA-NC 4.0, ovvero in parole molto povere:

Si è liberi di condividere il progetto e farne modifiche a patto che (detto brevemente):

  • Sia indicato l’autore originale ed indicate chiaramente le eventuali modifiche fatte
  • Il progetto o qualsiasi sua parte non siano utilizzati a scopi commerciali (non ve lo potete vendere, ne nella sua interezza, ne in una sua parte)
  • La condivisione deve essere fatta allo stesso modo di come l’ho fatta io (stessa licenza)

Il testo completo della licenza è disponibile qui. Chiaramente non c’è nessun tipo di garanzia nè responsabilità: il progetto è fornito a titolo puramente didattico.

Sono pienamente consapevole che questa licenza, purtroppo, non è Open Source e che le Creative Commons non sono nemmeno adatte al Software, ma ho perso la fiducia in molti italiani smanettoni della domenica nel momento in cui ho visto alcuni miei progetti trasformati e messi in vendita senza mai aver ricevuto nessuna richiesta, per non tenere conto degli elementi che li condividono come propri per fare le raccolte a punti su siti che avallano questi sistemi. Non esistendo licenze software adatte a specificare che non è ammesso l’utilizzo commerciale (si, esiste la Commons Clause ma è da applicare insieme ad una licenza Open e pare che non tutti la considerano valida), ho scelto questa che stia bene o meno, tanto siamo qui per sperimentare e divertirci.

Download

Schema e sorgente li ho messi su Github: preferisco che stiano qui in modo tale che, se faccio modifiche, è più semplice per tutti avere accesso alle ultime versioni disponibili:

Se questo articolo ti è piaciuto, condividilo su un social:
  • 63
  •  
  •  
  •  
  •  
  •  
Se l'articolo ti è piaciuto o ti è stato utile, potresti dedicare un minuto a leggere questa pagina, dove ho elencato alcune cose che potrebbero farmi contento? Grazie :)