Arduino Uno WakeUp aus DeepSleep per RTC und Button

Hi zusammen,

mit dem ESP32 hab ich das mit dem DeepSleep und Aufwecken jetzt ganz gut raus.
Aber nun wollte ich gestern was Vergleichbares mit einem Arduino Uno (ATMEGA 328P, ATMEGA16U2 Board) machen und stoße auf ein paar Problemchen:

Nr.1: Beim ESP wird doch nach dem Aufwecken die void setup() durchlaufen... beim Arduino (Uno) scheint er direkt im Loop wieder weiterzumachen, korrekt? Oder gibt es auch einen DeepSleep-Mode, der nach dem Aufwecken nochmal die Setup durchläuft? Fand ich eigentlich aus diversen Gründen ganz praktisch.

Nr.2: Ich will unterscheiden, ob er WakeUp per Taste ausgelöst wurde oder per RTC-Alarm.

Wenn ich den unten aufgeführten Code (meinen ersten Versuch sozusagen) ausführe, bekomme ich nach dem Aufwecken per Taster mehrmals die Ausgabe "Aufgeweckt durch Taster" (warum mehrfach - ich vermute wegen des Prellens durch den Taster?) und danach geht er wieder in den DeepSleep wie erwartet.

Wenn der Arduino vom RTC Alarm geweckt wird, bekomme ich in einer Dauersalve "Aufgeweckt durch Alarm", ich vermute, weil das RTC-Modul hier am SQW-Pin dauerfeuert? Einschlafen tut mir der Arduino dann gar nicht mehr.

Lange Rede, kurzer Sinn: Wie schaff ich es, dass jeweils nur einmal eine Meldung kommt und er auch nach dem RTC-Alarm wieder einschläft?

Hier mein Code:

#include <RTClib.h>
#include "LowPower.h"

// Aufwachpin für Taster
#define WAKEUP_PIN 2
// Aufwachpin für RTC-Clock
#define CLOCK_INTERRUPT_PIN 3

unsigned long awake_timer = 10000, lastchange = 0;
 
RTC_DS3231 rtc;

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

    delay(1000);
    Serial.println("Aufgewacht!"); // Wird nur beim ersten Start angezeigt, nicht mehr nach dem Aufwachen...also anders als beim ESP32

    lastchange = millis();

    pinMode(LED_BUILTIN, OUTPUT);
    pinMode(WAKEUP_PIN, INPUT_PULLUP);
    pinMode(CLOCK_INTERRUPT_PIN, INPUT_PULLUP);

    // initializing the rtc
    if(!rtc.begin()) {
        Serial.println("RTC-Modul nicht gefunden!");
        Serial.flush();
        while (1) delay(10);
    }

    if(rtc.lostPower()) {
        rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    }

    rtc.disable32K();
    rtc.clearAlarm(1);
    rtc.clearAlarm(2);
    rtc.writeSqwPinMode(DS3231_OFF);
    rtc.disableAlarm(2);
}

void loop() {

     // LED kurz blinken lassen als Rückmeldung, dass der Loop durchlaufen wird nach dem Aufwachen
    digitalWrite(LED_BUILTIN, HIGH);  
    delay(1000); 
    digitalWrite(LED_BUILTIN, LOW); 
    delay(1000);  


    if (millis() - lastchange > awake_timer) {
      // Neuen Alarm setzen....
      rtc.setAlarm1(rtc.now() + TimeSpan(20),DS3231_A1_Second);

      attachInterrupt(digitalPinToInterrupt(WAKEUP_PIN), onButton, LOW);
      attachInterrupt(digitalPinToInterrupt(CLOCK_INTERRUPT_PIN), onAlarm, LOW);
      Serial.println("Einschlafen!");
      delay(1000);
      lastchange = millis();
      LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
      Serial.println("Dieser Text wäre nach esp_deep_sleep_start() nicht lesbar, warum hier?");
    }
}


void onButton() {
 // detachInterrupt(WAKEUP_PIN);
  lastchange = millis();
  Serial.println("Aufgewacht durch Taster");
}

void onAlarm() {
  //detachInterrupt(CLOCK_INTERRUPT_PIN);
  lastchange = millis();
  Serial.println("Aufgewacht durch Alarm");
}

Danke für eure "Entwicklungshilfe" :slight_smile:
LG

Moin @basementmedia ,

da sich noch niemand gemeldet hat, hier ein Versuch :wink:

  • In den Interruptroutinen die detach-Funktionen entkommentieren. Das sollte erneutes "Feuern" der Interrupts verhindern.
  • Nach der Zeile "LowPower.powerDown(...)" den Alarm löschen (clearAlarm(2)) oder sperren (disableAlarm(2)), damit sollte auch der Alarm generell ausgeschaltet werden ...

Konnte es leider nicht testen, da ich keinen RTC zur Hand habe und die sleep Modi bei Wokwi nicht unterstützt werden ...

Viel Erfolg!
ec2021

Level Interrupts feuern dauernd, solange der Pegel anliegt.

Ich würde den Interrupt (wenn man ihn denn überhaupt braucht) eher auf die Flanke legen. Dann kann der auf dem Potential bleiben, solange er will, ohne das etwas passiert.
Wobei man überlegen sollte, für welche handbetätigte Taste man überhaupt einen Interrupt braucht (nach meiner Meinung für keine)

Gruß Tommy

Der TO will den Controller per Interrupt aus dem sleep Modus wecken...

INT 0+1: Flanke tuts nicht im Tiefschlaf.

Alternativ: PCINT

Beim Wecken aus dem Tiefschlaf braucht es aber auch keine Interruptroutine oder?
Das Wecken sollte doch nach detektieren des Pegels laufen und dann ist der Pegel egal.

Gruß Tommy

Doch doch.

Ok, dann ziehe ich mein Veto zurück.
Die sollte man dann aber deaktivieren, wie @ec2021 empfohlen hat.

Gruß Tommy

Hab ich gemacht, meine onAlarm() sieht jetzt so aus:

void onAlarm() {
  detachInterrupt(CLOCK_INTERRUPT_PIN);
  detachInterrupt(WAKEUP_PIN); // Diese hier könnte ich mir denk ich sparen
  lastchange = millis();
  Serial.println("Aufgewacht durch Alarm");
}

Bringt leider keine Änderung, immer noch "Dauerfeuer".

Den Alarm 2 hab ich gar nicht belegt.
Wenn ich dagegen den Alarm 1 per clear lösche

if (millis() - lastchange > awake_timer) {
      attachInterrupt(digitalPinToInterrupt(WAKEUP_PIN), onButton, LOW);
      attachInterrupt(digitalPinToInterrupt(CLOCK_INTERRUPT_PIN), onAlarm, LOW);
      Serial.println("Einschlafen!");
      delay(1000);
      lastchange = millis();
      LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
      rtc.clearAlarm(1);
      Serial.println("Dieser Text wäre nach esp_deep_sleep_start() nicht lesbar, warum hier?");
    }

habe ich immer noch das gleiche Problem: Es wird per Dauerfeuer die Zeile "Aufgewacht durch Alarm" im Serial geschossen... ich blicks ned!

digitalPinToInterrupt() vergessen.
Beim attach wusstest du noch wie man es macht, beim detach ist es vergessen.
Als wenn es keine Doku geben würde.....

Nennen wir es einfach mal: Leichtsinnsfehler :slight_smile:
Danke, jetzt funktioniert's! :hugs: