Skip to Content

Alarme mit der Echtzeituhr DS3231

Alarme mit der Echtzeituhr DS3231

In diesem Tutorial lernst du, wie man Alarme mit der Real-Time-Clock (RTC) DS3231 und einem Arduino verwendet.

Das DS3231 Modul ist eine externe Uhr. Es hat eine eigene Batterie und behält die aktuelle Zeit und das Datum auch dann bei, wenn die Hauptstromversorgung ausgeschaltet ist. Außerdem verfügt es über zwei Alarmregister, die du einstellen kannst, um Alarme zu bestimmten Zeiten und Daten auszulösen.

Eine Real-Time-Clock ist besonders nützlich, wenn du den Arduino für längere Zeit schlafen legen möchtest. Die maximale Schlafdauer für einen Arduino Uno beträgt 8 Sekunden. Mit einer RTC kannst du jedoch beliebig lange Schlafzeiten realisieren, was z.B. die Batterielaufzeit von Datenloggern erheblich verlängert.

Benötigte Teile

Für dieses Projekt benötigst du ein DS3231 RTC Modul und einen Arduino. Ich habe einen Arduino Uno verwendet, aber jeder andere Arduino funktioniert ebenfalls. In einem der Codebeispiele dieses Tutorials verwende ich außerdem einen passiven Summer, um einen Alarm auszulösen.

DS3231 RTC Modul

Arduino

Arduino Uno

USB Data Sync cable Arduino

USB-Kabel für Arduino UNO

Passiver Summer

Dupont wire set

Dupont-Kabelset

Half_breadboard56a

Breadboard

Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.

Grundlagen des DS3231 RTC Moduls

Das DS3231 RTC Modul ist eine präzise Echtzeituhr basierend auf dem DS3231 Chip. Zusätzlich zum DS3231 verfügt es über einen AT24C32 EEPROM und einen Halter für eine CR2032 Batterie.

Dank seiner Batterie behält das DS3231 die genaue Zeit auch dann bei, wenn der Arduino keinen Strom hat, neu startet oder in den Schlafmodus geht. Das Bild unten zeigt Vorder- und Rückseite eines typischen DS3231 RTC Moduls:


Front and back of DS3231 RTC Module
Vorder- und Rückseite des DS3231 RTC Moduls

Wenn du mehr Details zum DS3231 RTC Modul brauchst, schau dir unser Arduino and RTC Module DS3231 Tutorial an.

Pinbelegung des DS3231 RTC Moduls

Die Pinbelegung des DS3231 RTC Moduls ist wie folgt: VCC und GND sind für die Stromversorgung mit einer Spannung von 3,3–5,5V. SCL und SDA sind die Pins für die I2C-Schnittstelle. 32K und SQW sind Ausgänge für das 32kHz Taktsignal und ein programmierbares Rechtecksignal. Der SQW-Ausgang kann auch als Interrupt-Signal konfiguriert werden, was wir später verwenden werden.

Pinout of DS3231 RTC Module
Pinbelegung des DS3231 RTC Moduls

Während das DS3231 RTC Modul mit 5V läuft, solltest du 3,3V als Stromversorgung verwenden, wenn eine CR2032 Batterie eingesetzt ist. Das Modul hat eine sehr einfache Ladeschaltung, die die Batterie beschädigen kann, wenn das Modul mit 5V betrieben wird. Für mehr Informationen lies das Arduino and RTC Module DS3231 Tutorial und den Battery charging circuit of DS3231 module Artikel.

Anschluss des DS3231 RTC Moduls an den Arduino

Den Anschluss des DS3231 RTC Moduls an einen Arduino ist einfach. Verbinde zuerst die SCL- und SDA-Pins des DS3231 RTC Moduls mit den entsprechenden Pins am Arduino-Board, wie unten gezeigt. Verbinde dann GND mit Masse und VCC mit 3,3V des DS3231 RTC Moduls – und das war’s.

Connecting DS3231 RTC Module to Arduino
Anschluss des DS3231 RTC Moduls an Arduino

Denke daran, das DS3231 RTC Modul mit 3,3V zu betreiben, sonst könnte eine eingesetzte CR2032 Batterie beschädigt werden.

Einfacher Testcode für das DS3231 RTC Modul

