Alarm programmieren mit DS3231

Hallo zusammen,

ich möchte mit dem DS3231 und der library DS3231.h einen Alarm programmieren der 11 Stunden nach der aktuellen Zeit auslöst. Hat jemand eine Ahnung wie man das am besten programmiert?

Minutengenau oder Sekundengenau?

Am einfachsten ist es die aktuelle Zeit in unix-timestamp herunterrechen, 11h ( = 39600 Sekunden) dazurechenen und dann kontrollieren ob die Zeit vorbei ist.
Umrechnung zB UnixTime - Arduino Reference

Alternativ kannst Du mit der jetzigen Zeit und Datum die zukünftige Zeit/Datum errechnen. Dann mußt Du aber den Übertrag der Stunden/Tag/Monat/Jahr berücksichtigen.

Grüße Uwe

Möchte es sekundengenau haben. Hast du vll einen Programmcode dafür ?

Schau das Beispiel der Bibliothek an.
Grüße Uwe

Komme ehrlich gesagt mit dem Beispiel nicht klar und weiß ned wie ich zu dem aktuellen Zeitpunkt die 11h dazurechnen kann, dass es funktioniert?

Am liebsten wäre mir so ein Befehl

alarmzeit = aktuelleZeit + 11h;

Probiere es doch mal mit dieser lib
https://github.com/RobTillaart/CountDown

Dort kannst du einen countdown laufen lassen. So könntest du mithilfe deiner rtc sagen: starte countdown zu bestimmter Uhrzeit und löse alarm aus wenn der countdown auf 0 ist.

Ich persönlich finde, dass die lib sehr einfach zu verstehen ist und dein Vorhaben damit realisierbar ist.

Aber den Countdown müsste doch der Arduino abchecken und der schläft ja :sweat_smile:

Streng Dich mal nen bisschen an….

Wie der schläft? Das lese ich jetzt zum 1. mal.
Ich habe das so verstanden, dass auf deinem Arduino eine rtc läuft und wenn eine bestimmte Uhrzeit erreicht ist, soll 11 Stunden später ein Alarm (buzzer) losgehen. Das der in der Zeit 11 Stunden schlafen soll war mir so bisher nicht bewusst.

Aber dann evtl. ein anderer Ansatz. Muss es ein Arduino sein? Oder würde auch ein seeeduino Xiao gehen? Den kannst du ganz einfach für eine festgelegte Zeit schlafen legen. Wie das geht wird hier https://stuartsprojects.github.io/2022/10/03/Seeeduino_XIAO.html
beschrieben. So könntest du mithilfe deiner rtc den xiao zu einer bestimmten Uhrzeit für eine Festgelegte Zeit schlafen legen. Wenn er wieder aufwacht, kannst du deinen code ganz normal ausführen lassen. Programmieren lässt sich so ein xiao eie ein arduino.

Falls du wissen willst um was es geht:

Hallo,

die Lib von Uwe ist ganz einfach anzuwenden. Man muss nur wissen das in Time Libs alles in Sekunden (seit 1970) rechnen. In den Libs wird intern alles korrigiert, wenn richtig programmiert, wovon ich ausgehen. Der Übergang von April zu Mai funktioniert jedenfalls.
Du gibt der Lib das aktuelle Datum und Zeit.
getUnix() gibt dir Sekunden (seit 1970) zurück.

getDateTime(x) berechnet intern mit den übergegeben Sekunden alle Einzelwerte.
Bsp. möchtest du 8 Tage vorausrechnen.
Du weißt das ein Tag 86400s hat. Mein Bsp. rechnet 8 Tage dazu.
Das Ergebnis der aktuellen Sekunden + dem Offset wird getDateTime(x) übergeben.
Anschließend holt man sich die Einzelwerte.
Das gleiche Prinzip machste mit Stunden oder sonstwas.
Fazit, wenn du alles in Sekunden umrechnest und mit der unixTime der Lib verrechnet klappt alles.

Am Ende gibst du die Einzelwerte der RTC Lib, müsste BCD Format sein, was eine RTC Lib intern machen sollte.

#include <UnixTime.h>

UnixTime stamp(1);  // GMT für Berlin +1

void setup() {
  Serial.begin(9600);
  // ========= Дата и время в UNIX ==========
  // установка даты и времени библиотеки через функцию setDateTime
  // setDateTime(год, месяц (с 1), день (с 1), час, минута, секунда);
  // либо stamp.year = 2021 и так далее
  //stamp.setDateTime(2021, 1, 1, 0, 0, 0);
  stamp.setDateTime(2024, 4, 29, 19, 9, 10);   // YYYY,MM,DD, HH:MM:SS

  // getUnix() - получить unix время (выполняется ~56 us на AVR)
  const unsigned long unix = stamp.getUnix();
  Serial.print("\nunix "); Serial.println(unix);

  // ========= UNIX в дату и время ==========
  // конвертировать unix в дату и время
  // getDateTime(unix stamp) выполняется ~500 us на AVR
  // https://www.unixtimestamp.com/index.php
  
  // забираем вот так
  //stamp.getDateTime(unix);
  Serial.print(stamp.day);    Serial.print('.');
  Serial.print(stamp.month);  Serial.print('.');
  Serial.print(stamp.year);   Serial.print(", ");
  Serial.print(stamp.hour);   Serial.print(':');
  Serial.print(stamp.minute); Serial.print(':');
  Serial.print(stamp.second);
  //Serial.print(stamp.dayOfWeek);  // 1 понедельник, 7 воскресенье
  Serial.println('\n');

  const unsigned long var = 86400UL * 8; // 8 Tage in Sekunden
  stamp.getDateTime(unix + var);
  
  Serial.print(stamp.day);    Serial.print('.');
  Serial.print(stamp.month);  Serial.print('.');
  Serial.print(stamp.year);   Serial.print(", ");
  Serial.print(stamp.hour);   Serial.print(':');
  Serial.print(stamp.minute); Serial.print(':');
  Serial.print(stamp.second);
  Serial.println('\n');
}

void loop() {
}

Omg jetzt blick ich wieder durch, die rtc hatte ich sogar selbst vorgeschlagen :wink:

Danke schonmal :slight_smile:

#include <avr/sleep.h>
#include <RTClib.h>
#include <Wire.h>
#include <time.h>
#include <Adafruit_BusIO_Register.h>

// Setup clock
RTC_DS3231 rtc;

constexpr byte PIN_SQW {3}; // the pin to receive signal from DS3231
constexpr byte MAX_DURCHLAUFE {4};    // Maximale Anzahl von Durchläufen pro Tag
constexpr byte PIN_PUMPE {13};
constexpr unsigned long PUMPENLAUFZEIT {5*1000UL};
unsigned int durchlaufe=0;

//Datum und Uhrzeit definieren
byte Sekunden = 0;
byte Minuten = 55;
byte Stunden = 19;
byte Tag = 1;
byte Monat = 5;
byte Jahr = 2024;

void setup() {

  Serial.begin(115200);
    //begin I2C communication
  Wire.begin();

  // configure I/O pins on the Arduino
  pinMode(PIN_PUMPE, OUTPUT);
  pinMode(PIN_SQW, INPUT);
  pinMode(13, OUTPUT);

  // Set the DS3231 clock mode to 24-hour
  //myRTC.setClockMode(false); // false = not using the alternate, 12-hour mode

  //Datum und Uhrzeit setzen
  rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));//rtc.adjust(DateTime(Jahr, Monat, Tag, Stunden, Minuten, Sekunden));

  

  // enable output on the 32K pin of the DS3231
  rtc.disable32K();
 
  // stop oscillating signals at SQW Pin
  // otherwise setAlarm1 will fail
  rtc.writeSqwPinMode(DS3231_OFF);
  
  //Interrupt definieren
  attachInterrupt(digitalPinToInterrupt(PIN_SQW), AUFWACHEN, FALLING);

  // set alarm 1, 2 flag to false (so alarm 1, 2 didn't happen so far)
  // if not done, this easily leads to problems, as both register aren't reset on reboot/recompile
  rtc.clearAlarm(1);
  rtc.clearAlarm(2);

  //deaktiviert Alarm 2 
  // disable Alarm 2 interrupt
  rtc.disableAlarm(2);
    
}

