Creare passo a passo radiosveglia con Dot Matrix

// Includo le librerie per RTC, OLED
#include <Wire.h>
#include "RTClib.h"
#include <U8x8lib.h>

// Assegno i nomi per RTC, OLED, sensori temperatura e RadioFM
U8X8_SSD1306_128X64_ALT0_HW_I2C oled;
RTC_DS1307 rtc;

// Imposto pulsante a 0 e dati per RTC e Sveglia
int stato = 0;
char str[3];
char settimana[7][4] = {"dom", "lun", "mar", "mer", "gio", "ven", "sab"};
DateTime now;

int alarmh, alarmm;
int alarms = 0; // Messo a 0, così non imposto i secondi della sveglia, quando scatta il minuto suona
bool ALARM = false;

void setup() {
  Wire.begin();
  Serial.begin(9600);

  // Imposto i PIN, in ingresso per i pulsanti e in uscita per il buzzer
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  pinMode(6, OUTPUT);

  // Inizializzo lo schermo OLED e imposto il font
  oled.begin();
  oled.setFont(u8x8_font_5x7_f);

  // Inizializzo RTC e imposto data e ora
  if (! rtc.begin()) {
    Serial.println("Errore RTC");
    abort();
  }
  if (! rtc.isrunning()) {
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }
}

void loop() {
  // In base a quale tasto premo entro nella funzione scelta
  switch (stato) {
    case 0:
      dataora();
      break;
    case 1:
      impostaOra();
      break;
    case 2:
      impostaMinuti();
      break;
    case 3:
      impostaSecondi();
      break;
    case 10:
      accendiSveglia();
      break;
    case 11:
      impostaOraSveglia();
      break;
    case 12:
      impostaMinutiSveglia();
      break;
    case 20:
      allarmeSveglia();
      break;
    case 21:
      snooze();
      break;
    case 30:
      radioFM();
      break;
    case 40:
      meteo();
      break;
  }
}

bool FIRST = true;
unsigned long t1, dt;
int ch, cm, cs;

// stato 0 - Schermata principale dove visualizzo data/ora e se sveglia attiva
void dataora() {
  if (FIRST) {
    oled.clear();
    t1 = millis();
    FIRST = false;
  }

  // Assegno i nomi ch, cm, cs per visualizzare l'RTC
  dt = millis() - t1;
  if (dt > 500) {
    now = rtc.now();
    ch = now.hour();
    cm = now.minute();
    cs = now.second();

    oled.setCursor(0, 0);

    // Visualizzo ora, minuti e secondi sul display
    sprintf(str, "%02d", ch);
    oled.print(str);
    oled.print(":");
    sprintf(str, "%02d", cm);
    oled.print(str);
    oled.print(":");
    sprintf(str, "%02d", cs);
    oled.print(str);

    // Se la sveglia è accesa scrivo ON, se spenta visualizzo la data corrente
    oled.setCursor(0, 1);
    if (ALARM) {
      oled.print("ON");
    } else {
      oled.print(settimana[now.dayOfTheWeek()]);
      oled.print(" ");
      oled.print(now.day(), DEC);
      oled.print('/');
      oled.print(now.month(), DEC);
      oled.print('/');
      oled.print(now.year(), DEC);
    }

    // Se ora, minuti e secondi sono uguali a quelli impostati per la sveglia, allora vai allo stato 20 - void allarmeSveglia();
    if ( (ch == alarmh) && (cm == alarmm) && (cs == alarms)) {
      stato = 20;
      FIRST = true;
    }
    t1 = millis();
  }

  // Se premo il pulsante collegato al PIN 4, vado allo stato 1 - impostaOra();
  if (digitalRead(4)) {
    stato = 1;
    delay(200);
    FIRST = true;
  }

  // Se premo il pulsante collegato al PIN 5, vado allo stato 10 - accendiSveglia();
  if (digitalRead(5)) {
    stato = 10;
    delay(200);
    FIRST = true;
  }

  // Se premo il pulsante collegato al PIN 3, vado allo stato 30 - radioFM();
  if (digitalRead(3)) {
    stato = 30;
    delay(200);
    FIRST = true;
  }

  // Se premo il pulsante collegato al PIN 2, vado allo stato 40 - meteo();
  if (digitalRead(2)) {
    stato = 40;
    delay(200);
    FIRST = true;
  }
}

// Imposto la variabile seth a 0, nel void impostaOra() assegno alla variabile l'ora
int seth = 0;

// stato 1 - Qui imposto l'ora
void impostaOra() {       // Recupero l'ora dal RTC e viene assegnata a seth
  if (FIRST) {
    oled.clear();
    now = rtc.now();
    seth = now.hour();
    FIRST = false;
  }
  // Visualizzo a schermo che viene modificata l'ora, minuti e secondi sono tratteggiati
  oled.setCursor(0, 0);
  sprintf(str, "%02d", seth);
  oled.print(str);
  oled.print(" : -- : --");

  // Se premo il pulsante collegato al PIN 5, aumento le ore
  if (digitalRead(5)) {
    seth++;
    if (seth >= 24) seth = 0;
    delay(200);
  }
  // Se premo il pulsante al PIN 4, salvo l'ora impostata nel RTC e vado nello stato 02 - void impostaMinuti();
  if (digitalRead(4)) {
    rtc.adjust(DateTime(now.year(), now.month(), now.day(), seth, now.minute(), now.second()));
    stato = 02;
    FIRST = true;
    delay(200);
  }
}

// Imposto la variabile setm a 0, nel void impostaMinuti() assegno alla variabile i minuti
int setm = 0;

// stato 2 - Qui imposto i minuti
void impostaMinuti() {
  // Recupero i minuti dal RTC e li assegno alla variabile setm
  if (FIRST) {
    oled.clear();
    now = rtc.now();
    setm = now.minute();
    FIRST = false;
  }
  // Visualizzo a schermo che vengono modificati i minuti, ora e secondi sono tratteggiati
  oled.setCursor(0, 0);
  sprintf(str, "%02d", setm);
  oled.print("-- : ");
  oled.print(str);
  oled.print(" : --");

  // Se premo il pulsante collegato al PIN  5, aumento i minuti, arrivato a 60 riparto da zero
  if (digitalRead(5)) {
    setm++;
    if (setm >= 60) setm = 0;
    delay(200);
  }
  // Se premo il pulsante collegato al PIN 4, salvo i minuti impostati nel RTC e vado allo stato 3 - impostaSecondi()
  if (digitalRead(4)) {
    rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), setm, now.second()));
    stato = 03;
    FIRST = true;
    delay(200);
  }
}
// Imposto la variabile sets a 0, nel void impostaSecondi() assegno alla variabile i secondi
int sets = 0;

// stato 3 - Qui imposto i secondi
void impostaSecondi() {
  // Recupero i secondi dal RTC e li assegno alla variabile sets
  if (FIRST) {
    oled.clear();
    now = rtc.now();
    sets = now.second();
    FIRST = false;
  }
  // Visualizzo a schermo che vengono modificati i secondi, ora e minuti sono tratteggiati
  oled.setCursor(0, 0);
  sprintf(str, "%02d", sets);
  oled.print("-- : -- : ");
  oled.print(str);

  // Se premo il pulsante collegato al PIN  5, aumento i secondi, se arrivano a 60, riparte da zero
  if (digitalRead(5)) {
    sets++;
    if (sets >= 60) sets = 0;
    delay(200);
  }
  // Se premo il pulsante collegato al PIN 4, salvo i secondi impostati nel RTC e vado allo stato 0 - dataora()
  if (digitalRead(4)) {
    rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), now.minute(), sets));
    stato = 0;
    FIRST = true;
    delay(200);
  }
}

// stato 10 - Qui attivo la sveglia
void accendiSveglia() {
  if (FIRST) {
    oled.clear();
    oled.setCursor(0, 0);
    oled.print("Sveglia: ");
    FIRST = false;
  }
  // In base allo stato di ALARM, scrivo se la sveglia è attiva o spenta
  oled.setCursor(10, 0);
  if (ALARM) {
    oled.print("ON ");
  }
  else {
    oled.print("OFF");
  }
  // Se premo il pulsante collegato al PIN 5, cambio lo stato attuale della sveglia
  if (digitalRead(5)) {
    ALARM = !ALARM;
    delay(200);
  }
  // Premendo il pulsante del PIN 4, controllo lo stato della sveglia, se spenta vado alla schermata principale - stato 0, se accesa vado allo stato 11 - impostaOraSveglia()
  if (digitalRead(4)) {
    if (!ALARM) stato = 0;
    else stato = 11;
    FIRST = true;
    delay(300);
  }
}

// stato 11 - Qui imposto alla variabile alarmh l'ora corrente
void impostaOraSveglia() {
  if (FIRST) {
    oled.clear();
    now = rtc.now();
    alarmh = now.hour();
    FIRST = false;
  }
  oled.setCursor(0, 0);
  sprintf(str, "%02d", alarmh);
  oled.print(str);
  oled.print(" : -- : -- A");

  // Se premo il pulsante collegato al PIN 5, aumento le ore per la sveglia
  if (digitalRead(5)) {
    alarmh++;
    if (alarmh >= 24) alarmh = 0;
    delay(200);
  }
  // Se premo il pulsante collegato al PIN 4, vado allo stato 12 - impostaMinutiSveglia()
  if (digitalRead(4)) {
    stato = 12;
    FIRST = true;
    delay(200);
  }
}