Du solltest eine Softwarebibliothek verwenden, um mit dem DS3231 RTC Modul zu kommunizieren. Es gibt mehrere, hier verwende ich die Arduino-DS3231 Bibliothek von Korneliusz Jarzębski. Um sie zu installieren, gehe zu github repo und klicke auf den grünen „Code“-Button. Wähle dann „Download Zip“ aus dem Menü, wie unten gezeigt:

Download ZIP file for Arduino-DS3231library
ZIP-Datei für Arduino-DS3231 Bibliothek herunterladen

Diese Datei mit dem Namen „Arduino-DS3231-dev.zip“ wird auf deinen Computer heruntergeladen. Um diese ZIP-Bibliothek zu installieren, folge den üblichen Schritten. Klicke auf Sketch -> Add .ZIP Library und wähle dann die Datei Arduino-DS3231-dev.zip aus, die du gerade heruntergeladen hast.

Installing a .ZIP Arduino library
Installation einer .ZIP Arduino Bibliothek

Mit der installierten Bibliothek kannst du die Funktion des DS3231 RTC Moduls testen. Das folgende einfache Codebeispiel setzt die Zeit und das Datum im RTC Modul auf die Kompilierzeit des Sketches und gibt dann Zeit und Datum in einer Schleife aus:

#include "Wire.h"
#include "DS3231.h"

DS3231 rtc;

void setup() {
  Serial.begin(9600);
  rtc.begin();
  rtc.setDateTime(__DATE__, __TIME__);
}

void loop() {
  RTCDateTime  dt = rtc.getDateTime();
  Serial.println(rtc.dateFormat("d-m-Y H:i:s", dt));
  delay(1000);
}

Der Code beginnt mit dem Einbinden der Wire.h Bibliothek für die I2C-Kommunikation und der DS3231.h Bibliothek für die Kommunikation mit dem DS3231 RTC.

Als nächstes erstellen wir eine Instanz der DS3231 Klasse. In der setup() Funktion initialisieren wir die RTC und setzen das aktuelle Datum und die Zeit mit rtc.setDateTime(). Die Makros __DATE__ und __TIME__ fügen automatisch das Datum und die Zeit ein, zu der der Sketch kompiliert wurde. Beachte, dass die Zeit nicht aktuell ist, wenn der Arduino nach dem Kompilieren und Hochladen neu gestartet wird.

In der loop-Funktion holen wir das Datum und die Zeit von der RTC mit RTCDateTime dt = rtc.getDateTime(), formatieren sie als String und geben sie mit Serial.println(rtc.dateFormat("d-m-Y H:i:s", dt)) im Serial Monitor aus. Das hier angegebene Format ist day-month-year hours:minutes:seconds. Siehe das DS3231_dateformat.ino file der Arduino-DS3231 Bibliothek für weitere Formatierungsbeispiele.

Zum Schluss fügen wir eine Verzögerung von 1 Sekunde ein,, um zu verhindern, dass der Serial Monitor mit Nachrichten überflutet wird.

Ausgabe-Beispiel

Wenn du den Code hochlädst und den Serial Monitor öffnest, solltest du die Zeit und das Datum wie folgt ausgegeben sehen:

RTC date and time printed in Serial Monitor
RTC Datum und Zeit im Serial Monitor ausgegeben

Nachdem du sichergestellt hast, dass das DS3231 korrekt angeschlossen ist und funktioniert, sprechen wir über die Alarmfunktion des DS3231.

Alarmregister des DS3231

Das DS3231 RTC Modul hat zwei Alarmregister: Alarm 1 und Alarm 2. Diese beiden Alarme können Ereignisse basierend auf bestimmten Zeitbedingungen auslösen. Die folgende Tabelle zeigt dir die Bit-Einstellungen für diese Bedingungen:

Alarms register bit masks of the DS3231
Alarmregister Bitmasken des DS3231 (source)

Wie du siehst, bietet Alarm 1 mehr Konfigurationsmöglichkeiten. Du kannst ihn so einstellen, dass er basierend auf Sekunden, Minuten, Stunden und Tag/Datum auslöst. Alarm 2 erlaubt nur die Einstellung von Minuten, Stunden und Tag/Datum. Außerdem kann Alarm 1 so konfiguriert werden, dass er einmal pro Minute, täglich oder wöchentlich auslöst, während Alarm 2 nur einmal oder jede Minute auslösen kann.