void loop() {
  while (durchlaufe < MAX_DURCHLAUFE)
  {
    rtc.setAlarm1(rtc.now()+TimeSpan(0,0,0,20), DS3231_A1_Second);
    PUMPE_AN(); 
    //schlafen gehen
    go_to_sleep();
    durchlaufe++;
  }
  
  rtc.setAlarm1(rtc.now()+TimeSpan(0,0,1,0), DS3231_A1_Minute);
  go_to_sleep();
  
  durchlaufe=0;
}

// the interrupt service routine 
void PUMPE_AN() {
  digitalWrite(PIN_PUMPE, HIGH);
  delay(PUMPENLAUFZEIT);
  digitalWrite(PIN_PUMPE, LOW);
  }

void go_to_sleep()
{
  cli();
  //Schlafen 
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // EDIT: could also use SLEEP_MODE_PWR_DOWN for lowest power consumption.
  sleep_enable();
  sei();
  sleep_mode();   // Now enter sleep mode.
  // The program will continue from here after the WDT timeout
  sleep_disable();   // First thing to do is disable sleep.
}

void AUFWACHEN()
{
}

Habs jetzt so probiert. Zum Testen statt einer Pumpe die interne LED des Arduino benutzt. Leider leuchtet das ganze 1x 5 Sekunden auf, geht dann in den Schlaf 15 Sekunden lang, wacht wieder auf, leuchtet 5 Sekunden, sollte dann wieder nur 15 Sekunden schlafen aber schläft und wacht nicht mehr auf ...
Findet im Code jemand den Fehler?

Die DS3231 hat 2 Alarmregister, die einen Interrupt erzeugen kann und den Kontroller aufwecken kann.
Da muß man aber die Zieluhrzeit und Datum berechnen und diese als Alarmzeit abspeichern.

Grüße Uwe.

#include <DS3231.h>
#include <wire.h>
#include <time.h>
#include <UnixTime.h>

// Setup clock
DS3231 myRTC;

UnixTime stamp(1); //GMT für Berlin +1

constexpr byte PIN_SQW {3}; // the pin to receive signal from DS3231
constexpr byte MAX_DURCHLAUFE {1};    // Maximale Anzahl von Durchläufen pro Tag
constexpr byte PIN_PUMPE {13};
constexpr unsigned long PUMPENLAUFZEIT {10*1000UL};
unsigned int durchlaufe=0;


//Datum und Uhrzeit definieren
byte Sekunden = 0;
byte Minuten = 55;
byte Stunden = 19;
byte Tag = 1;
byte Monat = 5;
byte Jahr = 2024;

//aktuelle Zeit
byte aktuelleSekunde;
byte aktuelleMinute;
byte aktuelleStunde;
byte aktuellerTag;
byte aktuellerMonat;
byte aktuellesJahr;

byte Wecktag;
byte Weckstunde;
byte Weckminute;
byte Wecksekunde;

bool h12;
bool ampm;
bool century = false;

byte alarmDay, alarmHour, alarmMinute, alarmSecond, alarmBits;
bool alarmDy, alarmH12Flag, alarmPmFlag;

