PROGETTI ARDUINO

  • HOME
  • 200 Progetti
  • Progetti e Tutorial
  • Reti
    • Impara le reti
    • internet fibra
    • wifi mesh
    • modem router
    • diagnostica reti
    • ethernet lan
    • sicurezza configurazione
  • Elettronica
  • Recensioni
  • Manuale
  • Contatti
  • HOME
  • 200 Progetti
  • Progetti e Tutorial
  • Reti
    • Impara le reti
    • internet fibra
    • wifi mesh
    • modem router
    • diagnostica reti
    • ethernet lan
    • sicurezza configurazione
  • Elettronica
  • Recensioni
  • Manuale
  • Contatti

Progetto 128
​Timer settimanale con RTC

🚀 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.

🎯 Cosa realizzerai
​

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
Il pulsante utilizza la resistenza interna INPUT_PULLUP di Arduino, quindi non richiede una resistenza esterna.

🛒 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
​

📚 Librerie necessarie
​

Per realizzare il progetto devi installare:
  • RTClib
  • Adafruit SSD1306
  • Adafruit GFX Library
La libreria Wire, necessaria per la comunicazione I2C, è già inclusa nell’Arduino IDE.
​
Per installare le altre librerie:
  1. apri Arduino IDE;
  2. vai su Sketch → Include Library → Manage Libraries;
  3. cerca RTClib;
  4. installa la libreria sviluppata da Adafruit;
  5. cerca Adafruit SSD1306;
  6. installa la libreria;
  7. verifica che sia installata anche Adafruit GFX Library.

🔌 Schema di collegamento
​

Display OLED SSD1306
  • VCC → 5V
  • GND → GND
  • SDA → A4
  • SCL → A5
L’indirizzo I2C utilizzato dal display è 0x3C.

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
Il codice è già configurato per un modulo relè attivo LOW.

Pulsante modalità
  • un terminale → pin digitale 2
  • terminale opposto → GND
Ogni pressione cambia la modalità del sistema:
  1. automatica;
  2. manuale acceso;
  3. manuale spento.
Foto

⚠️ 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.

💻 Codice Arduino
​

​Nel programma il timer è configurato per attivare il relè:
  • dal lunedì al venerdì;
  • dalle 18:30 alle 22:00.
Il sabato e la domenica il relè rimane spento in modalità automatica.
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();
}

⚙️ Come funziona
​

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.
Il relè viene attivato quando il valore corrente si trova tra l’orario di accensione e quello di spegnimento.

📅 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:
  • posizione 0 → domenica
  • posizione 1 → lunedì
  • posizione 2 → martedì
  • posizione 3 → mercoledì
  • posizione 4 → giovedì
  • posizione 5 → venerdì
  • posizione 6 → sabato
Il valore true indica un giorno attivo, mentre false indica un giorno escluso dalla programmazione.

🕒 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.

🌙 Fasce che superano la mezzanotte
​

Il codice gestisce anche intervalli come:
  • accensione alle 22:00;
  • spegnimento alle 06:00.
In questo caso il periodo di funzionamento attraversa la mezzanotte.
​
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.

​📟 Informazioni visualizzate
​

Il display OLED mostra:
  • giorno della settimana;
  • ora corrente;
  • fascia oraria programmata;
  • modalità del timer;
  • stato del relè.
Quando il sistema è in modalità automatica, il display indica anche se il giorno corrente è programmato.

⏱️ Aggiornamento senza delay
​

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:
  • 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:
  • 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
👉 Questa struttura può essere utilizzata per irrigazione, illuminazione, riscaldamento, ventilazione e controllo di apparecchiature.

🔄 Varianti possibili
​

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
Foto
Foto

© ProgettiArduino.com 2026 - Fancello Salvatore
​ Layout, immagini e testi sono di esclusiva proprietà di www.progettiarduino.com 
Privacy Policy