🚀 Introduzione
Vuoi realizzare un orologio digitale preciso che continui a mantenere data e ora anche quando Arduino viene spento? ⏰
In questo progetto utilizzerai il modulo RTC DS3231, un orologio in tempo reale dotato di batteria tampone.
Arduino leggerà ore, minuti, secondi e data dal modulo RTC, visualizzando tutte le informazioni su un display OLED.
👉 Il DS3231 è molto più preciso rispetto al semplice conteggio del tempo con millis() ed è ideale per sveglie, timer, centraline e automazioni programmate.
Vuoi realizzare un orologio digitale preciso che continui a mantenere data e ora anche quando Arduino viene spento? ⏰
In questo progetto utilizzerai il modulo RTC DS3231, un orologio in tempo reale dotato di batteria tampone.
Arduino leggerà ore, minuti, secondi e data dal modulo RTC, visualizzando tutte le informazioni su un display OLED.
👉 Il DS3231 è molto più preciso rispetto al semplice conteggio del tempo con millis() ed è ideale per sveglie, timer, centraline e automazioni programmate.
🎯 Cosa realizzerai
Con Arduino creerai un sistema che:
Con Arduino creerai un sistema che:
- ⏰ mostra ore, minuti e secondi
- 📅 visualizza giorno, mese e anno
- 📆 indica il giorno della settimana
- 📟 utilizza un display OLED SSD1306
- 🔋 mantiene l’ora anche quando Arduino è spento
- ⚠️ controlla se il modulo RTC ha perso l’alimentazione
- 🔄 aggiorna automaticamente la schermata
- 🕒 permette di impostare data e ora dal codice
🧰 Materiale necessario
- 1x Arduino UNO
- 1x modulo RTC DS3231
- 1x batteria CR2032 per il modulo RTC
- 1x display OLED 0,96 pollici I2C SSD1306
- cavetti jumper
- breadboard
- cavo USB
🛒 Non hai ancora i componenti?
Molti dei progetti presenti su progettiarduino.com utilizzano componenti elettronici di base come LED, resistenze, sensori e breadboard.
Se stai iniziando con Arduino, il modo più semplice è utilizzare un kit completo per principianti, che include quasi tutti i componenti necessari per realizzare molti dei progetti presenti sul sito.
👉 Kit Arduino consigliato:
ℹ️ Link di affiliazione Amazon: in qualità di Affiliato Amazon ricevo un guadagno dagli acquisti idonei, senza alcun costo aggiuntivo per te.
https://amzn.to/4b3dxjW
📘 Vuoi imparare Arduino passo dopo passo?
Se vuoi approfondire e realizzare progetti più completi, puoi dare un'occhiata anche ai miei libri dedicati ad Arduino.
All'interno troverai spiegazioni semplici, schemi chiari e molti progetti pratici pensati per chi parte da zero.
👉 Scopri i miei libri su Arduino:
ℹ️ Link di affiliazione Amazon: in qualità di Affiliato Amazon ricevo un guadagno dagli acquisti idonei, senza alcun costo aggiuntivo per te.
https://amzn.to/4rfzjXE
Molti dei progetti presenti su progettiarduino.com utilizzano componenti elettronici di base come LED, resistenze, sensori e breadboard.
Se stai iniziando con Arduino, il modo più semplice è utilizzare un kit completo per principianti, che include quasi tutti i componenti necessari per realizzare molti dei progetti presenti sul sito.
👉 Kit Arduino consigliato:
ℹ️ Link di affiliazione Amazon: in qualità di Affiliato Amazon ricevo un guadagno dagli acquisti idonei, senza alcun costo aggiuntivo per te.
https://amzn.to/4b3dxjW
📘 Vuoi imparare Arduino passo dopo passo?
Se vuoi approfondire e realizzare progetti più completi, puoi dare un'occhiata anche ai miei libri dedicati ad Arduino.
All'interno troverai spiegazioni semplici, schemi chiari e molti progetti pratici pensati per chi parte da zero.
👉 Scopri i miei libri su Arduino:
ℹ️ Link di affiliazione Amazon: in qualità di Affiliato Amazon ricevo un guadagno dagli acquisti idonei, senza alcun costo aggiuntivo per te.
https://amzn.to/4rfzjXE
📚 Librerie necessarie
Per realizzare il progetto devi installare:
Per installarle:
Per realizzare il progetto devi installare:
- RTClib
- Adafruit SSD1306
- Adafruit GFX Library
Per installarle:
- apri Arduino IDE;
- vai su Sketch → Include Library → Manage Libraries;
- cerca RTClib;
- installa la libreria sviluppata da Adafruit;
- cerca Adafruit SSD1306;
- installa la libreria;
- installa anche Adafruit GFX Library se non è già presente.
🔌 Schema di collegamento
Display OLED SSD1306
Modulo RTC DS3231
Questo è possibile perché i dispositivi I2C utilizzano indirizzi differenti:
Batteria tampone
Inserisci una batteria CR2032 nel portabatteria del modulo DS3231, rispettando la polarità indicata
La batteria alimenta soltanto l’orologio interno quando Arduino viene spento.
👉 Senza batteria tampone, il modulo potrebbe perdere data e ora quando viene scollegata l’alimentazione principale.
Display OLED SSD1306
- VCC → 5V
- GND → GND
- SDA → A4
- SCL → A5
Modulo RTC DS3231
- VCC → 5V
- GND → GND
- SDA → A4
- SCL → A5
Questo è possibile perché i dispositivi I2C utilizzano indirizzi differenti:
- display OLED: 0x3C;
- modulo DS3231: 0x68.
Batteria tampone
Inserisci una batteria CR2032 nel portabatteria del modulo DS3231, rispettando la polarità indicata
La batteria alimenta soltanto l’orologio interno quando Arduino viene spento.
👉 Senza batteria tampone, il modulo potrebbe perdere data e ora quando viene scollegata l’alimentazione principale.
💻 Codice Arduino
Il programma inizializza il display OLED e il modulo DS3231.
Successivamente legge dal modulo:
Le informazioni vengono visualizzate su tre aree del display:
Il programma inizializza il display OLED e il modulo DS3231.
Successivamente legge dal modulo:
- ora;
- minuti;
- secondi;
- giorno;
- mese;
- anno;
- giorno della settimana.
Le informazioni vengono visualizzate su tre aree del display:
- giorno della settimana nella parte superiore;
- ora in formato grande al centro;
- data nella parte inferiore.
/*
============================================================
Progetto 126 - Orologio digitale con RTC DS3231
Visualizza ora, data e giorno della settimana su display OLED
============================================================
*/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <RTClib.h>
// Dimensioni del display OLED
#define LARGHEZZA_DISPLAY 128
#define ALTEZZA_DISPLAY 64
#define RESET_OLED -1
// Indirizzo I2C tipico del display SSD1306
#define INDIRIZZO_OLED 0x3C
// Oggetto utilizzato per controllare il display OLED
Adafruit_SSD1306 display(
LARGHEZZA_DISPLAY,
ALTEZZA_DISPLAY,
&Wire,
RESET_OLED
);
// Oggetto utilizzato per comunicare con il modulo DS3231
RTC_DS3231 rtc;
// Nomi italiani dei giorni della settimana.
// La funzione dayOfTheWeek() restituisce:
// 0 = domenica, 1 = lunedì, fino a 6 = sabato.
const char* giorniSettimana[7] = {
"Domenica",
"Lunedi",
"Martedi",
"Mercoledi",
"Giovedi",
"Venerdi",
"Sabato"
};
// Memorizza l'ultimo secondo mostrato.
// In questo modo il display viene aggiornato solo quando serve.
int ultimoSecondoVisualizzato = -1;
// Indica se il modulo RTC è stato rilevato correttamente
bool rtcDisponibile = false;
void setup() {
// Avvia la comunicazione I2C
Wire.begin();
// Inizializza il display OLED
if (!display.begin(
SSD1306_SWITCHCAPVCC,
INDIRIZZO_OLED
)) {
// Se il display non viene rilevato,
// il programma rimane bloccato in questo ciclo.
while (true) {
}
}
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextWrap(false);
// Mostra una breve schermata iniziale
mostraAvvio();
// Prova a inizializzare il modulo RTC DS3231
rtcDisponibile = rtc.begin();
// Se il modulo non viene rilevato,
// mostra un messaggio di errore e blocca il programma.
if (!rtcDisponibile) {
mostraErroreRtc();
while (true) {
}
}
// Se il DS3231 ha perso l'alimentazione,
// imposta automaticamente data e ora di compilazione.
if (rtc.lostPower()) {
rtc.adjust(
DateTime(
F(__DATE__),
F(__TIME__)
)
);
mostraImpostazioneOra();
}
}
void loop() {
// Legge data e ora correnti dal DS3231
DateTime adesso = rtc.now();
// Aggiorna la schermata soltanto quando cambia il secondo.
// Questo evita di ridisegnare inutilmente il display
// centinaia di volte al secondo.
if (adesso.second() != ultimoSecondoVisualizzato) {
ultimoSecondoVisualizzato = adesso.second();
mostraOrologio(adesso);
}
}
void mostraAvvio() {
// Pulisce completamente il display
display.clearDisplay();
display.setTextSize(1);
display.setCursor(22, 17);
display.println("Orologio digitale");
display.setCursor(31, 33);
display.println("RTC DS3231");
display.setCursor(28, 49);
display.println("Progetto 126");
// Invia al display gli elementi appena disegnati
display.display();
delay(1800);
}
void mostraOrologio(const DateTime& adesso) {
// Cancella la schermata precedente
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
// Recupera il giorno della settimana
byte indiceGiorno = adesso.dayOfTheWeek();
// Mostra il nome del giorno nella parte superiore
display.setTextSize(1);
centraTesto(
giorniSettimana[indiceGiorno],
2,
1
);
// Disegna una linea sotto il giorno della settimana
display.drawLine(
0,
12,
127,
12,
SSD1306_WHITE
);
// Mostra ore, minuti e secondi con caratteri grandi
display.setTextSize(2);
display.setCursor(16, 21);
stampaDueCifre(adesso.hour());
display.print(":");
stampaDueCifre(adesso.minute());
display.print(":");
stampaDueCifre(adesso.second());
// Mostra la data nella parte inferiore
display.setTextSize(1);
display.setCursor(35, 51);
stampaDueCifre(adesso.day());
display.print("/");
stampaDueCifre(adesso.month());
display.print("/");
display.print(adesso.year());
// Rende visibile la nuova schermata
display.display();
}
void stampaDueCifre(int valore) {
// Aggiunge uno zero davanti ai numeri inferiori a 10.
// Per esempio, 7 viene visualizzato come 07.
if (valore < 10) {
display.print("0");
}
display.print(valore);
}
void centraTesto(
const char* testo,
int coordinataY,
byte dimensione
) {
// Imposta la dimensione utilizzata per calcolare il testo
display.setTextSize(dimensione);
int16_t x1;
int16_t y1;
uint16_t larghezzaTesto;
uint16_t altezzaTesto;
// Calcola lo spazio occupato dalla scritta
display.getTextBounds(
testo,
0,
coordinataY,
&x1,
&y1,
&larghezzaTesto,
&altezzaTesto
);
// Calcola la posizione orizzontale necessaria
// per centrare il testo sul display
int coordinataX =
(LARGHEZZA_DISPLAY - larghezzaTesto) / 2;
display.setCursor(
coordinataX,
coordinataY
);
display.print(testo);
}
void mostraErroreRtc() {
// Mostra un messaggio quando il DS3231
// non viene rilevato sul bus I2C.
display.clearDisplay();
display.setTextSize(1);
display.setCursor(20, 15);
display.println("ERRORE RTC");
display.setCursor(9, 31);
display.println("DS3231 non rilevato");
display.setCursor(15, 47);
display.println("Controlla i cavi");
display.display();
}
void mostraImpostazioneOra() {
// Avvisa che data e ora sono state impostate
// usando il momento di compilazione del programma.
display.clearDisplay();
display.setTextSize(1);
display.setCursor(18, 15);
display.println("Ora impostata");
display.setCursor(10, 31);
display.println("Data di compilazione");
display.setCursor(14, 47);
display.println("Batteria: controlla");
display.display();
delay(2000);
}
============================================================
Progetto 126 - Orologio digitale con RTC DS3231
Visualizza ora, data e giorno della settimana su display OLED
============================================================
*/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <RTClib.h>
// Dimensioni del display OLED
#define LARGHEZZA_DISPLAY 128
#define ALTEZZA_DISPLAY 64
#define RESET_OLED -1
// Indirizzo I2C tipico del display SSD1306
#define INDIRIZZO_OLED 0x3C
// Oggetto utilizzato per controllare il display OLED
Adafruit_SSD1306 display(
LARGHEZZA_DISPLAY,
ALTEZZA_DISPLAY,
&Wire,
RESET_OLED
);
// Oggetto utilizzato per comunicare con il modulo DS3231
RTC_DS3231 rtc;
// Nomi italiani dei giorni della settimana.
// La funzione dayOfTheWeek() restituisce:
// 0 = domenica, 1 = lunedì, fino a 6 = sabato.
const char* giorniSettimana[7] = {
"Domenica",
"Lunedi",
"Martedi",
"Mercoledi",
"Giovedi",
"Venerdi",
"Sabato"
};
// Memorizza l'ultimo secondo mostrato.
// In questo modo il display viene aggiornato solo quando serve.
int ultimoSecondoVisualizzato = -1;
// Indica se il modulo RTC è stato rilevato correttamente
bool rtcDisponibile = false;
void setup() {
// Avvia la comunicazione I2C
Wire.begin();
// Inizializza il display OLED
if (!display.begin(
SSD1306_SWITCHCAPVCC,
INDIRIZZO_OLED
)) {
// Se il display non viene rilevato,
// il programma rimane bloccato in questo ciclo.
while (true) {
}
}
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextWrap(false);
// Mostra una breve schermata iniziale
mostraAvvio();
// Prova a inizializzare il modulo RTC DS3231
rtcDisponibile = rtc.begin();
// Se il modulo non viene rilevato,
// mostra un messaggio di errore e blocca il programma.
if (!rtcDisponibile) {
mostraErroreRtc();
while (true) {
}
}
// Se il DS3231 ha perso l'alimentazione,
// imposta automaticamente data e ora di compilazione.
if (rtc.lostPower()) {
rtc.adjust(
DateTime(
F(__DATE__),
F(__TIME__)
)
);
mostraImpostazioneOra();
}
}
void loop() {
// Legge data e ora correnti dal DS3231
DateTime adesso = rtc.now();
// Aggiorna la schermata soltanto quando cambia il secondo.
// Questo evita di ridisegnare inutilmente il display
// centinaia di volte al secondo.
if (adesso.second() != ultimoSecondoVisualizzato) {
ultimoSecondoVisualizzato = adesso.second();
mostraOrologio(adesso);
}
}
void mostraAvvio() {
// Pulisce completamente il display
display.clearDisplay();
display.setTextSize(1);
display.setCursor(22, 17);
display.println("Orologio digitale");
display.setCursor(31, 33);
display.println("RTC DS3231");
display.setCursor(28, 49);
display.println("Progetto 126");
// Invia al display gli elementi appena disegnati
display.display();
delay(1800);
}
void mostraOrologio(const DateTime& adesso) {
// Cancella la schermata precedente
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
// Recupera il giorno della settimana
byte indiceGiorno = adesso.dayOfTheWeek();
// Mostra il nome del giorno nella parte superiore
display.setTextSize(1);
centraTesto(
giorniSettimana[indiceGiorno],
2,
1
);
// Disegna una linea sotto il giorno della settimana
display.drawLine(
0,
12,
127,
12,
SSD1306_WHITE
);
// Mostra ore, minuti e secondi con caratteri grandi
display.setTextSize(2);
display.setCursor(16, 21);
stampaDueCifre(adesso.hour());
display.print(":");
stampaDueCifre(adesso.minute());
display.print(":");
stampaDueCifre(adesso.second());
// Mostra la data nella parte inferiore
display.setTextSize(1);
display.setCursor(35, 51);
stampaDueCifre(adesso.day());
display.print("/");
stampaDueCifre(adesso.month());
display.print("/");
display.print(adesso.year());
// Rende visibile la nuova schermata
display.display();
}
void stampaDueCifre(int valore) {
// Aggiunge uno zero davanti ai numeri inferiori a 10.
// Per esempio, 7 viene visualizzato come 07.
if (valore < 10) {
display.print("0");
}
display.print(valore);
}
void centraTesto(
const char* testo,
int coordinataY,
byte dimensione
) {
// Imposta la dimensione utilizzata per calcolare il testo
display.setTextSize(dimensione);
int16_t x1;
int16_t y1;
uint16_t larghezzaTesto;
uint16_t altezzaTesto;
// Calcola lo spazio occupato dalla scritta
display.getTextBounds(
testo,
0,
coordinataY,
&x1,
&y1,
&larghezzaTesto,
&altezzaTesto
);
// Calcola la posizione orizzontale necessaria
// per centrare il testo sul display
int coordinataX =
(LARGHEZZA_DISPLAY - larghezzaTesto) / 2;
display.setCursor(
coordinataX,
coordinataY
);
display.print(testo);
}
void mostraErroreRtc() {
// Mostra un messaggio quando il DS3231
// non viene rilevato sul bus I2C.
display.clearDisplay();
display.setTextSize(1);
display.setCursor(20, 15);
display.println("ERRORE RTC");
display.setCursor(9, 31);
display.println("DS3231 non rilevato");
display.setCursor(15, 47);
display.println("Controlla i cavi");
display.display();
}
void mostraImpostazioneOra() {
// Avvisa che data e ora sono state impostate
// usando il momento di compilazione del programma.
display.clearDisplay();
display.setTextSize(1);
display.setCursor(18, 15);
display.println("Ora impostata");
display.setCursor(10, 31);
display.println("Data di compilazione");
display.setCursor(14, 47);
display.println("Batteria: controlla");
display.display();
delay(2000);
}
⚙️ Come funziona
Il modulo DS3231 contiene un circuito dedicato al conteggio del tempo.
A differenza di Arduino, non dipende dal ciclo principale del programma e continua a funzionare grazie alla batteria tampone.
La seguente istruzione legge la data e l’ora correnti:
DateTime adesso = rtc.now();
Dall’oggetto adesso è possibile ottenere ogni informazione:
adesso.hour();
adesso.minute();
adesso.second();
adesso.day();
adesso.month();
adesso.year();
Arduino aggiorna il display soltanto quando cambia il secondo visualizzato, evitando ridisegni inutili.
Il modulo DS3231 contiene un circuito dedicato al conteggio del tempo.
A differenza di Arduino, non dipende dal ciclo principale del programma e continua a funzionare grazie alla batteria tampone.
La seguente istruzione legge la data e l’ora correnti:
DateTime adesso = rtc.now();
Dall’oggetto adesso è possibile ottenere ogni informazione:
adesso.hour();
adesso.minute();
adesso.second();
adesso.day();
adesso.month();
adesso.year();
Arduino aggiorna il display soltanto quando cambia il secondo visualizzato, evitando ridisegni inutili.
🕒 Impostazione iniziale di data e ora
Quando il DS3231 viene utilizzato per la prima volta, è necessario impostare l’orologio.
Il codice controlla se il modulo ha perso l’alimentazione:
if (rtc.lostPower()) {
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
In questo caso, data e ora vengono impostate utilizzando il momento in cui il programma è stato compilato.
Le costanti:
👉 Dopo la prima impostazione, il modulo continuerà a mantenere l’orario grazie alla batteria CR2032.
Quando il DS3231 viene utilizzato per la prima volta, è necessario impostare l’orologio.
Il codice controlla se il modulo ha perso l’alimentazione:
if (rtc.lostPower()) {
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
In questo caso, data e ora vengono impostate utilizzando il momento in cui il programma è stato compilato.
Le costanti:
- __DATE__
- __TIME__
👉 Dopo la prima impostazione, il modulo continuerà a mantenere l’orario grazie alla batteria CR2032.
📅 Giorno della settimana
La funzione:
adesso.dayOfTheWeek();
restituisce un numero compreso tra 0 e 6.
Nel programma viene utilizzato un array per associare ogni numero al nome italiano del giorno:
const char* giorniSettimana[7] = {
"Domenica",
"Lunedi",
"Martedi",
"Mercoledi",
"Giovedi",
"Venerdi",
"Sabato"
};
In questo modo il display mostra direttamente il nome del giorno.
La funzione:
adesso.dayOfTheWeek();
restituisce un numero compreso tra 0 e 6.
Nel programma viene utilizzato un array per associare ogni numero al nome italiano del giorno:
const char* giorniSettimana[7] = {
"Domenica",
"Lunedi",
"Martedi",
"Mercoledi",
"Giovedi",
"Venerdi",
"Sabato"
};
In questo modo il display mostra direttamente il nome del giorno.
🔋 Perché utilizzare il DS3231
Arduino può misurare il tempo trascorso tramite millis(), ma questo sistema presenta alcuni limiti:
Il DS3231 è invece progettato appositamente per mantenere il tempo reale ed è dotato di compensazione della temperatura per migliorare la precisione.
Arduino può misurare il tempo trascorso tramite millis(), ma questo sistema presenta alcuni limiti:
- il conteggio riparte da zero quando Arduino viene spento;
- non conosce data e ora reali;
- può accumulare errori nel lungo periodo;
- non gestisce automaticamente giorni, mesi e anni.
Il DS3231 è invece progettato appositamente per mantenere il tempo reale ed è dotato di compensazione della temperatura per migliorare la precisione.
⚠️ Problemi comuni
Il display non mostra nulla
Controlla:
Il modulo RTC non viene rilevato
Controlla:
L’orario si azzera quando Arduino viene spento
Controlla che:
L’orario è leggermente indietro rispetto al computer
La compilazione e il caricamento richiedono alcuni secondi.
Poiché __TIME__ contiene l’ora di compilazione, al primo avvio può esserci un piccolo ritardo.
Il display non mostra nulla
Controlla:
- alimentazione del display;
- collegamenti SDA e SCL;
- indirizzo I2C 0x3C;
- installazione delle librerie.
Il modulo RTC non viene rilevato
Controlla:
- collegamenti del DS3231;
- alimentazione a 5V;
- condivisione corretta dei pin I2C;
- eventuali cavetti difettosi.
L’orario si azzera quando Arduino viene spento
Controlla che:
- la batteria CR2032 sia presente;
- la batteria sia inserita nel verso corretto;
- la batteria non sia scarica.
L’orario è leggermente indietro rispetto al computer
La compilazione e il caricamento richiedono alcuni secondi.
Poiché __TIME__ contiene l’ora di compilazione, al primo avvio può esserci un piccolo ritardo.
🧠 Perché è un progetto importante
Con questo progetto impari a:
Con questo progetto impari a:
- utilizzare un modulo RTC DS3231
- mantenere data e ora senza alimentazione principale
- collegare più dispositivi allo stesso bus I2C
- utilizzare la libreria RTClib
- leggere ore, minuti e secondi
- gestire giorno, mese e anno
- mostrare il giorno della settimana
- formattare correttamente un orario
- aggiornare il display soltanto quando necessario
🔄 Varianti possibili
Vuoi migliorare il progetto?
Vuoi migliorare il progetto?
- 🔘 aggiungi pulsanti per regolare data e ora
- 🔔 collega un buzzer per creare una sveglia
- 🌡️ mostra la temperatura interna del DS3231
- 📊 alterna orologio e dati ambientali
- 💡 regola automaticamente la luminosità del display
- 🌙 crea una modalità notturna
- ⏱️ aggiungi un cronometro
- ⏳ inserisci un timer countdown
- 💾 salva impostazioni e preferenze nella EEPROM
- 📟 utilizza un display OLED più grande
- 📶 sincronizza l’ora tramite Internet con ESP8266 o ESP32