void setup() {

  Serial.begin(115200);
  //begin I2C communication
  Wire.begin();

  // configure I/O pins on the Arduino
  pinMode(PIN_PUMPE, OUTPUT);
  pinMode(PIN_SQW, INPUT);
  pinMode(13, OUTPUT);

  // Set the DS3231 clock mode to 24-hour
  myRTC.setClockMode(false); // false = not using the alternate, 12-hour mode

  //Datum und Uhrzeit setzen
  myRTC.setSecond(Sekunden);
  myRTC.setMinute(Minuten);
  myRTC.setHour(Stunden);
  myRTC.setDoW(Tag);
  myRTC.setMonth(Monat);
  myRTC.setYear(Jahr);

  digitalWrite(PIN_PUMPE, HIGH);

  // enable output on the 32K pin of the DS3231
  myRTC.enable32kHz(false);
  
  //Interrupt definieren
  attachInterrupt(digitalPinToInterrupt(PIN_SQW), AUFWACHEN, FALLING);

  //deaktiviert Alarm 2 
  // disable Alarm 2 interrupt
  myRTC.turnOffAlarm(2);
  // clear Alarm 2 flag
  myRTC.checkIfAlarm(2);
  
}

void loop() 
{
  for(durchlaufe=0;durchlaufe<MAX_DURCHLAUFE;durchlaufe++)
  {
    //Alarm setzen
    myRTC.turnOffAlarm(1);
    const unsigned long ZeitbisAlarm1=30UL;//Sekunden
    //WECKZEIT_BERECHNEN(ZeitbisAlarm1);

  aktuelleSekunde = myRTC.getSecond();
  aktuelleMinute = myRTC.getMinute();
  aktuelleStunde = myRTC.getHour(h12,ampm);
  aktuellerTag = myRTC.getDate();
  aktuellerMonat = myRTC.getMonth(century);
  aktuellesJahr = myRTC.getYear();

  stamp.setDateTime(aktuellesJahr, aktuellerMonat, aktuellerTag, aktuelleStunde, aktuelleMinute, aktuelleSekunde);

  Serial.print("Alarm1");

  const unsigned long unix = stamp.getUnix();
  Serial.print("\nunix "); Serial.println(unix);
  Serial.print(stamp.day);    Serial.print('.');
  Serial.print(stamp.month);  Serial.print('.');
  Serial.print(stamp.year);   Serial.print(", ");
  Serial.print(stamp.hour);   Serial.print(':');
  Serial.print(stamp.minute); Serial.print(':');
  Serial.print(stamp.second);
  Serial.println('\n');

  stamp.getDateTime(unix + ZeitbisAlarm1);
  Serial.println('\n');
  
  Serial.print(stamp.day);    Serial.print('.');
  Serial.print(stamp.month);  Serial.print('.');
  Serial.print(stamp.year);   Serial.print(", ");
  Serial.print(stamp.hour);   Serial.print(':');
  Serial.print(stamp.minute); Serial.print(':');
  Serial.print(stamp.second);
  Serial.println('\n');

    //Alarm1 setzen
    //0b00001110(when seconds match);0b00001100(when seconds and minutes match); 0b00001000 (when hours, minutes and seconds match)
    myRTC.setA1Time(Wecktag, Weckstunde, Weckminute, Wecksekunde, 0b00001110, false, false, false); 
    // enable Alarm 1 interrupts
    myRTC.turnOnAlarm(1);
    // clear Alarm 1 flag
    myRTC.checkIfAlarm(1);
    //Pumpe einschalten
    PUMPE_AN(); 
    //schlafen gehen
    go_to_sleep();
  }
    //Schlafen setzen für 11h
    //Alarm setzen
    myRTC.turnOffAlarm(1);
    const unsigned long ZeitbisAlarm2=40UL;//Sekunden
    //WECKZEIT_BERECHNEN(ZeitbisAlarm2);

  aktuelleSekunde = myRTC.getSecond();
  aktuelleMinute = myRTC.getMinute();
  aktuelleStunde = myRTC.getHour(h12,ampm);
  aktuellerTag = myRTC.getDate();
  aktuellerMonat = myRTC.getMonth(century);
  aktuellesJahr = myRTC.getYear();

  stamp.setDateTime(aktuellesJahr, aktuellerMonat, aktuellerTag, aktuelleStunde, aktuelleMinute, aktuelleSekunde);

  Serial.print("Alarm2");

  const unsigned long unix = stamp.getUnix();
  Serial.print("\nunix "); Serial.println(unix);
  Serial.print(stamp.day);    Serial.print('.');
  Serial.print(stamp.month);  Serial.print('.');
  Serial.print(stamp.year);   Serial.print(", ");
  Serial.print(stamp.hour);   Serial.print(':');
  Serial.print(stamp.minute); Serial.print(':');
  Serial.print(stamp.second);
  Serial.println('\n');

  stamp.getDateTime(unix + ZeitbisAlarm2);
  Serial.print(unix + ZeitbisAlarm2);
  Serial.println('\n');
  
  Serial.print(stamp.day);    Serial.print('.');
  Serial.print(stamp.month);  Serial.print('.');
  Serial.print(stamp.year);   Serial.print(", ");
  Serial.print(stamp.hour);   Serial.print(':');
  Serial.print(stamp.minute); Serial.print(':');
  Serial.print(stamp.second);
  Serial.println('\n');

  byte Wecktag = stamp.day;
  byte Weckstunde = stamp.hour;
  byte Weckminute = stamp.minute;
  byte Wecksekunde = stamp.second;

    //Alarm2 setzen
    //0b00001110(when seconds match);0b00001100(when seconds and minutes match); 0b00001000 (when hours, minutes and seconds match)
    myRTC.setA1Time(Wecktag, Weckstunde, Weckminute, Wecksekunde, 0b00001110, false, false, false); 
    // enable Alarm 1 interrupts
    myRTC.turnOnAlarm(1);
    // clear Alarm 1 flag
    myRTC.checkIfAlarm(1);
    //Pumpe einschalten
    PUMPE_AN(); 
    //schlafen gehen
    go_to_sleep();
}

// the interrupt service routine 
void PUMPE_AN() {
  digitalWrite(PIN_PUMPE, HIGH);
  delay(PUMPENLAUFZEIT);
  digitalWrite(PIN_PUMPE, LOW);
  }

void go_to_sleep()
{
  cli();
  //Schlafen 
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // EDIT: could also use SLEEP_MODE_PWR_DOWN for lowest power consumption.
  sleep_enable();
  sei();
  sleep_mode();   // Now enter sleep mode.
  // The program will continue from here after the WDT timeout
  sleep_disable();   // First thing to do is disable sleep.
}

Kann mir jemand bei diesem Code weiterhelfen?
Möchte den Wecker auf 30 Sekunden stellen, dann die LED 10 Sekunden leuchten lassen, dann den Arduino schlafen legen und nach den verbleibenden 20 Sekunden wieder aufwecken.
Anschließend soll der Wecker auf 40 Sekunden gestellt werden, dann die LED 10 Sekunden leuchten, den Arduino schlafen legen und nach 30 Sekunden wieder aufwecken.

Habe es probiert mit dem obigen Code, aber leider ist es immer so, dass der Arduino immer wieder genau nach 1 Minute nach dem Weckerstellen wieder aufwacht :slight_smile:

Warum eine for schleife in der Loop? Bitte keine delay verwenden. Bitte den Code sauber Formatieren. Bitte die Kommentare richtig schreiben.
Erst dann kann man weitersehen. Und mal Googlen, wie eine statemaschine geht.

Was soll diese Zeile machen?

Ich will mit der Zeile die 30 oder halt 40 Sekunden auf meine aktuelle Zeit draufrechnen und das ganze wieder in normale Zeit zurückrechnen und damit den Alarm stellen