Allerdings erlauben keine der beiden Alarme, einen Alarm periodisch auszulösen, wenn er nicht mit einer Basiseinheit übereinstimmt. Zum Beispiel kannst du einen Alarm jede Minute oder zur 10. Sekunde jeder Minute auslösen, aber du kannst nicht einen Alarm alle 10 Sekunden mit den Alarmregistern auslösen. Ich zeige dir aber, wie du diese Einschränkung umgehen kannst.

Codebeispiele für Alarme mit DS3231

In diesem Abschnitt besprechen wir verschiedene Codebeispiele, um Alarme auszulösen oder zu verwenden.

Ereignisse auslösen ohne Verwendung der DS3231 Alarmregister

Beginnen wir mit einem Beispiel, das tatsächlich das Alarmregister nicht verwendet. Stattdessen fragen wir kontinuierlich die Zeit und das Datum des DS3231 ab. Wenn eine bestimmte Bedingung erfüllt ist, führen wir eine Aktion aus. Das folgende Codebeispiel gibt die aktuelle Zeit alle 10 Sekunden aus:

#include "Wire.h"
#include "DS3231.h"

DS3231 rtc;

void setup() {
  Serial.begin(9600);
  rtc.begin();
  rtc.setDateTime(__DATE__, __TIME__);
}

void loop() {
  RTCDateTime dt = rtc.getDateTime();
  if (dt.second % 10 == 0) {   
    Serial.println(rtc.dateFormat("H:i:s", dt)); 
    delay(1000);  
  }
}

Die entscheidende Zeile ist die folgende, in der wir prüfen, ob die Sekunden durch 10 teilbar sind, mit dem modulo (%) Operator:

if (dt.second % 10 == 0) 

Wenn das der Fall ist, geben wir die Zeit aus. Beachte, dass du eine Verzögerung von mindestens 1 Sekunde (delay(1000)) hinzufügen musst, sonst wird die Zeit mehrfach ausgegeben, da die Schleife schneller als 1 Sekunde läuft.

Der Vorteil, Alarme auf diese Weise auszulösen, ist, dass du nicht auf die vom Alarmregister unterstützten Bedingungen beschränkt bist. Wenn du z.B. die Zeit alle 10 Sekunden, aber nur an Wochenenden (dt.dayOfWeek >= 6) ausgeben möchtest, kannst du folgende Bedingung schreiben:

  if ((dt.dayOfWeek >= 6) && (dt.second % 10 == 0)) {   
    Serial.println(rtc.dateFormat("H:i:s", dt)); 
    delay(1000);  
  }

Die RTCDateTime struct der Arduino-DS3231 Bibliothek hat folgende Zeit- und Datumsfelder, die du bei der Implementierung eigener Auslösebedingungen verwenden kannst:

struct RTCDateTime {
    uint16_t year;
    uint8_t month;
    uint8_t day;
    uint8_t hour;
    uint8_t minute;
    uint8_t second;
    uint8_t dayOfWeek;
    uint32_t unixtime;
};

Das gibt dir völlige Freiheit, aber die korrekte Umsetzung der Auslösebedingungen kann ziemlich knifflig sein und du kannst so keine Interrupts verwenden. Mehr zu Interrupts später.

Im nächsten Abschnitt zeige ich dir, wie du Alarmregister verwendest, um Ereignisse auszulösen. Die Bedingungen sind eingeschränkter, aber der Code wird kürzer und robuster.

Ereignisse auslösen mit DS3231 Alarmregistern

Das folgende Codebeispiel gibt die aktuelle RTC-Zeit jede Stunde mit Alarm 2 aus:

#include "Wire.h"
#include "DS3231.h"

DS3231 rtc;

void setup() {
  Serial.begin(9600);
  rtc.begin();
  rtc.setDateTime(__DATE__, __TIME__);
  rtc.setAlarm2(0, 0, 0, DS3231_MATCH_M);
}

void loop() {
  if (rtc.isAlarm2()) {  // also clears alarm
    RTCDateTime dt = rtc.getDateTime();
    Serial.println(rtc.dateFormat("H:i:s", dt));
  }
}

