orologio

Come realizzare un orologio a matrice LED con NodeMCU e un modulo RTC D3231

Ritorniamo a parlare di ESP8266 per realizzare un orologio a matrice LED, collegando alla nostra dev-board un modulo RTC D3231 che ci servirà per stampare su una matrtice LED 8×32 l’orario e la data corrente. Sul modulo a matrice stamperemo anche il nostro indirizzo URL, alimentando tutto sia a batteria che con un comunissimo alimentatore a 5V. Il progetto è stato poi completato con la realizzazione della scocca con la stampante 3D.

Progetto

Per questo progetto avevamo inizialmente pensato di adoperare un Arduino Nano, ma abbiamo optato per il NodeMCU solo perché avevamo già in nostro possesso sia la scheda stessa, che la batteria ricaricabile e il modulo di ricarica. Inoltre, la batteria stessa fornisca da sola la tensione necessaria alla dev-board per funzionare, inoltre è di piccole dimensioni e facile da collocare nella scocca. Non adopereremo la connessione Wi-Fi per rilevare l’orario, bensì ci affideremo al modulo RTC, in modo che la scheda sia autonoma dalla connessione a Internet.

Orologio

Componenti

Come già detto sopra, il NodeMCU con ESP8266 è la scheda utilizzata per questo progetto, che collegheremo alla matrice led 8×32, che gestirà i 256 LED rossi che compongono il dispositivo. Per calcolare il tempo ci serviremo del modulo RTC D3231, già visto in precedenza [LINK]. Oltre a queste componenti da programmare, abbiamo anche utilizzato una batteria LIPO da 3,7V a 2000mAh che verrà ricaricata da un modulo di ricarica TP4056 con porta Micro-USB. Il modulo fornirà corrente sia per la ricarica della batteria, che per il funzionamento dell’orologio; questo permetterà di alimentare l’intero sistema con un comunissimo alimentatore per smartphone o semplicemente con la batteria, qualora andasse via la corrente.

  • Orologio

Acquista componenti su Amazon:

Acquista componenti su Aliexpress:



Collegamenti

Partiamo collegando il NodeMCU alla matrice LED, collegando il DIN al pin D7, il CLK al pin D5 e il CS al pin D8, mentre il Vcc ai 3,3V e il GND al GND. Il modulo DS3231 utilizza l’interfaccia I2C, quindi colleghiamo i pin SCL e SDA del modulko RTC ai pin D1 e D2 del NodeMCU. Anche in questo caso, colleghiamo il Vcc ai 3,3V e il GND del modulo al GND della dev-board.

Orologio

Il collegamenti del modulo di ricarica sono gli stessi visti già in altri progetto. Sul modulo sono presenti delle indicazioni precise: dove indicato B+ e B- andranno saldati, rispettivamente, il polo positivo e negativo della batteria; mentre, dove troviamo la scritte OUT+ e OUT- ovviamente i 3,3V che arrivano al NodeMCU e il GND.

  • Orologio

Codice

Per programmare il nostro orologio a matrice LED (potete scaricare il codice a questo LINK) ci serviranno diverse librerie, alcune per il funzionamento dei moduli, altre per creare effetti visivi. A tal riguardo, la libreria MD_Parola [LINK] sarà utile per lo scrolling di testo modulare su display a matrice. Per gestire la matrice sarà necessaria la libreria MD_MAX72xx [LINK], mentre per la gestione del modulo RTC adoperiamo la libreria RTClib [LINK]. Da contorno fanno le librerie Wire e SPI, utili per utilizzare l’interfaccia I2C e SPI. Includiamo anche il file Font_Data.h, contenente i caratteri alfanumerici da adoperare sulla matrice. Proseguiamo creando gli oggetti per gestire il modulo RTC e l’orario.


#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>
#include "Font_Data.h"
#include "RTClib.h"
#include <Wire.h>

RTC_DS3231 rtc;
DateTime now;

Creiamo ora le variabili per la data e l’orario (ore, minuti, secondi, giorni, mesi e anni), definiamo pure il numero di matrici (4) e la tipologia. Indichiamo, poi, i pin a cui connettere il NodeMCU, l’oggetto P per la gestione della libreria MD_Parola ed una serie di parametri per la gestione degli effetti e delle dimensioni del testo. Non dimentichiamo di creare i simboli per i gradi Celsius e Fahrenheit.


int dd,mm,yyyy;
int h, m, s;

#define MAX_DEVICES 4
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW

//Pin NodeMCU ESP8266
#define CLK_PIN D5
#define DATA_PIN D7
#define CS_PIN D8

MD_Parola P = MD_Parola(HARDWARE_TYPE,CS_PIN, MAX_DEVICES);

#define SPEED_TIME 55
#define PAUSE_TIME 0

#define MAX_MESG 20

char szTime[9];
char szMesg[MAX_MESG+1] = "";

uint8_t degC[] = { 6, 3, 3, 56, 68, 68, 68 };
uint8_t degF[] = { 6, 3, 3, 124, 20, 20, 4 };



Una parte di codice sarà dedicata alla creazione di array per la gestione dei nomi dei giorni della settimana e dei mesi, in modo da associare una stringa ad ogni numero del mese o del giorno della settimana.


char *mon2str(uint8_t mon, char *psz, uint8_t len)

{
static const __FlashStringHelper* str[] =
{
F("Gen"), F("Feb"), F("Mar"), F("Apr"),
F("Mag"), F("Giu"), F("Lug"), F("Ago"),
F("Set"), F("Ott"), F("Nov"), F("Dic")
};

strncpy_P(psz, (const char PROGMEM *)str[mon-1], len);
psz[len] = '\0';

return(psz);
}

