Mkr1400 deep sleep + adafruit ds3231 interupt con 2 allarmi

Buongiorno a tutti, sto sviluppando una stazione remota che legge sensori e scatta ed invia immagini. Dato che va a batteria ho il problema di gestire i consumi e quello che ho fatto è lowpower.deepsleep + ds3231 che risveglia su 2 allarmi Alarm1 al minuto 15 e 45 tutte le ore (solo sensori) ed alarm 2 scatto foto ed invio solo alle 10:30 e 14:30 ogni giorno. Il problema che però riscontro è il risveglio che o non avviene oppure trascina uno dei due allarmi. questo è il codice per chi può darmi qualche dritta in merito.

Grazie per vs il contributo

#include <Wire.h>
#include <ArduinoLowPower.h>
#include "log.h"
#include "times_utils.h"
#include "sensors.h"
#include "display.h"
#include "camera.h"
#include "thingspeak_utils.h"
#include "common.h"

#define DS3231_INT_PIN A2
volatile bool wokeUp = false;

// ================== DEBUG ==================
#define DEBUG true
#define debugPrint(...)   if (DEBUG) Serial.print(__VA_ARGS__)
#define debugPrintln(...) if (DEBUG) Serial.println(__VA_ARGS__)

// aggiungere debug mode anche per deepsleep

// ================== Watchdog ==================
#define WATCHDOG_TIMEOUT (60UL * 60UL * 1000UL)  // 60 minuti = 3.600.000 ms era >> 240000UL
unsigned long lastLoopTime = 0;
unsigned long lastWatchdogCheck = 0;

void checkWatchdog() {
    // 🔹 Controlla se è trascorso troppo tempo dall’ultimo aggiornamento del watchdog
    //    - 'millis()' restituisce i millisecondi trascorsi dall’avvio della scheda
    //    - 'lastWatchdogCheck' è l’ultimo momento in cui il loop è stato eseguito
    //    - Se la differenza supera la soglia WATCHDOG_TIMEOUT, si considera un blocco
    if (millis() - lastWatchdogCheck > WATCHDOG_TIMEOUT) {

        // 🔹 Legge l’orario corrente dal modulo RTC, applicando l’ora legale
        //    (serve per loggare nel file l’istante del reset)
        DateTime now = applyDST(rtc.now());

        // 🔹 Crea una stringa descrittiva del tipo:
        //    "Watchdog timeout: reset a 2025-11-03 17:09:53"
        char msg[64];
        snprintf(msg, sizeof(msg),
                 "Watchdog timeout: reset a %04d-%02d-%02d %02d:%02d:%02d",
                 now.year(), now.month(), now.day(),
                 now.hour(), now.minute(), now.second());

        // 🔹 Stampa il messaggio sulla seriale per debug
        debugPrintln(msg);

        // 🔹 Scrive il messaggio nel file di log su SD (tramite logMessage)
        //    → utile per capire, dopo il riavvio, che il reset è stato causato dal watchdog
        logMessage(msg);

        // 🔹 Piccolo ritardo per permettere il completamento della scrittura su SD
        //    (senza questo, c’è il rischio che la MCU si resetti prima di finire il log)
        // 🔸 Attendo 1 secondo prima del reset per assicurare:
        //    - completamento scrittura log su SD  
        //    - flush dei buffer
        //    - rilascio bus SPI/I2C
        delay(1000);

        // 🔹 Esegue un reset hardware del microcontrollore
        //    - NVIC_SystemReset() è una chiamata diretta al vettore di reset ARM Cortex-M
        //    - Riavvia completamente la scheda come se fosse premuto il pulsante RESET
        NVIC_SystemReset();
    }
}

// ================== LED lampeggio ==================
void lampeggiaLED(int times, int onTime = 100, int offTime = 100) {
    for (int i = 0; i < times; i++) {
        digitalWrite(LED_BUILTIN, HIGH);
        delay(onTime);
        digitalWrite(LED_BUILTIN, LOW);
        if (i < times - 1) delay(offTime); // No delay dopo l'ultimo
    }
}

