🚀 Introduzione
Vuoi accendere automaticamente una luce, una pompa o un altro dispositivo soltanto in determinati giorni e orari? 📅⚡
In questo progetto realizzerai un timer settimanale con Arduino e RTC DS3231, capace di controllare un modulo relè secondo una programmazione prestabilita.
Arduino verificherà continuamente il giorno della settimana e l’ora corrente. Quando l’orario rientra nella fascia programmata, il relè verrà attivato automaticamente.
👉 Il progetto è ideale per illuminazione, irrigazione, ventilazione, insegne luminose e piccole automazioni domestiche.
Vuoi accendere automaticamente una luce, una pompa o un altro dispositivo soltanto in determinati giorni e orari? 📅⚡
In questo progetto realizzerai un timer settimanale con Arduino e RTC DS3231, capace di controllare un modulo relè secondo una programmazione prestabilita.
Arduino verificherà continuamente il giorno della settimana e l’ora corrente. Quando l’orario rientra nella fascia programmata, il relè verrà attivato automaticamente.
👉 Il progetto è ideale per illuminazione, irrigazione, ventilazione, insegne luminose e piccole automazioni domestiche.
🎯 Cosa realizzerai
Con Arduino creerai un sistema che:
Con Arduino creerai un sistema che:
- ⏰ legge data e ora dal modulo RTC DS3231
- 📅 riconosce il giorno della settimana
- ⚡ attiva automaticamente un relè
- 🕒 utilizza un orario di accensione e spegnimento
- 📆 funziona soltanto nei giorni programmati
- 📟 mostra tutte le informazioni su display OLED
- 🔘 permette di scegliere tra modalità automatica e manuale
- 💾 mantiene l’orario anche quando Arduino viene spento
- ⚠️ segnala eventuali errori del modulo RTC
🧰 Materiale necessario
- 1x Arduino UNO
- 1x modulo RTC DS3231
- 1x batteria CR2032
- 1x display OLED 0,96 pollici I2C SSD1306
- 1x modulo relè 5V
- 1x pulsante
- 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 installare le altre librerie:
Per realizzare il progetto devi installare:
- RTClib
- Adafruit SSD1306
- Adafruit GFX Library
Per installare le altre librerie:
- apri Arduino IDE;
- vai su Sketch → Include Library → Manage Libraries;
- cerca RTClib;
- installa la libreria sviluppata da Adafruit;
- cerca Adafruit SSD1306;
- installa la libreria;
- verifica che sia installata anche Adafruit GFX Library.
🔌 Schema di collegamento
Display OLED SSD1306
Modulo RTC DS3231
Il display OLED e il modulo RTC condividono gli stessi collegamenti I2C.
Questo è possibile perché utilizzano indirizzi differenti:
Modulo relè
Molti moduli relè sono attivi a livello basso. In questo caso:
Pulsante modalità
Display OLED SSD1306
- VCC → 5V
- GND → GND
- SDA → A4
- SCL → A5
Modulo RTC DS3231
- VCC → 5V
- GND → GND
- SDA → A4
- SCL → A5
Il display OLED e il modulo RTC condividono gli stessi collegamenti I2C.
Questo è possibile perché utilizzano indirizzi differenti:
- OLED SSD1306 → 0x3C
- RTC DS3231 → 0x68
Modulo relè
- VCC → 5V
- GND → GND
- IN → pin digitale 8
Molti moduli relè sono attivi a livello basso. In questo caso:
- LOW → relè attivato
- HIGH → relè disattivato
Pulsante modalità
- un terminale → pin digitale 2
- terminale opposto → GND
- automatica;
- manuale acceso;
- manuale spento.
⚠️ Sicurezza del relè
Il relè permette di controllare dispositivi alimentati separatamente da Arduino.
Per le prime prove utilizza un LED o un piccolo carico a bassa tensione.
👉 Non lavorare con la tensione di rete a 230V se non possiedi esperienza e competenze adeguate. La tensione domestica può provocare folgorazioni e incendi.
Il relè permette di controllare dispositivi alimentati separatamente da Arduino.
Per le prime prove utilizza un LED o un piccolo carico a bassa tensione.
👉 Non lavorare con la tensione di rete a 230V se non possiedi esperienza e competenze adeguate. La tensione domestica può provocare folgorazioni e incendi.
💻 Codice Arduino
Nel programma il timer è configurato per attivare il relè:
Puoi modificare facilmente giorni e orari attraverso le costanti presenti all’inizio del codice.
Nel programma il timer è configurato per attivare il relè:
- dal lunedì al venerdì;
- dalle 18:30 alle 22:00.
Puoi modificare facilmente giorni e orari attraverso le costanti presenti all’inizio del codice.
/*
============================================================
Progetto 128 - Timer settimanale con RTC DS3231
Controlla un relè in base al giorno e all'orario programmato
============================================================
*/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <RTClib.h>
// Dimensioni e indirizzo del display OLED
#define LARGHEZZA_DISPLAY 128
#define ALTEZZA_DISPLAY 64
#define RESET_OLED -1
#define INDIRIZZO_OLED 0x3C
// Collegamenti del pulsante e del modulo relè
#define PIN_PULSANTE 2
#define PIN_RELE 8
// Livelli logici del modulo relè.
// La maggior parte dei moduli è attiva a livello LOW.
const byte RELE_ATTIVO = LOW;
const byte RELE_DISATTIVO = HIGH;
// Orario programmato di accensione
const byte ORA_ACCENSIONE = 18;
const byte MINUTO_ACCENSIONE = 30;
// Orario programmato di spegnimento
const byte ORA_SPEGNIMENTO = 22;
const byte MINUTO_SPEGNIMENTO = 0;
// Giorni durante i quali il timer può funzionare.
// L'ordine è: domenica, lunedì, martedì,
// mercoledì, giovedì, venerdì, sabato.
const bool giorniAttivi[7] = {
false,
true,
true,
true,
true,
true,
false
};
// Intervallo antirimbalzo del pulsante
const unsigned long TEMPO_DEBOUNCE = 180;
// Oggetti per il display OLED e il modulo RTC
Adafruit_SSD1306 display(
LARGHEZZA_DISPLAY,
ALTEZZA_DISPLAY,
&Wire,
RESET_OLED
);
RTC_DS3231 rtc;
// Nomi abbreviati dei giorni della settimana
const char* nomiGiorni[7] = {
"DOM",
"LUN",
"MAR",
"MER",
"GIO",
"VEN",
"SAB"
};
// Modalità disponibili per il timer
enum ModalitaTimer {
AUTOMATICA,
MANUALE_ACCESO,
MANUALE_SPENTO
};
ModalitaTimer modalitaAttuale = AUTOMATICA;
// Stato corrente del modulo relè
bool releAcceso = false;
// Momento dell'ultima pressione accettata
unsigned long ultimaPressione = 0;
// Memorizza il secondo già visualizzato
int ultimoSecondoVisualizzato = -1;
void setup() {
// Il pulsante viene collegato tra pin e GND.
// INPUT_PULLUP attiva la resistenza interna di Arduino.
pinMode(PIN_PULSANTE, INPUT_PULLUP);
// Imposta il pin del relè come uscita
pinMode(PIN_RELE, OUTPUT);
// All'avvio mantiene il relè spento
impostaRele(false);
// 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.
while (true) {
}
}
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextWrap(false);
mostraAvvio();
// Inizializza il modulo RTC DS3231
if (!rtc.begin()) {
mostraErroreRtc();
// Mantiene il relè spento in caso di errore
impostaRele(false);
while (true) {
}
}
// Se il modulo ha perso l'alimentazione,
// imposta data e ora di compilazione.
if (rtc.lostPower()) {
rtc.adjust(
DateTime(
F(__DATE__),
F(__TIME__)
)
);
}
}
void loop() {
// Legge data e ora correnti dal DS3231
DateTime adesso = rtc.now();
// Controlla se l'utente ha premuto il pulsante
gestisciPulsante();
// Stabilisce lo stato corretto del relè
aggiornaRele(adesso);
// Aggiorna il display soltanto quando cambia il secondo
if (adesso.second() != ultimoSecondoVisualizzato) {
ultimoSecondoVisualizzato = adesso.second();
mostraSchermata(adesso);
}
}
void gestisciPulsante() {
// Con INPUT_PULLUP il pulsante premuto viene letto come LOW
if (digitalRead(PIN_PULSANTE) == LOW &&
millis() - ultimaPressione >= TEMPO_DEBOUNCE) {
ultimaPressione = millis();
// Passa alla modalità successiva
if (modalitaAttuale == AUTOMATICA) {
modalitaAttuale = MANUALE_ACCESO;
} else if (modalitaAttuale == MANUALE_ACCESO) {
modalitaAttuale = MANUALE_SPENTO;
} else {
modalitaAttuale = AUTOMATICA;
}
// Forza l'aggiornamento immediato del display
ultimoSecondoVisualizzato = -1;
}
}
void aggiornaRele(const DateTime& adesso) {
bool nuovoStato = false;
// In modalità automatica il relè segue
// giorni e orari programmati.
if (modalitaAttuale == AUTOMATICA) {
nuovoStato = timerAutomaticoAttivo(adesso);
}
// In modalità manuale acceso il relè
// rimane sempre attivo.
else if (modalitaAttuale == MANUALE_ACCESO) {
nuovoStato = true;
}
// In modalità manuale spento il relè
// rimane sempre disattivato.
else {
nuovoStato = false;
}
// Aggiorna l'uscita solo se lo stato è cambiato
if (nuovoStato != releAcceso) {
impostaRele(nuovoStato);
}
}
bool timerAutomaticoAttivo(const DateTime& adesso) {
// Converte gli orari in minuti trascorsi dalla mezzanotte.
// Questo rende più semplice il confronto.
int minutiAttuali =
adesso.hour() * 60 + adesso.minute();
int minutiAccensione =
ORA_ACCENSIONE * 60 + MINUTO_ACCENSIONE;
int minutiSpegnimento =
ORA_SPEGNIMENTO * 60 + MINUTO_SPEGNIMENTO;
byte giornoCorrente = adesso.dayOfTheWeek();
// Caso normale: accensione e spegnimento
// avvengono durante lo stesso giorno.
if (minutiAccensione < minutiSpegnimento) {
return giorniAttivi[giornoCorrente] &&
minutiAttuali >= minutiAccensione &&
minutiAttuali < minutiSpegnimento;
}
// Se gli orari coincidono, la fascia viene
// considerata disattivata per evitare un relè
// acceso per l'intera giornata.
if (minutiAccensione == minutiSpegnimento) {
return false;
}
// Caso notturno: la fascia attraversa la mezzanotte.
// Prima della mezzanotte si usa il giorno corrente.
if (minutiAttuali >= minutiAccensione) {
return giorniAttivi[giornoCorrente];
}
// Dopo la mezzanotte la fascia appartiene
// alla programmazione iniziata il giorno precedente.
byte giornoPrecedente;
if (giornoCorrente == 0) {
giornoPrecedente = 6;
} else {
giornoPrecedente = giornoCorrente - 1;
}
return giorniAttivi[giornoPrecedente] &&
minutiAttuali < minutiSpegnimento;
}
void impostaRele(bool acceso) {
// Memorizza lo stato logico del relè
releAcceso = acceso;
// Scrive il livello corretto sul modulo relè
if (releAcceso) {
digitalWrite(PIN_RELE, RELE_ATTIVO);
} else {
digitalWrite(PIN_RELE, RELE_DISATTIVO);
}
}
void mostraSchermata(const DateTime& adesso) {
// Cancella la schermata precedente
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
// Mostra giorno e modalità corrente
display.setCursor(0, 0);
display.print(nomiGiorni[adesso.dayOfTheWeek()]);
display.setCursor(28, 0);
if (modalitaAttuale == AUTOMATICA) {
display.print("AUTOMATICO");
} else if (modalitaAttuale == MANUALE_ACCESO) {
display.print("MANUALE ON");
} else {
display.print("MANUALE OFF");
}
// Disegna una linea di separazione
display.drawLine(
0,
10,
127,
10,
SSD1306_WHITE
);
// Mostra l'ora corrente con caratteri grandi
display.setTextSize(2);
display.setCursor(16, 17);
stampaDueCifre(adesso.hour());
display.print(":");
stampaDueCifre(adesso.minute());
display.print(":");
stampaDueCifre(adesso.second());
// Mostra la fascia programmata
display.setTextSize(1);
display.setCursor(0, 40);
display.print("Timer: ");
stampaDueCifre(ORA_ACCENSIONE);
display.print(":");
stampaDueCifre(MINUTO_ACCENSIONE);
display.print("-");
stampaDueCifre(ORA_SPEGNIMENTO);
display.print(":");
stampaDueCifre(MINUTO_SPEGNIMENTO);
// Mostra se il giorno corrente è programmato
display.setCursor(0, 51);
display.print("Giorno: ");
if (giorniAttivi[adesso.dayOfTheWeek()]) {
display.print("ATTIVO");
} else {
display.print("ESCLUSO");
}
// Mostra lo stato reale del relè
display.setCursor(76, 51);
display.print("RELE ");
if (releAcceso) {
display.print("ON");
} else {
display.print("OFF");
}
// Indicazione del comando del pulsante
display.setCursor(0, 61);
display.print("Pulsante: cambia modalita");
// Invia al display tutti gli elementi disegnati
display.display();
}
void stampaDueCifre(int valore) {
// Aggiunge uno zero davanti ai valori inferiori a 10
if (valore < 10) {
display.print("0");
}
display.print(valore);
}
void mostraAvvio() {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(17, 15);
display.print("Timer settimanale");
display.setCursor(31, 31);
display.print("RTC DS3231");
display.setCursor(28, 47);
display.print("Progetto 128");
display.display();
delay(1800);
}
void mostraErroreRtc() {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(27, 15);
display.print("ERRORE RTC");
display.setCursor(9, 31);
display.print("DS3231 non rilevato");
display.setCursor(15, 47);
display.print("Controlla i cavi");
display.display();
}
============================================================
Progetto 128 - Timer settimanale con RTC DS3231
Controlla un relè in base al giorno e all'orario programmato
============================================================
*/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <RTClib.h>
// Dimensioni e indirizzo del display OLED
#define LARGHEZZA_DISPLAY 128
#define ALTEZZA_DISPLAY 64
#define RESET_OLED -1
#define INDIRIZZO_OLED 0x3C
// Collegamenti del pulsante e del modulo relè
#define PIN_PULSANTE 2
#define PIN_RELE 8
// Livelli logici del modulo relè.
// La maggior parte dei moduli è attiva a livello LOW.
const byte RELE_ATTIVO = LOW;
const byte RELE_DISATTIVO = HIGH;
// Orario programmato di accensione
const byte ORA_ACCENSIONE = 18;
const byte MINUTO_ACCENSIONE = 30;
// Orario programmato di spegnimento
const byte ORA_SPEGNIMENTO = 22;
const byte MINUTO_SPEGNIMENTO = 0;
// Giorni durante i quali il timer può funzionare.
// L'ordine è: domenica, lunedì, martedì,
// mercoledì, giovedì, venerdì, sabato.
const bool giorniAttivi[7] = {
false,
true,
true,
true,
true,
true,
false
};
// Intervallo antirimbalzo del pulsante
const unsigned long TEMPO_DEBOUNCE = 180;
// Oggetti per il display OLED e il modulo RTC
Adafruit_SSD1306 display(
LARGHEZZA_DISPLAY,
ALTEZZA_DISPLAY,
&Wire,
RESET_OLED
);
RTC_DS3231 rtc;
// Nomi abbreviati dei giorni della settimana
const char* nomiGiorni[7] = {
"DOM",
"LUN",
"MAR",
"MER",
"GIO",
"VEN",
"SAB"
};
// Modalità disponibili per il timer
enum ModalitaTimer {
AUTOMATICA,
MANUALE_ACCESO,
MANUALE_SPENTO
};
ModalitaTimer modalitaAttuale = AUTOMATICA;
// Stato corrente del modulo relè
bool releAcceso = false;
// Momento dell'ultima pressione accettata
unsigned long ultimaPressione = 0;
// Memorizza il secondo già visualizzato
int ultimoSecondoVisualizzato = -1;
void setup() {
// Il pulsante viene collegato tra pin e GND.
// INPUT_PULLUP attiva la resistenza interna di Arduino.
pinMode(PIN_PULSANTE, INPUT_PULLUP);
// Imposta il pin del relè come uscita
pinMode(PIN_RELE, OUTPUT);
// All'avvio mantiene il relè spento
impostaRele(false);
// 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.
while (true) {
}
}
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextWrap(false);
mostraAvvio();
// Inizializza il modulo RTC DS3231
if (!rtc.begin()) {
mostraErroreRtc();
// Mantiene il relè spento in caso di errore
impostaRele(false);
while (true) {
}
}
// Se il modulo ha perso l'alimentazione,
// imposta data e ora di compilazione.
if (rtc.lostPower()) {
rtc.adjust(
DateTime(
F(__DATE__),
F(__TIME__)
)
);
}
}
void loop() {
// Legge data e ora correnti dal DS3231
DateTime adesso = rtc.now();
// Controlla se l'utente ha premuto il pulsante
gestisciPulsante();
// Stabilisce lo stato corretto del relè
aggiornaRele(adesso);
// Aggiorna il display soltanto quando cambia il secondo
if (adesso.second() != ultimoSecondoVisualizzato) {
ultimoSecondoVisualizzato = adesso.second();
mostraSchermata(adesso);
}
}
void gestisciPulsante() {
// Con INPUT_PULLUP il pulsante premuto viene letto come LOW
if (digitalRead(PIN_PULSANTE) == LOW &&
millis() - ultimaPressione >= TEMPO_DEBOUNCE) {
ultimaPressione = millis();
// Passa alla modalità successiva
if (modalitaAttuale == AUTOMATICA) {
modalitaAttuale = MANUALE_ACCESO;
} else if (modalitaAttuale == MANUALE_ACCESO) {
modalitaAttuale = MANUALE_SPENTO;
} else {
modalitaAttuale = AUTOMATICA;
}
// Forza l'aggiornamento immediato del display
ultimoSecondoVisualizzato = -1;
}
}
void aggiornaRele(const DateTime& adesso) {
bool nuovoStato = false;
// In modalità automatica il relè segue
// giorni e orari programmati.
if (modalitaAttuale == AUTOMATICA) {
nuovoStato = timerAutomaticoAttivo(adesso);
}
// In modalità manuale acceso il relè
// rimane sempre attivo.
else if (modalitaAttuale == MANUALE_ACCESO) {
nuovoStato = true;
}
// In modalità manuale spento il relè
// rimane sempre disattivato.
else {
nuovoStato = false;
}
// Aggiorna l'uscita solo se lo stato è cambiato
if (nuovoStato != releAcceso) {
impostaRele(nuovoStato);
}
}
bool timerAutomaticoAttivo(const DateTime& adesso) {
// Converte gli orari in minuti trascorsi dalla mezzanotte.
// Questo rende più semplice il confronto.
int minutiAttuali =
adesso.hour() * 60 + adesso.minute();
int minutiAccensione =
ORA_ACCENSIONE * 60 + MINUTO_ACCENSIONE;
int minutiSpegnimento =
ORA_SPEGNIMENTO * 60 + MINUTO_SPEGNIMENTO;
byte giornoCorrente = adesso.dayOfTheWeek();
// Caso normale: accensione e spegnimento
// avvengono durante lo stesso giorno.
if (minutiAccensione < minutiSpegnimento) {
return giorniAttivi[giornoCorrente] &&
minutiAttuali >= minutiAccensione &&
minutiAttuali < minutiSpegnimento;
}
// Se gli orari coincidono, la fascia viene
// considerata disattivata per evitare un relè
// acceso per l'intera giornata.
if (minutiAccensione == minutiSpegnimento) {
return false;
}
// Caso notturno: la fascia attraversa la mezzanotte.
// Prima della mezzanotte si usa il giorno corrente.
if (minutiAttuali >= minutiAccensione) {
return giorniAttivi[giornoCorrente];
}
// Dopo la mezzanotte la fascia appartiene
// alla programmazione iniziata il giorno precedente.
byte giornoPrecedente;
if (giornoCorrente == 0) {
giornoPrecedente = 6;
} else {
giornoPrecedente = giornoCorrente - 1;
}
return giorniAttivi[giornoPrecedente] &&
minutiAttuali < minutiSpegnimento;
}
void impostaRele(bool acceso) {
// Memorizza lo stato logico del relè
releAcceso = acceso;
// Scrive il livello corretto sul modulo relè
if (releAcceso) {
digitalWrite(PIN_RELE, RELE_ATTIVO);
} else {
digitalWrite(PIN_RELE, RELE_DISATTIVO);
}
}
void mostraSchermata(const DateTime& adesso) {
// Cancella la schermata precedente
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
// Mostra giorno e modalità corrente
display.setCursor(0, 0);
display.print(nomiGiorni[adesso.dayOfTheWeek()]);
display.setCursor(28, 0);
if (modalitaAttuale == AUTOMATICA) {
display.print("AUTOMATICO");
} else if (modalitaAttuale == MANUALE_ACCESO) {
display.print("MANUALE ON");
} else {
display.print("MANUALE OFF");
}
// Disegna una linea di separazione
display.drawLine(
0,
10,
127,
10,
SSD1306_WHITE
);
// Mostra l'ora corrente con caratteri grandi
display.setTextSize(2);
display.setCursor(16, 17);
stampaDueCifre(adesso.hour());
display.print(":");
stampaDueCifre(adesso.minute());
display.print(":");
stampaDueCifre(adesso.second());
// Mostra la fascia programmata
display.setTextSize(1);
display.setCursor(0, 40);
display.print("Timer: ");
stampaDueCifre(ORA_ACCENSIONE);
display.print(":");
stampaDueCifre(MINUTO_ACCENSIONE);
display.print("-");
stampaDueCifre(ORA_SPEGNIMENTO);
display.print(":");
stampaDueCifre(MINUTO_SPEGNIMENTO);
// Mostra se il giorno corrente è programmato
display.setCursor(0, 51);
display.print("Giorno: ");
if (giorniAttivi[adesso.dayOfTheWeek()]) {
display.print("ATTIVO");
} else {
display.print("ESCLUSO");
}
// Mostra lo stato reale del relè
display.setCursor(76, 51);
display.print("RELE ");
if (releAcceso) {
display.print("ON");
} else {
display.print("OFF");
}
// Indicazione del comando del pulsante
display.setCursor(0, 61);
display.print("Pulsante: cambia modalita");
// Invia al display tutti gli elementi disegnati
display.display();
}
void stampaDueCifre(int valore) {
// Aggiunge uno zero davanti ai valori inferiori a 10
if (valore < 10) {
display.print("0");
}
display.print(valore);
}
void mostraAvvio() {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(17, 15);
display.print("Timer settimanale");
display.setCursor(31, 31);
display.print("RTC DS3231");
display.setCursor(28, 47);
display.print("Progetto 128");
display.display();
delay(1800);
}
void mostraErroreRtc() {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(27, 15);
display.print("ERRORE RTC");
display.setCursor(9, 31);
display.print("DS3231 non rilevato");
display.setCursor(15, 47);
display.print("Controlla i cavi");
display.display();
}
⚙️ Come funziona
Il modulo DS3231 fornisce ad Arduino:
Arduino trasforma l’ora corrente in minuti trascorsi dalla mezzanotte.
Per esempio:
Il modulo DS3231 fornisce ad Arduino:
- giorno della settimana;
- ore;
- minuti;
- secondi;
- data completa.
Arduino trasforma l’ora corrente in minuti trascorsi dalla mezzanotte.
Per esempio:
- 18:30 corrisponde a 1110 minuti;
- 22:00 corrisponde a 1320 minuti.
📅 Programmazione dei giorni
Il programma utilizza un array di sette valori:
const bool giorniAttivi[7] = {
false,
true,
true,
true,
true,
true,
false
};
Ogni posizione corrisponde a un giorno:
Il programma utilizza un array di sette valori:
const bool giorniAttivi[7] = {
false,
true,
true,
true,
true,
true,
false
};
Ogni posizione corrisponde a un giorno:
- posizione 0 → domenica
- posizione 1 → lunedì
- posizione 2 → martedì
- posizione 3 → mercoledì
- posizione 4 → giovedì
- posizione 5 → venerdì
- posizione 6 → sabato
🕒 Orario di accensione e spegnimento
Gli orari vengono impostati attraverso quattro costanti:
const byte ORA_ACCENSIONE = 18;
const byte MINUTO_ACCENSIONE = 30;
const byte ORA_SPEGNIMENTO = 22;
const byte MINUTO_SPEGNIMENTO = 0;
In questo esempio il dispositivo viene acceso alle 18:30 e spento alle 22:00.
La fascia comprende l’orario iniziale ma termina prima dell’orario di spegnimento.
Gli orari vengono impostati attraverso quattro costanti:
const byte ORA_ACCENSIONE = 18;
const byte MINUTO_ACCENSIONE = 30;
const byte ORA_SPEGNIMENTO = 22;
const byte MINUTO_SPEGNIMENTO = 0;
In questo esempio il dispositivo viene acceso alle 18:30 e spento alle 22:00.
La fascia comprende l’orario iniziale ma termina prima dell’orario di spegnimento.
🌙 Fasce che superano la mezzanotte
Il codice gestisce anche intervalli come:
Arduino riconosce automaticamente questa situazione e mantiene il relè acceso durante la notte.
👉 Quando la fascia supera la mezzanotte, dopo le 00:00 viene considerata la programmazione iniziata il giorno precedente.
Il codice gestisce anche intervalli come:
- accensione alle 22:00;
- spegnimento alle 06:00.
Arduino riconosce automaticamente questa situazione e mantiene il relè acceso durante la notte.
👉 Quando la fascia supera la mezzanotte, dopo le 00:00 viene considerata la programmazione iniziata il giorno precedente.
🔘 Modalità automatica e manuale
Il pulsante permette di scegliere tre modalità.
Automatica
Il relè segue giorni e orari programmati.
Manuale acceso
Il relè rimane sempre attivo, indipendentemente dall’orario.
Manuale spento
Il relè rimane sempre disattivato.
Il display mostra chiaramente la modalità selezionata.
Il pulsante permette di scegliere tre modalità.
Automatica
Il relè segue giorni e orari programmati.
Manuale acceso
Il relè rimane sempre attivo, indipendentemente dall’orario.
Manuale spento
Il relè rimane sempre disattivato.
Il display mostra chiaramente la modalità selezionata.
📟 Informazioni visualizzate
Il display OLED mostra:
Il display OLED mostra:
- giorno della settimana;
- ora corrente;
- fascia oraria programmata;
- modalità del timer;
- stato del relè.
⏱️ Aggiornamento senza delay
Il programma utilizza millis() per:
Non vengono utilizzati ritardi nel ciclo principale.
Arduino può quindi continuare a leggere l’orologio e controllare il relè senza interruzioni.
Il programma utilizza millis() per:
- controllare il pulsante;
- aggiornare il display;
- ridurre il rimbalzo meccanico.
Non vengono utilizzati ritardi nel ciclo principale.
Arduino può quindi continuare a leggere l’orologio e controllare il relè senza interruzioni.
⚠️ Problemi comuni
Il relè funziona al contrario
Alcuni moduli relè sono attivi HIGH.
In questo caso modifica:
const byte RELE_ATTIVO = LOW;
const byte RELE_DISATTIVO = HIGH;
in:
const byte RELE_ATTIVO = HIGH;
const byte RELE_DISATTIVO = LOW;
Il timer non si attiva
Controlla:
Il DS3231 perde l’orario
Controlla la batteria CR2032 e la sua polarità.
Il pulsante cambia più modalità
Aumenta leggermente il valore del tempo antirimbalzo:
const unsigned long TEMPO_DEBOUNCE = 180;
Il relè funziona al contrario
Alcuni moduli relè sono attivi HIGH.
In questo caso modifica:
const byte RELE_ATTIVO = LOW;
const byte RELE_DISATTIVO = HIGH;
in:
const byte RELE_ATTIVO = HIGH;
const byte RELE_DISATTIVO = LOW;
Il timer non si attiva
Controlla:
- giorno della settimana impostato nel DS3231;
- orario del modulo RTC;
- valori presenti nell’array giorniAttivi;
- orari di accensione e spegnimento;
- modalità attualmente selezionata.
Il DS3231 perde l’orario
Controlla la batteria CR2032 e la sua polarità.
Il pulsante cambia più modalità
Aumenta leggermente il valore del tempo antirimbalzo:
const unsigned long TEMPO_DEBOUNCE = 180;
🧠 Perché è un progetto importante
Con questo progetto impari a:
Con questo progetto impari a:
- realizzare un timer settimanale
- confrontare orari e giorni
- utilizzare un array di valori booleani
- controllare un relè tramite RTC
- gestire fasce orarie oltre la mezzanotte
- creare modalità automatiche e manuali
- utilizzare più dispositivi I2C
- aggiornare il sistema tramite millis()
- organizzare il codice in funzioni
- realizzare una vera automazione programmata
🔄 Varianti possibili
Vuoi migliorare il progetto?
Vuoi migliorare il progetto?
- 🎛️ aggiungi pulsanti per modificare gli orari
- 💾 salva la programmazione nella EEPROM
- 📅 imposta fasce differenti per ogni giorno
- ⚡ aggiungi un secondo relè
- 🌱 controlla un impianto di irrigazione
- 💡 programma l’illuminazione esterna
- 🌬️ attiva una ventola in determinati orari
- 🔔 aggiungi un avviso prima dell’accensione
- 📋 crea più programmi giornalieri
- 📶 modifica il timer tramite Wi-Fi
- 🔒 aggiungi un blocco delle impostazioni
- 📟 crea un menu completo sul display OLED