char *dow2str(uint8_t code, char *psz, uint8_t len)
{
static const __FlashStringHelper* str[] =
{
F("Domenica"), F("Lunedi"), F("Martedi"),
F("Mercoledi"), F("Giovedi"), F("Venerdi"),
F("Sabato"), F("Domenica")
};

strncpy_P(psz, (const char PROGMEM *)str[code-1], len);
psz[len] = '\0';

return(psz);
}

Per ottenere l’orario, vediamo la funzione getTime() con cui otteniamo i valori di ore, minuti e secondi, stampando su matrice l’orario, facendo lampeggiare il punto separatore tra ore e minuti.


void getTime(char *psz, bool f = true)
{
s = now.second();
m = now.minute();
h = now.hour();
sprintf(psz, "%02d%c%02d", h, (f ? '.' : ' '), m);
}

La funzione getDate() sarà utilizzata per ottenere giorno, mese e anno, stampando tutto su matrice nel formato Giorno/Mese/Anno, dove il mese è indicato per nome proprio.


void getDate(char *psz)
{
char szBuf[10];

dd=now.day();
mm=now.month();
yyyy=now.year();
sprintf(psz, "%d %s %04d",dd , mon2str(mm, szBuf, sizeof(szBuf)-1),(yyyy));
}



Passiamo ora al VOID SETUP, dove avviamo il modulo RTC, l’oggetto per la gestione degli effetti, settando subito l’orario e la data. Impostiamo pure il comportamento degli effetti, come ad esempio il movimento del testo da destra verso sinistra, la velocità di spostamento ed altro, già dichiarato precedentemente. La funzione termina con il completamento del settaggio dei simboli da adoperare per la misurazione della temperatura.


rtc.begin();
P.begin(2);
P.setInvert(false);

rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

P.setZone(0, MAX_DEVICES-4, MAX_DEVICES-1);

P.setZone(1, MAX_DEVICES-4, MAX_DEVICES-1);
P.displayZoneText(1, szTime, PA_CENTER, SPEED_TIME, PAUSE_TIME, PA_PRINT, PA_NO_EFFECT);

P.displayZoneText(0, szMesg, PA_CENTER, SPEED_TIME, 0,PA_PRINT , PA_NO_EFFECT);

 P.addChar('$', degC);
P.addChar('&', degF);

Nel VOID LOOP dichiariamo 3 variabili, una per la gestione del conteggio del tempo, una destinata alla funzione Switch(case), l’ultima per la funzione getTime(). Preleviamo poi i valori temporali e avviamo le animazioni su display.


static uint32_t lastTime = 0; 
static uint8_t display = 0; 
static bool flasher = false;

now = rtc.now();

P.displayAnimate();



Con la funzione Switch(case) iniziamo a stampare sulla matrice prima la temperatura in gradi Celsius e poi in Fahrenheit


if (P.getZoneStatus(0))
{
switch (display)
{
case 0: // Temperatura C
P.setPause(0,1000);
P.setTextEffect(0, PA_SCROLL_UP, PA_SCROLL_LEFT);
display++; 
dtostrf(rtc.getTemperature(), 3, 1, szMesg);
strcat(szMesg, "$");


break;

case 1: // Temperatura F
P.setTextEffect(0, PA_SCROLL_DOWN, PA_SCROLL_LEFT);
display++;
dtostrf((1.8 *rtc.getTemperature() )+32, 3, 1, szMesg);
strcat(szMesg, "&");

break;

Successivamente stampiamo l’orario, il giorno della settimana e la data completa.


case 2: // Orario

P.setFont(0, numeric7Seg);
P.setTextEffect(0, PA_PRINT, PA_NO_EFFECT);
P.setPause(0,0);

if (millis() - lastTime >= 1000)
{
lastTime = millis();
getTime(szMesg, flasher);
flasher = !flasher;
}
if(s==00&& s<=30){
display++;
P.setTextEffect(0, PA_CLOSING, PA_GROW_DOWN);
} 

break;

case 3: // Giorno della settimana

P.setFont(0,nullptr);
P.setTextEffect(0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
display++;
dow2str(now.dayOfTheWeek()+1, szMesg, MAX_MESG);

//dow2str(5, szMesg, MAX_MESG);
break;
case 4: // Calendario 
P.setTextEffect(0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
display++;
getDate(szMesg);
break;



Concludiamo facendo stampare un messaggio personalizzato e resettiamo il display.


case 5: // Messaggio
P.setTextEffect(0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
strcpy(szMesg, "www.fattelodasolo.it"); 
display=0;
break;
default: 
display = 0;
break;
}

P.displayReset(0); //reset

Ricordiamoci, prima di caricare il codice, di selezionare come scheda NodeMCU 0.9 (ESP-12) e selezionare la porta corretta. Una volta caricato lo sketch, la matrice LED stamperà tutte le informazioni raccolte.

Assemblaggio

Per realizzare la scocca con la stampante 3D potete scaricare da questo LINK i file STL. Abbiamo fissato tutto con colla a caldo e nastro biadesivo, mentre per avvitare il coperchio abbiamo usato viti da 2mm, in particolar modo quelle che vengono date in dotazione con i servo-motori SG90. Il tempo di realizzazione della scocca dipende da stampante a stampante, noi abbiamo comunque impiegato circa 5 ore, stampando entrambi i file con PLA Bianca. Abbiamo deciso di saldare i cavi direttamente sul NodeMCU e sulla matrice per evitare i disturbi creati dai connettori dei jumper. Parlando proprio del NodeMCU, abbiamo isolato tutti i pin con la colla a caldo e poi usato il nastro biadesivo per fissarlo sulla batteria, scelta non felice ma funzionale.

  • orologio