Es gibt zwei neue Codezeilen. Zuerst setzen wir die Auslösebedingung für Alarm 2 in der setup Funktion über

rtc.setAlarm2(0, 0, 0, DS3231_MATCH_M);

. Diese Bedingung löst aus, wenn die Minute 0 ist (DS3231_MATCH_M), also einmal pro Stunde.

Zweitens prüfen wir in der loop-Funktion, ob die Bedingung für Alarm 2 ausgelöst wurde, über

if (rtc.isAlarm2())

. Das ist kürzer und robuster, als die Bedingung selbst zu prüfen. Zum Beispiel müsstest du folgenden Code schreiben, um jede Minute auszulösen, wenn du Zeit und Datum selbst prüfst:

  if ((rtc.minute==0) & (rec.second==0)) {
    RTCDateTime dt = rtc.getDateTime();
    Serial.println(rtc.dateFormat("H:i:s", dt));
    delay(1000);
  }

Durch die Verwendung der isAlarm2() Funktion wird die Bedingung einfacher und wir brauchen kein delay() mehr, da isAlarm2() den Alarm nach dem Auslösen zurücksetzt. Du kannst dieses Verhalten deaktivieren, indem du stattdessen isAlarm2(false) aufrufst.

Zeit- und Datumsabgleich

Auslösebedingungen werden definiert, indem festgelegt wird, welche Zeit- und Datumsbestandteile bestimmten Werten entsprechen müssen. Wenn du z.B. einen Alarm jeden Montag (=1) um 10:45:30 auslösen willst, würdest du folgende Alarmbedingung setzen:

setAlarm1(1, 10, 45, 30, DS3231_MATCH_DY_H_M_S);

Beachte, dass nur Alarm 1 das Abgleichen von Sekunden unterstützt. Das folgende Enum zeigt die matching constant mit ihren Bitmasken, die von der Arduino-DS3231 Bibliothek für Alarm 1 unterstützt werden:

typedef enum {
    DS3231_EVERY_SECOND   = 0b00001111,
    DS3231_MATCH_S        = 0b00001110,
    DS3231_MATCH_M_S      = 0b00001100,
    DS3231_MATCH_H_M_S    = 0b00001000,
    DS3231_MATCH_DT_H_M_S = 0b00000000,
    DS3231_MATCH_DY_H_M_S = 0b00010000
} DS3231_alarm1_t;

Und hier sind die für Alarm 2:

typedef enum {
    DS3231_EVERY_MINUTE   = 0b00001110,
    DS3231_MATCH_M        = 0b00001100,
    DS3231_MATCH_H_M      = 0b00001000,
    DS3231_MATCH_DT_H_M   = 0b00000000,
    DS3231_MATCH_DY_H_M   = 0b00010000
} DS3231_alarm2_t;

Mehr Beispiele, wie man bestimmte Zeiten und Daten abgleicht, findest du in der DS3231_alarm.ino Beispieldatei der Arduino-DS3231 Bibliothek.

Egal ob du isAlarm1(), isAlarm2() oder Zeit- und Datumsfelder selbst vergleichst (z.B. dt.second==10), es erfordert immer kontinuierliches Abfragen in der loop Funktion, um zu erkennen, ob ein Alarm ausgelöst wurde.

Je nachdem, wie viel andere Arbeit du in der loop-Funktion machst und wie lange das dauert, kann das die Erkennung eines Alarms verzögern. Wenn z.B. der Code in der loop-Funktion 30 Sekunden braucht, du aber innerhalb einer Sekunde auf einen Alarm reagieren willst, ist das nicht möglich.

Interrupts sind eine Lösung für dieses Problem und werden Thema des nächsten Abschnitts sein.

Interrupt-Ausgang aktivieren

Der DS3231 unterstützt Interrupts am SQW-Pin. Standardmäßig gibt dieser Pin ein Rechtecksignal (SQW) mit konfigurierbarer Frequenz aus, kann aber so eingestellt werden, dass er ein Interrupt-Signal sendet, wenn ein Alarm ausgelöst wird.

Um das auszuprobieren, verbinde eine LED mit einem Vorwiderstand von ca. 220Ω mit dem SQW-Pin des DS3231, wie unten gezeigt:

Connecting an LED to the interrupt pin SQW of the DS3231
Anschluss einer LED an den Interrupt-Pin SQW des DS3231