// ================== ISR ==================
void wakeUp() { wokeUp = true; }

// ================== Funzione di reset ==================
void resetBoard(const char *reason = "Richiesta di reset manuale da seriale") {
    delay(100);
    Wire.begin();
    DateTime now = getSafeNow();

    if (now.year() < 2000 || now.year() > 2100) {
        if (rtc.lostPower()) {
            debugPrintln("RTC ha perso potenza, reimposto data/ora compilazione");
            rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
        }
        now = rtc.now();
    }

    now = applyDST(now);
    char msg[64];
    snprintf(msg, sizeof(msg), "%s alle %02d:%02d:%02d",
             reason, now.hour(), now.minute(), now.second());
    debugPrintln(msg);
    delay(500);
    NVIC_SystemReset();
}

// ================== Setup ==================
void setup() {
    pinMode(DS3231_INT_PIN, INPUT_PULLUP);
    pinMode(LED_BUILTIN, OUTPUT);
    lampeggiaLED(5, 1000, 1000);
    digitalWrite(LED_BUILTIN, LOW); // spengo il led base in caso rimanga acceso

    #if DEBUG
    Serial.begin(9600);
    unsigned long startSerial = millis(); //Salva il tempo attuale (in millisecondi dall’avvio) per calcolare un timeout.
    while (!Serial && millis() - startSerial < 2000); // Il ciclo resta in attesa al massimo 2000 ms (=2 secondi). Se dopo 2s la Serial non è disponibile, prosegue comunque.
                                                      // ➡️ Questo evita che lo sketch resti bloccato se il monitor seriale non è collegato.
    debugPrintln("=== Avvio sistema meteo remoto ===");
    debugPrintln("Comandi: SCATTA / EINK / INVIO-IOT / RESET");
    #endif

    Wire.begin(); // Inizializza il bus I²C (necessario per sensori come SHT30, BME280, DS3231, ecc.). 
                  //-->> nei file .h e.cpp ✅ Inclusione (#include <Wire.h>) → sì, in tutti i file che usano l’I²C.
    pinMode(EPD_CS, OUTPUT);
    pinMode(SD_CS_PIN, OUTPUT);
    digitalWrite(EPD_CS, HIGH);
    digitalWrite(SD_CS_PIN, HIGH);

    sensorsInit(); // Inizializzo i sensori
  
    // Avvia prima SD
            initCommonPins();                 // Definizione dell'uso dei pins come prima cosa
            selectSD();                       // Disattiva il display e attiva la SD
            bool sdOK = sd.begin(SD_CONFIG);  // Inizializza SD
            releaseSD();                      // Dsiattiva SD
           
            if (!sdOK) {
                debugPrintln("⚠️ SD non trovata!");
            } else {
                debugPrintln("✅ SD inizializzata correttamente");
            }

            // Solo dopo aver diattivato la SD
            selectEPD();         // Attivo il display
            display.begin();     // Inizializzo il dispaly 
            releaseEPD();        // Disattiva il display

    
            if (!rtc.begin()) {  // Inizializzo il modulo RTC
                debugPrintln("❌ RTC non trovato! Controlla collegamenti I2C");
                while (1);
            }

                                // Inizializzo la CAM
            if (!cameraInit()) debugPrintln("⚠️ Camera non inizializzata! Scatto foto 📸 e upload dati 📝 potrebbero non funzionare.");

    // Disattiva uscita onda quadra, pulisce eventuali allarmi pendenti
    rtc.disable32K();                // uscita che non serve tenere attiva quindi la disabilitiamo
    rtc.writeSqwPinMode(DS3231_OFF); // INT/SQW come interrupt
    rtc.clearAlarm(1);               //cancella l’eventuale stato di attivazione di Alarm1
    rtc.clearAlarm(2);               //cancella l’eventuale stato di attivazione di Alarm2

    //DateTime now = getSafeNow(); // applico l'ora legale calcolata in time_utils.h e .cpp
    DateTime now = rtc.now();
  
    // ================== Calcolo prossimo Alarm1 (Lettura sensori + invio su thingspeak + aggiorna Eink) ==================
    // Alarm1: eventi periodici ogni mezz'ora, sempre al minuto 15 o 45.
    // Esempio: 08:15, 08:45, 09:15, 09:45, ecc. tutta la giornata
    DateTime nextAlarm1 = nextAlarm1Time(now);
    rtc.setAlarm1(nextAlarm1, DS3231_A1_Minute);
    /* Qui usi la modalità DS3231_A1_Minute, che confronta fino ai minuti: “L’allarme scatterà ogni ora quando i minuti e i secondi coincidono.”
    In pratica: Scatterà ogni ora, allo stesso minuto e secondo di nextAlarm1. Non considera l’ora! (quindi se metti nextAlarm1 = 08:15, scatta alle 08:15, 09:15, 10:15, ecc.) */

     // ================== Calcolo prossimo Alarm2 (Lettura sensori + scatto foto + invio + aggiorna Eink)==================
    // Alarm2: eventi a orari fissi della giornata.
    // Orari configurati: 08:20, 11:45, 15:00, 22:30
    DateTime nextAlarm2 = nextAlarm2Time(now);
    rtc.setAlarm2(nextAlarm2, DS3231_A2_Minute);

    LowPower.attachInterruptWakeup(DS3231_INT_PIN, wakeUp, FALLING); /// SPOSTARE NELLA GESTIONE DELLE COMMON

    debugPrint("Prossimo Alarm1 (💻 DATI): "); debugPrintln(nextAlarm1.timestamp());
    debugPrint("Prossimo Alarm2 (📸 FOTO): "); debugPrintln(nextAlarm2.timestamp());

    lastLoopTime = millis();
    
}