// stato 12 - Qui imposto i minuti della sveglia
void impostaMinutiSveglia() {
  if (FIRST) {
    oled.clear();
    now = rtc.now();
    alarmm = now.minute();
    FIRST = false;
  }
  oled.setCursor(0, 0);
  sprintf(str, "%02d", alarmm);
  oled.print("-- : ");
  oled.print(str);
  oled.print(" : -- A");

  // Se premo il pulsante collegato al PIN 5, aumento i minuti della sveglia
  if (digitalRead(5)) {
    alarmm++;
    if (alarmm >= 60) alarmm = 0;
    delay(200);
  }
  // Se premo il pulsante collegato al PIN 4, torno alla schermata principale, stato 0 - dataora()
  if (digitalRead(4)) {
    stato = 0;
    FIRST = true;
    delay(200);
  }
}

// stato 20 - Si attiva quando, la data impostata per la sveglia è uguale a quella del RTC
void allarmeSveglia() {
  if (FIRST) {
    oled.clear();
    oled.setCursor(0, 0);
    oled.print("SVEGLIA!!");
    FIRST = false;
  }

  tone(6, 440, 100);
  delay(100);

  // Se premo uno dei 4 pulsanti, vado nello stato snooze
  if (digitalRead(4) || digitalRead(5) || digitalRead(3) || digitalRead(2)) {
    stato = 21;
    FIRST = true;
    delay(300);
  }
}

// Imposto una variabile globale posponi per contare 5 minuti dal posponi della sveglia
unsigned long tp, posponi;

// Creo un bool per la lunga pressione del tasto così da spegnere la sveglia
bool svegliaoff;
bool prev_svegliaoff;
unsigned long toff;
int statopulsanteoff = LOW;

// Attivo il posponi per 5 minuti, se premo il tasto 4 per 5 secondi disattivo la sveglia
void snooze() {
  noTone(6);
  oled.clear();
  oled.setCursor(0, 0);
  oled.print("POSPONI");
  if (FIRST) {
    posponi = millis() - tp;
    if (posponi >= 300000) {
      tp = millis();
      oled.clear();
      oled.setCursor(0, 0);
      oled.print("SVEGLIA!!");
      FIRST = false;
      tone(6, 440, 100);
      delay(100);
    }
    if (svegliaoff = digitalRead(4)) {
      if (!prev_svegliaoff && svegliaoff) {
        toff = millis();
      }
      if (prev_svegliaoff && !svegliaoff) {
        unsigned long tempotasto = millis() - toff;
        if (tempotasto > 5000) {
          stato = 0;
          FIRST = true;
          delay(300);
        }
      }
      prev_svegliaoff = svegliaoff;
    }
    stato = 0;
    FIRST = true;
    delay (300);
  }
}

// Stato 30 - Qui gestisco la radio FM
void radioFM() {
  Serial.print("funzione radio fm");
}

// Stato 40 - Qui gestisco i sensori per la temperatura, umidità e pressione barometrica
void meteo() {
  Serial.print("funzione meteo");
}

Certo, l'errore è mio e la colpa è della vista e della cattiva identazione. Ora ho copiato e incollato il tuo codice nel simulatore online ho premuto Ctrl+shift+i per attivare la formattazione automatica e mi sono reso conto che l'errore che ti ho segnalato non c'è.

Il perché è presto spiegato. La funzione loop() inizia con { e termina con }. Il framework di arduino chiama la funzione loop() la quale termina e il controllo torna al framework che chiama nuovamente la funzione loop() e questo accade all'infinito. Nella funzione loop() tu hai la tua state machina basata sullo switch case, quando la variabile stato ha valore 0 (zero) viene chiamata la funzione dataora(), la quale termina e restituisce il controllo al chiamante (la funzione loop() la quale a sua volta termina e come detto prima il framework la richiama nuovamente è se la variabile stato vale ancora 0 il tutto si ripete all'infinito. Ciò non accade poiché dentro la funzione dataota() hai previsto il modo di spezzare questo ciclo assegnando alla variabile stato un valore differente da 0 a seconda di quale tasto premi. Quindi la funzione dataora() verrà chiamata e terminera molte volte al secondo e a te serve salvare il tempo in t1 solo la prima volta.

Scusa il post un poco lungo ma dovevo farmi perdonare la svista.

Qui anche grazie a un topic con la partecipazione di Claudio_FF abbiamo trovato un modo alternativo alla variabile FIRST.

Ciao.