Der folgende Code setzt Alarm 1 so, dass er jede 10. Sekunde (setAlarm1(0, 0, 0, 10, DS3231_MATCH_S)) auslöst:

#include "Wire.h"
#include "DS3231.h"

DS3231 rtc;

void setup() {
  Serial.begin(9600);
  rtc.begin();
  rtc.setDateTime(__DATE__, __TIME__);
  rtc.setAlarm1(0, 0, 0, 10, DS3231_MATCH_S);
  rtc.enableOutput(false);
}

void loop() {
  RTCDateTime dt = rtc.getDateTime();
  Serial.println(rtc.dateFormat("H:i:s", dt));
  delay(1000);
}

Die wichtige Zeile in diesem Code ist

rtc.enableOutput(false);

, die dem DS3231 sagt, das Rechtecksignal am SQW-Pin zu deaktivieren und stattdessen ein Interrupt-Signal zu senden. Ohne diese Zeile würde die LED mit einer konstanten Frequenz von 1 Hz blinken.

Der Code gibt jede Sekunde die Zeit aus, prüft aber nicht in der loop Funktion auf den Trigger. Wenn du den Code ausführst und den Serial Monitor sowie die LED beobachtest, wirst du sehen, dass die LED ausgeht (und aus bleibt), sobald die 10. Sekunde erreicht ist. Interrupts werden also durch ein LOW-Signal am SQW-Pin angezeigt!

Die LED bleibt aus, weil wir den Alarm nicht zurücksetzen. Wenn du möchtest, dass die LED z.B. 5 Sekunden (delay(5000)) alle 10 Sekunden ausgeht, könntest du die loop-Funktion wie folgt ändern:

void loop() {
  if (rtc.isAlarm1(false)) { 
    delay(5000);
    rtc.clearAlarm1();
  }
}

Der Code prüft den Alarm, ohne ihn zurückzusetzen. Wenn der Alarm ausgelöst wird, wartet er 1 Sekunde, während die LED aus ist, und setzt dann den Alarm zurück, was das Interrupt-Signal am SQW-Pin löscht und die LED wieder einschaltet.

Aufwachen aus dem Schlaf mit DS3231 Interrupts

Der häufigste Anwendungsfall für Interrupts ist, einen Arduino aus dem Tiefschlaf in regelmäßigen Abständen aufzuwecken, um Aktionen wie Datenlogging durchzuführen. Zum Beispiel möchtest du vielleicht alle 30 Minuten die Umgebungstemperatur messen, aber dazwischen soll der Arduino schlafen, um Batterie zu sparen.

Dazu entfernen wir die LED vom SQW-Pin und verbinden stattdessen den SQW-Pin des DS3231 mit Pin 2 des Arduino, wie unten gezeigt:

Connecting interrupt pin SQW of DS3231 to Arduino
Anschluss des Interrupt-Pins SQW des DS3231 an Arduino

Beachte, dass du einen Arduino pin der Interrupts unterstützt, verwenden musst. Beim Arduino Uno können nur die Pins 2 und 3 für Interrupts verwendet werden.

Mit der Interrupt-Verbindung können wir den Arduino nun aus dem Tiefschlaf aufwecken, wenn ein Alarm ausgelöst wird. Das folgende Codebeispiel versetzt den Arduino in den Tiefschlaf, weckt ihn jede Minute auf und gibt dann die Zeit aus:

#include "Wire.h"
#include "DS3231.h"
#include "avr/sleep.h"

const int intPin = 2;

DS3231 rtc;

void wakeUp() {
}

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

  pinMode(intPin, INPUT);
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();

  rtc.begin();
  rtc.setDateTime(__DATE__, __TIME__);
  rtc.setAlarm1(0, 0, 0, 0, DS3231_MATCH_S);
  rtc.enableOutput(false);
}

void loop() {
  attachInterrupt(digitalPinToInterrupt(intPin), wakeUp, LOW);
  Serial.println("sleeping...");
  delay(100); // finish printing
  sleep_cpu();  // <= go to sleep

  // waking up here when interrupt detected
  detachInterrupt(digitalPinToInterrupt(intPin));
  rtc.clearAlarm1();
  RTCDateTime dt = rtc.getDateTime();
  Serial.println(rtc.dateFormat("H:i:s", dt)); 
  delay(100); // finish printing   
}

Lass uns den Code in seine Bestandteile zerlegen, um ihn besser zu verstehen.

Includes

Wir beginnen mit dem Einbinden der notwendigen Bibliotheken für unser Projekt: Wire.h für I2C-Kommunikation, DS3231.h für die Ansteuerung der DS3231 RTC und avr/sleep.h für das Management der Schlafmodi.

#include "Wire.h"
#include "DS3231.h"
#include "avr/sleep.h"

Konstanten und Variablen

Als nächstes definieren wir eine Konstante für den Interrupt-Pin und erstellen eine Instanz der DS3231 Klasse, um mit der RTC zu kommunizieren.

const int intPin = 2;
DS3231 rtc;

Wake-Up Funktion

Die wakeUp() Funktion ist definiert, bleibt aber leer. Sie wird von der attachInterrupt() Funktion benötigt und darf nicht null sein. Diese Funktion wird aufgerufen, wenn der Interrupt ausgelöst wird.

void wakeUp() { }

Du könntest dort einfache Dinge machen, wie eine Variable inkrementieren, aber wir brauchen das nicht und werden es nicht verwenden. Sei vorsichtig, du solltest dort nichts Komplexes machen, das mit Interrupts zusammenhängt, einschließlich Serial.print()!

Setup-Funktion

In der setup() Funktion initialisieren wir die serielle Kommunikation, konfigurieren den Interrupt-Pin und setzen die Arduino sleep mode.

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

  pinMode(intPin, INPUT);
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();

  rtc.begin();
  rtc.setDateTime(__DATE__, __TIME__);
  rtc.setAlarm1(0, 0, 0, 0, DS3231_MATCH_S);
  rtc.enableOutput(false);
}

Wir setzen außerdem die Zeit und das Datum der RTC, konfigurieren Alarm 1 so, dass er jede Minute (Sekunde = 0) ausgelöst wird, und schalten den Ausgang (SQW) auf Interrupt-Ausgabe um.

Loop-Funktion

In der loop() Funktion hängen wir einen Interrupt an den Interrupt-Pin, sodass der Mikrocontroller aufwacht, wenn Pin 2 LOW wird. Wir geben „sleeping…“ im Serial Monitor aus und versetzen den Arduino dann in den Schlafmodus.

void loop() {
  attachInterrupt(digitalPinToInterrupt(intPin), wakeUp, LOW);
  Serial.println("sleeping...");
  delay(100); // finish printing
  sleep_cpu();

  detachInterrupt(digitalPinToInterrupt(intPin));
  rtc.clearAlarm1();
  RTCDateTime dt = rtc.getDateTime();
  Serial.println(rtc.dateFormat("H:i:s", dt)); 
  delay(100); // finish printing   
}

Wenn ein Interrupt auftritt (Pin 2 wird LOW), wachen wir auf (direkt nach der Zeile mit sleep_cpu()). Zuerst deaktivieren wir den Interrupt, um ein sofortiges erneutes Auslösen zu verhindern. Wir setzen auch den Alarm zurück, um den Interrupt-Pin SQW des DS3231 wieder auf HIGH zu bringen.

Dann geben wir die aktuelle Zeit aus und warten kurz, damit der Serial Monitor die Ausgabe abschließen kann. Ohne die Verzögerung könnte die Ausgabe abgeschnitten werden.

Ausgabe-Beispiel

Wenn du diesen Code hochlädst, solltest du folgende Ausgabe im Serial Monitor sehen:

Output on Serial Monitor
Ausgabe im Serial Monitor

Beachte, dass der Arduino in den Schlaf geht, jede Minute aufwacht und die Zeit ausgibt.

Wir mussten nicht prüfen, ob ein Alarm mit isAlarm1() ausgelöst wurde, da wir wissen, dass ein Interrupt nur feuert, wenn Alarm 1 ausgelöst wird. Wenn du aber Alarm 1 und Alarm 2 setzt, was möglich ist, kannst du erkennen, welcher Alarm ausgelöst wurde, indem du isAlarm1() und isAlarm2() prüfst.

Ich zeige dir ein Beispiel im nächsten Abschnitt, in dem wir auch einen Summer anschließen, um einen ausgelösten Alarm zu signalisieren.

Alarme unterscheiden und Summer verwenden

Wir beginnen damit, einen active buzzer an Pin 3 des Arduino anzuschließen. Jeder andere Pin, der PWM unterstützt, würde auch funktionieren.

Connecting a Buzzer to Arduino
Anschluss eines Summers an den Arduino

Beachte, dass ich für dieses Beispiel das Interrupt-Signalkabel vom SQW entfernt habe, da ich hier keine Interrupts verwenden werde. Aber du könntest.

Der folgende Code lässt den Arduino jede Minute und jede Stunde mit dem Summer einen Alarm ausgeben:

#include "Wire.h"
#include "DS3231.h"

const int buzzerPin = 3;

DS3231 rtc;

void setup() {
  rtc.begin();
  rtc.setDateTime(__DATE__, __TIME__);
  rtc.setAlarm1(0, 0, 0, 0, DS3231_MATCH_S); // every minute
  rtc.setAlarm2(0, 0, 0, DS3231_MATCH_M);  // every hour
}

void loop() {
  if (rtc.isAlarm1() && !rtc.isAlarm2()) { 
     tone(buzzerPin, 1000, 200);
  }
  if (rtc.isAlarm2()) {
     tone(buzzerPin, 2000, 200);
  }
}

In der setup Funktion definieren wir die zwei Alarme. Alarm 1 löst aus, wenn die Sekunden 0 sind, also jede Minute. Alarm 2 löst aus, wenn die Minuten 0 sind, also jede Stunde:

void setup() {
  ...
  rtc.setAlarm1(0, 0, 0, 0, DS3231_MATCH_S); // every minute
  rtc.setAlarm2(0, 0, 0, DS3231_MATCH_M);  // every hour
}

In der loop Funktion prüfen wir, welcher Alarm ausgelöst wurde und geben einen unterschiedlichen tone aus:

void loop() {
  if (rtc.isAlarm1() && !rtc.isAlarm2()) { 
     tone(buzzerPin, 1000, 200);
  }
  if (rtc.isAlarm2()) {
     tone(buzzerPin, 2000, 200);
  }
}

Da zu Beginn jeder Stunde Minuten und Sekunden 0 sind, werden beide Alarme ausgelöst. Wir wollen aber nur den Ton von Alarm 2 zur vollen Stunde hören. Deshalb geben wir den Ton von Alarm 1 nur aus, wenn Alarm 2 nicht ausgelöst wurde (rtc.isAlarm1() && !rtc.isAlarm2()).

Du könntest das auch eleganter so umsetzen:

  if (rtc.isAlarm1()) { 
     tone(buzzerPin, rtc.isAlarm2() ? 2000 : 1000, 200);
  }

Und das war’s. Ich hoffe, du weißt jetzt viel mehr über die Alarmfunktion des DS3231.

Fazit

In diesem Tutorial hast du gelernt, wie man Alarme mit der Real-Time-Clock (RTC) DS3231 und einem Arduino verwendet.

Wir haben nicht besprochen, wie man die Zeit und das Datum der RTC einstellt. Wir haben einfach die Kompilierzeit verwendet, um die RTC zu initialisieren, was in Ordnung ist, solange du den Arduino nicht neu startest. Wenn du die Zeit nach einem Reset oder während der Laufzeit einstellen oder anpassen möchtest, schau dir das Arduino and RTC Module DS3231 Tutorial an, in dem wir Taster dafür verwenden. Dort lernst du auch, wie man die Zeit auf einem LCD anzeigt.

Wenn du mehr über die verschiedenen Schlafmodi erfahren möchtest, schau dir das How Do I Wake Up My Arduino From Sleep Mode? Tutorial an. Für batteriebetriebene Projekte bist du mit einem ESP32 vielleicht besser bedient.

Außerdem unterstützt ein ESP32 Wi-Fi, sodass die Synchronisation der RTC-Uhr mit einem internet time server einfach ist und die Notwendigkeit von Tastern zur Zeiteinstellung entfällt. Es löst auch das Problem mit der Sommerzeit. Schau dir das Real-Time-Clock DS3231 with ESP32 Tutorial an.

Wenn du Fragen hast, hinterlasse sie gerne im Kommentarbereich.

Viel Spaß beim Basteln ; )