// ================== LOOP ==================
void loop() {
    checkWatchdog(); // == spento in fase di debug
    lastLoopTime = millis();

      // ===== Controllo comandi da Serial =====
      // se apri il Monitor Serial a 9600 e scrivi SCATTA\n
      if (Serial && Serial.available()) {  
      // Se la Serial è attiva e ci sono dati ricevuti dal PC/monitor seriale
      String cmd = Serial.readStringUntil('\n');  // Legge la stringa inviata fino al carattere di newline ('\n')
      cmd.trim();  // Rimuove spazi bianchi e caratteri di ritorno a capo all'inizio/fine
  
      if (cmd.equalsIgnoreCase("SCATTA")) {
          delay(100);  // piccolo ritardo per stabilizzare SPI/Serial
          DateTime now = getSafeNow();
          SensorData d = sensorsRead();
  
          if (!cameraInit()) {
              debugPrintln("SCATTA: cameraInit fallita");
          } else {
              delay(150);  // garantisce che la camera sia pronta
  
              if (takeAndUpload()) {
                  debugPrintln("SCATTA: foto scattata e inviata");
                  displayUpdate(d, now, "Foto scattata e inviata");
              } else {
                  debugPrintln("SCATTA: errore scatto/upload");
              }
          }
      }
      else if (cmd.equalsIgnoreCase("EINK")) {
          debugPrintln("EINK: aggiornamento manuale display");
          SensorData d = sensorsRead();
          DateTime now = getSafeNow();
          displayUpdate(d, now, "Aggiornamento manuale E-Ink");
      }
              else if (cmd.equalsIgnoreCase("INVIO-IOT")) {
              debugPrintln("INVIO-IOT: invio manuale su ThingSpeak");
              SensorData d = sensorsRead();
              sensorsPrint(d);
              uploadToThingSpeak(d);
              DateTime now = applyDST(rtc.now());
              displayUpdate(d, now, "Dati inviati manualmente");
              logMessage("INVIO-IOT manuale completato");
          }
      else if (cmd.equalsIgnoreCase("RESET")) {
          debugPrintln("Confermi reset? Digita SI per procedere.");
          while (!Serial.available());
          String confirm = Serial.readStringUntil('\n');
          confirm.trim();
  
          if (confirm.equalsIgnoreCase("SI")) {
              resetBoard("Reset manuale confermato");
          } else {
              debugPrintln("Reset annullato.");
          }
      }
      else {
          debugPrintln("Comando non riconosciuto. Usa SCATTA / EINK / INVIO-IOT / RESET.");
      }
  
      delay(2000);
  }

   if (wokeUp) {
        wokeUp = false;
        delay(100); //stabilizza
        Wire.begin(); //<-- RE-INIZIALIZZA IL BUS I2C! Al risveglio, il bus è in uno stato indefinito
     
        DateTime now = applyDST(rtc.now()); //DateTime now = rtc.now(); // DateTime now = getSafeNow();
        debugPrint("Sveglio alle: "); debugPrintln(now.timestamp());

     // ⏲️ // ===== Alarm1 =====
        if (rtc.alarmFired(1)) {
            debugPrintln(">>> Alarm1 (💻 DATI): lettura sensori + invio a ThingSpeak");
            
            lampeggiaLED(5, 500, 500);

            SensorData d = sensorsRead();
            sensorsPrint(d);
            gsmConnect(); // aggiunto qui collegamento GSM 
            uploadToThingSpeak(d); //attenzione i pin schermo bloccano lo scketch e gli allarmi occhio alla connesione doppia gsm con invio cam
            delay(150);
            gsmDisconnect();
            delay(1000);
            releaseSD();                      // Dsiattiva SD
           //displayUpdate(d, now, "Dati inviati"); //funzione non usata per stampa su eink che ha refresh di 180000ms -> occhio che blocca INT PIN a LOW
            selectEPD();         // Attivo il display
            releaseEPD();        // Disattiva il display           

            char msg1[64];
            snprintf(msg1, sizeof(msg1),
                     "Alarm1: dati inviati alle %02d:%02d:%02d",
                     now.hour(), now.minute(), now.second());
            logMessage(msg1);
            delay(100);
          
            rtc.clearAlarm(1);    // clearAllRtcAlarms(rtc); // ✅ invece di rtc.clearAlarm(1)
            rtc.clearAlarm(2);   // ✅ pulisci anche A2F se rimasto attivo
            rtc.writeSqwPinMode(DS3231_OFF);   // 🔧 Rilascia INT dopo gestione allarme
            bool a1 = rtc.alarmFired(1);
            bool a2 = rtc.alarmFired(2);
            debugPrint("Dopo clear: A1F="); debugPrint(a1);
            debugPrint(" A2F="); debugPrintln(a2);
            
            
            DateTime nextAlarm1 = nextAlarm1Time(now); // ✅ Imposta prossimo Alarm1
            //rtc.setAlarm1(nextAlarm1, DS3231_A1_Minute);
            rtc.setAlarm1(DateTime(0, 0, 0, nextAlarm1.hour(), nextAlarm1.minute(), 0), DS3231_A1_Minute); // dichiarazione target più precisa e completa

            debugPrint("Prossimo Alarm1 (💻 DATI) - Invio Dati ambientali: "); debugPrintln(nextAlarm1.timestamp());
            debugPrint("INT pin dopo set: ");
            debugPrintln(digitalRead(DS3231_INT_PIN) == LOW ? "LOW ⚠️" : "HIGH ✅");

    // --- ⚠️ Controllo automatico INT stuck LOW ---
    unsigned long startCheck = millis();
    while (digitalRead(DS3231_INT_PIN) == LOW && millis() - startCheck < 2000) {
        debugPrintln("INT ancora LOW... attendo rilascio");
        delay(100);
    }

    if (digitalRead(DS3231_INT_PIN) == LOW) {
        debugPrintln("⚠️ INT bloccato LOW dopo Alarm1 → reset RTC comm.");
        Wire.end();
        delay(100);
        Wire.begin();
        rtc.clearAlarm(1);
        rtc.clearAlarm(2);
        rtc.writeSqwPinMode(DS3231_OFF);
    }            
            debugPrintln(" === Debug di ricontrollo ===");
            debugPrint("A1F="); debugPrint(rtc.alarmFired(1));
            debugPrint("  A2F="); debugPrintln(rtc.alarmFired(2));
        }

     
     
// ⏲️ // ===== Alarm2 =====
        if (rtc.alarmFired(2)) {
            debugPrintln(">>> Alarm2 (FOTO): lettura sensori + scatto foto 📷 + upload 📤");
            lampeggiaLED(10, 100, 100);          
            SensorData d = sensorsRead();
            sensorsPrint(d);
            // Nessun invio su thingspeak per evitare un doppio gsmconnect

            if (takeAndUpload()) {
                //displayUpdate(d, now, "Foto scattata e inviata");
                logMessage("Alarm2: foto scattata e inviata");
            } else {
                //displayUpdate(d, now, "Errore foto/upload");
                logMessage("Alarm2: errore foto/upload");
            }
           delay(1000); // lascia stabilizzare INT prima di gestire l’allarme

            // --- ⚙️ Reset del flag e rilascio INT ---

            rtc.clearAlarm(2);   // Cancella flag A2F (appena scattato)
            rtc.clearAlarm(1);   // Cancella anche A1F nel dubbio
            rtc.writeSqwPinMode(DS3231_OFF);       // 🔧 Rilascia il pin INT (HIGH)
              bool a1 = rtc.alarmFired(1);
              bool a2 = rtc.alarmFired(2);
              debugPrint("Dopo clear: A1F="); debugPrint(a1);
              debugPrint(" A2F="); debugPrintln(a2);

            DateTime nextAlarm2 = nextAlarm2Time(now);            
            rtc.setAlarm2(DateTime(0, 0, 0, nextAlarm2.hour(), nextAlarm2.minute(), 0), DS3231_A2_Minute);
            debugPrint("Prossimo Alarm2 impostato a: ");
            debugPrintln(nextAlarm2.timestamp());

            debugPrint("Prossimo Alarm2 - Invio foto 📷: "); debugPrintln(nextAlarm2.timestamp());

 // --- ⚠️ Controllo automatico INT stuck LOW ---
    unsigned long startCheck = millis();
    while (digitalRead(DS3231_INT_PIN) == LOW && millis() - startCheck < 2000) {
        debugPrintln("INT ancora LOW... attendo rilascio");
        delay(100);
    }

    if (digitalRead(DS3231_INT_PIN) == LOW) {
        debugPrintln("⚠️ INT bloccato LOW dopo Alarm2 → reset RTC comm.");
        Wire.end();
        delay(100);
        Wire.begin();
        rtc.clearAlarm(1);
        rtc.clearAlarm(2);
        rtc.writeSqwPinMode(DS3231_OFF);
    }


            // 🔍 verifica stato INT
            pinMode(DS3231_INT_PIN, INPUT_PULLUP);
            for (int i = 0; i < 5 && digitalRead(DS3231_INT_PIN) == LOW; i++) {
                debugPrintln("INT ancora LOW, ritento...");
                delay(100);
        }

    debugPrint("INT pin dopo reset: ");
    debugPrintln(digitalRead(DS3231_INT_PIN) == LOW ? "LOW ⚠️" : "HIGH ✅");
          
        }
    }

    delay(20);
    
    LowPower.sleep(); // attiva in produzione

Buongiorno,

... so che sei un vecchio utente del forum :slight_smile: , ma ... purtroppo non riesco a trovare il tuo post di presentazione :confused:, per cui, cortesemente, come prima cosa, leggi attentamente il REGOLAMENTO di detta sezione, (... e, per evitare future possibili discussioni/incomprensioni, prestando molta attenzione al punto 15), dopo di che, come da suddetto regolamento (punto 16.7), fai la tua presentazione NELL'APPOSITA DISCUSSIONE spiegando bene quali esperienze hai in elettronica e programmazione, affinché noi possiamo conoscere la tua esperienza ed esprimerci con termini adeguati.

Grazie,

Guglielmo

P.S.: Ti ricordo che, purtroppo, fino a quando non sarà fatta la presentazione nell’apposita discussione, nel rispetto del succitato regolamento nessuno ti risponderà (eventuali risposte o tuoi ulteriori post, verrebbero temporaneamente nascosti), quindi ti consiglio di farla al più presto. :wink: