Skip to Content

Echtzeituhr DS3231 mit ESP32

Echtzeituhr DS3231 mit ESP32

In diesem Tutorial lernst du, wie du ein DS3231 Real Time Clock (RTC) Modul mit einem ESP32 verwendest.

Ein RTC-Modul ist eine externe Uhr, die die aktuelle Zeit und das Datum verfolgt. Es arbeitet unabhängig von der Hauptstromversorgung, sodass es die genaue Zeit auch bei ausgeschaltetem Strom beibehält.

Ich zeige dir, wie du ein RTC in Kombination mit dem ESP32 im Deep-Sleep verwendest, wie du die RTC für die Sommerzeit anpasst und wie du die RTC mit einem Internet-Zeitserver synchronisierst.

Benötigte Teile

Für dieses Projekt benötigst du ein DS3231 RTC Modul und einen ESP32. Ich verwende den ESP32 lite als Mikroprozessor, da er eine Batterieschnittstelle zum Laden hat, mit der du den ESP32 und die RTC mit einer LiPo-Batterie betreiben kannst. Allerdings funktionieren auch andere ESP32 oder ESP8266. Zur Anzeige der Zeit habe ich ein OLED gewählt, du könntest aber auch ein LCD Display.

ESP32 lite Lolin32

ESP32 lite

USB data cable

USB-Datenkabel

DS3231 RTC Modul

Dupont wire set

Dupont-Kabelset

Half_breadboard56a

Breadboard

OLED display

OLED-Display

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 Chips

Der DS3231 ist ein kleiner Chip (38 x 22 x 14 mm) mit einer hochpräzisen Echtzeituhr (RTC) und einem integrierten temperaturkompensierten Quarzoszillator und Quarz. Dieser Chip enthält einen Batteriefachhalter, sodass er auch bei abgeschalteter Hauptstromversorgung weiterläuft.

DS3231 Chip
DS3231 Chip

Die RTC verfolgt Sekunden, Minuten, Stunden, Wochentag, Datum, Monat und Jahr. Für Monate mit weniger als 31 Tagen wird das Datum automatisch angepasst, inklusive Schaltjahreskorrekturen. Die Uhr arbeitet im 24-Stunden- oder 12-Stunden-Format. Sie bietet zwei programmierbare Kalenderalarme und einen programmierbaren Rechteckwellenausgang. Die Kommunikation erfolgt über einen I2C-Bus.

Eine präzise, temperaturkompensierte Spannungsreferenz und ein Komparator überwachen den VCC-Status, erkennen Stromausfälle, liefern einen Reset-Ausgang und schalten bei Bedarf automatisch auf die Backup-Stromversorgung um. Das Gerät integriert einen digitalen Temperatursensor, der über die I2C-Schnittstelle zugänglich ist.

Im Folgenden sind die Hauptmerkmale des DS3231 aufgeführt (source):

Technische Spezifikationen

  • Betriebsspannung: 3,3–5,5 V
  • Uhrchip: Hochpräziser Uhrchip DS3231
  • Uhrgenauigkeit: 2 ppm im Bereich von 0-40 °C, jährlicher Fehler ca. 1 Minute
  • Zwei Kalenderalarme
  • Programmierbarer Rechteckwellenausgang
  • Echtzeituhr generiert Sekunden, Minuten, Stunden, Wochentag, Datum, Monat und Jahr mit Schaltjahrkompensation bis 2100
  • Integrierter Temperatursensor mit ±3 °C Genauigkeit
  • Speicherchip: AT24C32 (32K Speicherkapazität)
  • IIC-Busschnittstelle, maximale Übertragungsgeschwindigkeit 400 kHz (bei 5 V Betriebsspannung)
  • Kann mit anderen IIC-Geräten kaskadiert werden, 24C32-Adresse kann durch Kurzschließen von A0/A1/A2 geändert werden, Standardadresse ist 0x57
  • Verwendet CR2032-Batterie, um den Betrieb der Uhr bei Stromausfall sicherzustellen

Blockdiagramm des DS3231

Das folgende Blockdiagramm zeigt die internen Komponenten des DS3231. Du siehst den Quarzoszillator, den Temperatursensor, die Stromversorgungskontrolle und die I2C-Schnittstelle.

Block Diagram of DS3231 Chip
Blockdiagramm des DS3231 Chips

Die Pins des DS3231 sind wie folgt: VCC und GND für die allgemeine Stromversorgung und VBAT für die Batteriepufferung. SCL und SDA sind für die I2C-Schnittstelle.

Der Pin mit der Bezeichnung „33kHz“ gibt das 32kHz Taktsignal aus. INT/SQW ist ein Interrupt-Pin, der zur Signalisierung von Alarmen oder als programmierbarer Rechteckwellenausgang verwendet werden kann.

RST ist der Reset-Pin, der dem angeschlossenen Mikroprozessor signalisiert, dass die Stromversorgung verloren ging oder wiederhergestellt wurde. Die Pins „33kHz“, INT/SQW und RST haben weitere Funktionen. Für Details siehe das unten verlinkte Datenblatt:

Grundlagen des DS3231 RTC Moduls

Der DS3231 Chip selbst ist zu klein, um direkt an einen ESP32 angeschlossen zu werden, und es fehlen einige erforderliche Bauteile. Deshalb verwendet man typischerweise ein DS3231 RTC Modul. Das Modul enthält die fehlenden Komponenten und hat auch einen Halter für eine CR2032 Batterie, die die Backup-Stromversorgung liefert. 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

Anstelle einer CR2032 Batterie kannst du auch eine wiederaufladbare LIR2032 Batterie verwenden, musst aber auf die Versorgungsspannung achten. Mehr dazu später.

Pinbelegung des DS3231 RTC Moduls

Die Pinbelegung eines DS3231 RTC Moduls entspricht im Wesentlichen der des DS3231 Chips.

Pinout of DS3231 RTC Module
Pinbelegung des DS3231 RTC Moduls

VCC und GND sind für die Stromversorgung mit 3,3–5,5 V. SCL und SDA sind die Pins für die I2C-Schnittstelle (mit integrierten 4,7k Pullup-Widerständen). Die Pins 32K und SQW sind Ausgänge für das 32kHz Taktsignal und das programmierbare Rechteckwellensignal, die hier nicht benötigt werden.

Die I2C-Adresse des DS3231 RTC Moduls ist über drei Lötpads (A0, A1, A2) konfigurierbar, die überbrückt werden können. Siehe den hervorgehobenen Bereich unten rechts auf dem Modul im Bild oben. Die Standard-I2C-Adresse ist 0x57, wenn keine Brücken gesetzt sind. Das Bild unten zeigt alle möglichen Konfigurationen und die entsprechenden I2C-Adressen.

Setting I2C address of the DS3231 RTC Module
Konfiguration der I2C-Adresse des DS3231 RTC Moduls (source)

Batterieladung mit DS3231 RTC Modul

Das DS3231 RTC Modul ermöglicht das Laden der Backup-Batterie über die Hauptstromversorgung (VCC). Das Bild unten zeigt den Schaltplan des DS3231 RTC Moduls. Im gelb markierten Bereich siehst du einen 200Ω Widerstand und eine 1N4148 Diode, die als sehr einfache Ladeschaltung fungieren.

Schematic  of DS3231 RTC Module
Schaltplan des DS3231 RTC Moduls

Du musst diese Ladeschaltung jedoch deaktivieren, wenn eine nicht wiederaufladbare CR2032-Batterie mit einer Versorgungsspannung von 5 V verwendet wird. Und wenn du eine wiederaufladbare LIR2032-Batterie nutzt, sollte VCC für sicheres Laden niemals höher als 4,7 Volt sein.

Der Battery charging circuit of DS3231 module Artikel beschreibt die Probleme der Ladeschaltung ausführlicher und zeigt auch, wie man sie deaktiviert. Die folgende Tabelle aus dem Artikel listet die verschiedenen Szenarien mit unterschiedlichen Versorgungsspannungen und Batterietypen sowie die erforderlichen Maßnahmen auf:

Batterietyp3,3 V Versorgung5 V Versorgung
CR2032Batterie nicht betroffenLadeschaltung deaktivieren
LIR2032Batterie nicht betroffen
Laden funktioniert nicht
Ladeschaltung deaktivieren oder
Sicherstellen, dass 5 V tatsächlich 4,7 V sind

Anschluss des DS3231 RTC Moduls an ESP32 lite

Das Anschließen des DS3231 RTC Moduls an einen ESP32 ist einfach. Verbinde zuerst SCL des DS3231 mit Pin 23 des ESP32. Dann verbinde SDA mit Pin 19 des ESP32. Schließlich verbinde GND mit Masse und 3,3 V mit VCC, wie unten gezeigt:

Connecting DS3231 RTC Module to ESP32 lite
Anschluss DS3231 RTC Modul an ESP32 lite

Da wir das DS3231 RTC Modul mit 3,3 V betreiben, kann eine CR2032-Batterie eingesetzt sein, während es an die Stromversorgung angeschlossen ist.

Einfacher Testcode für DS3231 RTC Modul

Du benötigst eine Softwarebibliothek, um mit dem DS3231 RTC Modul zu kommunizieren. Es gibt mehrere, aber ich mag die Arduino-DS3231 Bibliothek von Korneliusz Jarzębski am liebsten. Um sie zu installieren, gehe zum 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

Dies lädt eine Datei namens „Arduino-DS3231-dev.zip“ auf deinen Computer. 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 können wir die Funktion des DS3231 RTC Moduls testen. Der folgende einfache Code 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.printf("%4d-%02d-%02d %02d:%02d:%02d\n",
                dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second);
  delay(1000);
}

Der Code beginnt mit dem Einbinden der Wire.h Bibliothek für 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 Uhrzeit mit rtc.setDateTime(). Die Makros __DATE__ und __TIME__ fügen automatisch das Datum und die Uhrzeit ein, zu der der Sketch kompiliert wurde. Beachte, dass die Zeit nicht aktuell ist, wenn der ESP32 nach dem Kompilieren und Hochladen neu gestartet wird, aber das behandeln wir später.

In der loop-Funktion holen wir die aktuelle Zeit und das Datum von der RTC mit RTCDateTime dt = rtc.getDateTime() und verwenden printf, um Datum und Zeit im Serial Monitor auszugeben.

Beispielausgabe

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

RTC Datum und Zeit im Serial Monitor ausgegeben

Sommerzeit mit DS3231 RTC Modul

Während der DS3231 Datum und Zeit genau verfolgt, passt er die Sommerzeit (DST) nicht automatisch an. Das bedeutet, dass der DS3231 falsche Zeit (und Datum) anzeigt, wenn dein Land auf Sommerzeit oder zurück auf Normalzeit (ST) umstellt.

Es gibt im Wesentlichen zwei Möglichkeiten, damit umzugehen. 1) Du könntest einen Knopf hinzufügen, der manuell zwischen DST und ST wechselt. Das ist natürlich nicht optimal. 2) Du fügst Code hinzu, der den Wechsel automatisch für deine Zeitzone durchführt.

In diesem Abschnitt zeige ich dir, wie du die Sommerzeit automatisch handhabst, während du das DS3231 RTC Modul nutzt. Wir verwenden dafür die ezTime Library Bibliothek. Du kannst sie wie gewohnt über den Library Manager installieren:

Install ezTime Library via Library Manager
ezTime Bibliothek über Library Manager installieren

Nach der Installation kannst du sie verwenden, um die DS3231 RTC auf Sommerzeit mit folgendem Code einzustellen:

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

const char* TIMEZONE = "AEST-10AEDT,M10.1.0,M4.1.0/3";  // Melbourne

Timezone loc;
DS3231 rtc;

void setup() {
  Serial.begin(9600);
  rtc.begin();
  rtc.setDateTime(2024,12,4,3,16,30);  // UTC
  loc.setPosix(TIMEZONE);
}

void loop() {
  RTCDateTime dt = rtc.getDateTime();
  UTC.setTime(dt.hour, dt.minute, dt.second, dt.day, dt.month, dt.year);

  Serial.printf("RTC: %4d-%02d-%02d %02d:%02d:%02d\n",
                dt.year, dt.month, dt.day, 
                dt.hour, dt.minute, dt.second);

  Serial.printf("LOC: %4d-%02d-%02d %02d:%02d:%02d\n",
                loc.year(), loc.month(), loc.day(), 
                loc.hour(), loc.minute(), loc.second());                
               
  Serial.println();

  delay(5000);
}

Im obigen Code verbinden wir einen ESP32 mit einem DS3231 Real-Time Clock (RTC) Modul, um die aktuelle Zeit zu verfolgen, inklusive Anpassungen für die Sommerzeit. Das Programm initialisiert die RTC, setzt ein bestimmtes Datum und eine Uhrzeit und ruft dann alle 5 Sekunden die aktuelle UTC- und Ortszeit ab und zeigt sie an.

Lass uns den Code in seine Komponenten aufschlüsseln, um ihn besser zu verstehen.

Eingebundene Bibliotheken

Wir beginnen mit dem Einbinden der notwendigen Bibliotheken für unser Projekt. Die Wire.h Bibliothek wird für die I2C-Kommunikation verwendet, über die der ESP32 mit dem DS3231 RTC kommuniziert. Die DS3231.h Bibliothek bietet Funktionen speziell für die Interaktion mit dem DS3231 Modul, und ezTime.h wird für die Handhabung von Zeitzonen und Sommerzeitanpassungen genutzt.

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

Konstanten und Variablen

Als nächstes definieren wir eine Konstante für die Zeitzone. In diesem Fall verwenden wir AEST-10AEDT,M10.1.0,M4.1.0/3, was Melbourne, Australien entspricht. Dieser String gibt die Standardzeitverschiebung und die Regeln für die Sommerzeit an.

const char* TIMEZONE = "AEST-10AEDT,M10.1.0,M4.1.0/3";  // Melbourne

Die Teile dieser Zeitzonendefinition sind wie folgt

  • AEST: Australian Eastern Standard Time
  • -10: UTC-Versatz von 10 Stunden vor der koordinierten Weltzeit (UTC)
  • AEDT: Australian Eastern Daylight Time
  • M10.1.0: Übergang zur Sommerzeit am ersten Sonntag im Oktober
  • M4.1.0/3: Rückkehr zur Normalzeit am ersten Sonntag im April, mit 3 Stunden Unterschied zur UTC.

Für andere Zeitzonendefinitionen schau dir die Posix Timezones Database an. Kopiere einfach den dort gefundenen String und ändere die TIMEZONE Konstante entsprechend.

Wir erstellen auch Instanzen der Timezone und DS3231 Klassen. Die loc Variable hält unsere Ortszeitinformationen, während rtc die RTC verwaltet.

Timezone loc;
DS3231 rtc;

Setup-Funktion

In der setup() Funktion initialisieren wir die serielle Kommunikation mit 9600 Baud für Debugging. Dann initialisieren wir die RTC und setzen sie auf ein bestimmtes Datum und eine Uhrzeit (4. Dezember 2024, 03:16:30 UTC). Schließlich setzen wir die Zeitzone mit der zuvor definierten Konstante.

void setup() {
  Serial.begin(9600);
  rtc.begin();
  rtc.setDateTime(2024,12,4,3,16,30);  // UTC
  loc.setPosix(TIMEZONE);
}

Loop-Funktion

Die loop() Funktion läuft kontinuierlich. Zuerst holen wir die aktuelle Zeit und das Datum von der RTC mit rtc.getDateTime(). Dann setzen wir die UTC-Zeit mit den abgerufenen Werten.

  RTCDateTime dt = rtc.getDateTime();
  UTC.setTime(dt.hour, dt.minute, dt.second, dt.day, dt.month, dt.year);

Als nächstes geben wir die aktuelle UTC-Zeit formatiert im Serial Monitor aus. Das umfasst Jahr, Monat, Tag, Stunde, Minute und Sekunde.

  Serial.printf("RTC: %4d-%02d-%02d %02d:%02d:%02d\n",
                dt.year, dt.month, dt.day, 
                dt.hour, dt.minute, dt.second);

Wir geben auch die Ortszeit mit der loc Variable aus, die für Zeitzone und Sommerzeit angepasst wurde.

  Serial.printf("LOC: %4d-%02d-%02d %02d:%02d:%02d\n",
                loc.year(), loc.month(), loc.day(), 
                loc.hour(), loc.minute(), loc.second());                

Zum Schluss fügen wir eine neue Zeile für bessere Lesbarkeit im seriellen Output hinzu und machen eine Pause von 5000 Millisekunden (5 Sekunden), bevor die Schleife wiederholt wird.

  Serial.println();
  delay(5000);

Beispielausgabe

Wenn du den Code hochlädst und den Serial Monitor öffnest, solltest du die UTC-Zeit der RTC und die Ortszeit (LOC) sehen:

UTC and local time of RTC on Serial Monitor
UTC- und Ortszeit der RTC im Serial Monitor

Synchronisierung der DS3231 RTC mit SNTP-Server

Während der obige Code die Anpassung für Sommerzeit automatisch vornimmt, musst du die Zeit der RTC beim Start des ESP32 manuell einstellen. Noch schlimmer: Du musst den ESP32 an einen Computer anschließen, den Code ändern und neu programmieren. Das ist nervig!

Du könntest Tasten hinzufügen, um Zeit und Datum während des Betriebs des ESP32 zu ändern. Wenn du das machen willst, schau dir das Arduino and RTC Module DS3231 Tutorial an, wo wir zwei Tasten zum Einstellen der Zeit verwenden.

Die bessere Option ist jedoch, die RTC automatisch mit einem Internet-Zeitserver (SNTP) zu synchronisieren. Für mehr Hintergrundinfos siehe das How to synchronize ESP32 clock with SNTP server Tutorial.

Wenn du durchgehend Internetzugang hast und dir der Stromverbrauch egal ist, brauchst du keine RTC, da du die interne Uhr des ESP32 regelmäßig synchronisieren kannst. Das stellt die Uhr automatisch ein und berücksichtigt auch die Sommerzeit. Siehe die Automatic Daylight Savings Time Clock und Digital Clock on e-Paper Display Tutorials.

Für ein batteriebetriebenes Projekt willst du jedoch vermeiden, WiFi zu oft zu nutzen, da es viel Strom verbraucht. Ein typischer Anwendungsfall ist ein Datenlogger, z.B. für Temperatur, der möglichst lange mit Batterie laufen soll, aber genaue Zeitstempel braucht.

Der folgende Code zeigt dir, wie das geht. Er nutzt WiFi nur, wenn der ESP32 neu startet, um die RTC zu synchronisieren. Ansonsten ist der ESP32 im Deep-Sleep-Modus, um Strom zu sparen, und wacht nur gelegentlich auf, um die Zeit von der RTC zu holen und auszugeben:

#include "WiFi.h"
#include "esp_sntp.h"
#include "Wire.h"
#include "DS3231.h"
#include "ezTime.h"

const char* TIMEZONE = "AEST-10AEDT,M10.1.0,M4.1.0/3";  // Melbourne
const char* SSID = "SSID";
const char* PWD = "PASSWORD";
const int SLEEP = 10; // sec
 
DS3231 rtc; 
Timezone loc;

void syncTime() {
  WiFi.begin(SSID, PWD);
  while (WiFi.status() != WL_CONNECTED)
    ;
  configTzTime("UTC", "pool.ntp.org");
  setRtcTime();
}

void setRtcTime() {
  struct tm t;
  getLocalTime(&t);
  rtc.setDateTime(t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
}

void printTime() {
  RTCDateTime dt = rtc.getDateTime();
  UTC.setTime(dt.hour, dt.minute, dt.second, dt.day, dt.month, dt.year);

  Serial.printf("RTC: %4d-%02d-%02d %02d:%02d:%02d\n",
                dt.year, dt.month, dt.day, 
                dt.hour, dt.minute, dt.second);

  Serial.printf("LOC: %4d-%02d-%02d %02d:%02d:%02d\n",
                loc.year(), loc.month(), loc.day(), 
                loc.hour(), loc.minute(), loc.second());                
               
  Serial.println();

}

bool isReset() {
  return esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_TIMER;
}

void setup() {
  Serial.begin(9600);
  rtc.begin();
  loc.setPosix(TIMEZONE);

  if (isReset()) {
    syncTime();
  }   
  printTime();

  esp_sleep_enable_timer_wakeup(SLEEP * 1000000);                
  esp_deep_sleep_start();
}

void loop() {
}

Lass uns den Code in seine Komponenten aufschlüsseln, um ihn besser zu verstehen.

Bibliotheken und Konstanten

Wir binden die notwendigen Bibliotheken für WiFi, SNTP (Simple Network Time Protocol), I2C-Kommunikation und das DS3231 RTC ein. Außerdem definieren wir Konstanten für die Zeitzone, WiFi-Zugangsdaten und Schlafdauer.

#include "WiFi.h"
#include "esp_sntp.h"
#include "Wire.h"
#include "DS3231.h"
#include "ezTime.h"

const char* TIMEZONE = "AEST-10AEDT,M10.1.0,M4.1.0/3";  // Melbourne
const char* SSID = "SSID";
const char* PWD = "PASSWORD";
const int SLEEP = 10; // sec

Natürlich musst du die WiFi-Zugangsdaten durch deine eigenen ersetzen.

RTC- und Zeitzonenobjekte

Wir erstellen Instanzen der DS3231 Klasse für die RTC und der Timezone Klasse zur Handhabung der Ortszeitberechnung.

DS3231 rtc; 
Timezone loc;

Sync-Time-Funktion

Die syncTime() Funktion verbindet sich mit dem WiFi-Netzwerk und konfiguriert die Zeitzone für den NTP-Server. Dann ruft sie setRtcTime() auf, um die RTC mit der aktuellen Zeit zu aktualisieren.

void syncTime() {
  WiFi.begin(SSID, PWD);
  while (WiFi.status() != WL_CONNECTED)
    ;
  configTzTime("UTC", "pool.ntp.org");
  setRtcTime();
}

Set-RTC-Time-Funktion

In der setRtcTime() Funktion holen wir die Ortszeit und setzen die Zeit und das Datum der RTC entsprechend. Das Jahr wird um 1900 erhöht, da das tm_year Feld die Anzahl der Jahre seit 1900 zurückgibt.

void setRtcTime() {
  struct tm t;
  getLocalTime(&t);
  rtc.setDateTime(t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
}

Beachte, dass wir auch 1 zu tm_mon addieren müssen, um den korrekten Monat zu erhalten, da die struct tm Datenstruktur inkonsistente Bereiche verwendet. Zum Beispiel beginnt der Tag des Monats bei 1, aber der Monat des Jahres bei 0:

  Member    Type  Meaning                   Range
  tm_sec    int   seconds after the minute  0-61*
  tm_min    int   minutes after the hour    0-59
  tm_hour   int   hours since midnight      0-23
  tm_mday   int   day of the month          1-31
  tm_mon    int   months since January      0-11
  tm_year   int   years since 1900
  tm_wday   int   days since Sunday         0-6
  tm_yday   int   days since January 1      0-365
  tm_isdst  int   Daylight Saving Time flag

Print-Time-Funktion

Die printTime() Funktion holt die aktuelle Zeit und das Datum von der RTC und gibt sie im Serial Monitor aus. Sie gibt auch die Ortszeit mit dem Zeitzonenobjekt aus.

void printTime() {
  RTCDateTime dt = rtc.getDateTime();
  UTC.setTime(dt.hour, dt.minute, dt.second, dt.day, dt.month, dt.year);

  Serial.printf("RTC: %4d-%02d-%02d %02d:%02d:%02d\n",
                dt.year, dt.month, dt.day, 
                dt.hour, dt.minute, dt.second);

  Serial.printf("LOC: %4d-%02d-%02d %02d:%02d:%02d\n",
                loc.year(), loc.month(), loc.day(), 
                loc.hour(), loc.minute(), loc.second());                

  Serial.println();
}

Check-Reset-Funktion

Die isReset() Funktion prüft, ob der ESP32 aus dem Deep-Sleep durch einen Timer oder einen anderen Grund aufgewacht ist. Das hilft zu entscheiden, ob die Zeit synchronisiert werden soll.

bool isReset() {
  return esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_TIMER;
}

Setup-Funktion

In der setup() Funktion initialisieren wir die serielle Kommunikation, die RTC und setzen die Zeitzone. Wenn der ESP32 frisch startet (nicht aus dem Deep-Sleep aufwacht), synchronisieren wir die Zeit. Zum Schluss geben wir die Zeit aus und versetzen den ESP32 für die angegebene Dauer in den Deep-Sleep-Modus.

void setup() {
  Serial.begin(9600);
  rtc.begin();
  loc.setPosix(TIMEZONE);

  if (isReset()) {
    syncTime();
  }   
  printTime();

  esp_sleep_enable_timer_wakeup(SLEEP * 1000000);                
  esp_deep_sleep_start();
}

Loop-Funktion

Die loop() Funktion ist leer, da der ESP32 im Deep-Sleep keinen Code ausführt. Er wacht nur auf, um die setup() Funktion nach der Schlafdauer erneut auszuführen.

void loop() { }

Anzeige der RTC-Zeit auf OLED

Als letztes Beispiel zeige ich dir, wie du ein OLED anschließt, um die Zeit und das Datum der RTC auf einem Bildschirm anzuzeigen. Das ist nur eine einfache Erweiterung des obigen Codes.

Das Anschließen des OLED ist einfach, da es ebenfalls ein I2C-Gerät ist. Verbinde einfach SDA, SCL, VCC und GND des OLED parallel zum DS3231 wie unten gezeigt

Connecting OLED and DS3231 to ESP32 lite
Anschluss von OLED und DS3231 an ESP32 lite

Unten ist der Code, der Zeit und Datum auf dem OLED anzeigt. Beachte, dass er die Adafruit_SSD1306 Bibliothek verwendet, die du install it via the Library Manager wie gewohnt installieren kannst.

#include "WiFi.h"
#include "esp_sntp.h"
#include "Wire.h"
#include "DS3231.h"
#include "ezTime.h"
#include "Adafruit_SSD1306.h"

const char* TIMEZONE = "AEST-10AEDT,M10.1.0,M4.1.0/3";
const char* SSID = "SSID";
const char* PWD = "PASSWORD";
const int SLEEP = 10; // sec
 
DS3231 rtc; 
Timezone loc;
Adafruit_SSD1306 oled(128, 64, &Wire, -1);

void oled_init() {
  oled.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  oled.setTextSize(2);
  oled.setTextColor(WHITE);
}

void syncTime() {
  WiFi.begin(SSID, PWD);
  while (WiFi.status() != WL_CONNECTED)
    ;
  configTime(0, 0, "pool.ntp.org");
  setRtcTime();
}

void setRtcTime() {
  struct tm t;
  getLocalTime(&t);
  rtc.setDateTime(t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
}

void displayTime() {
  RTCDateTime dt = rtc.getDateTime();
  UTC.setTime(dt.hour, dt.minute, dt.second, dt.day, dt.month, dt.year);
  oled.clearDisplay();
  oled.setCursor(10, 15);
  oled.println(loc.dateTime("H:i:s"));  
  oled.setCursor(2, 45);
  oled.println(loc.dateTime("d/m/Y"));  
  oled.display();
}

bool isReset() {
  return esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_TIMER;
}

void setup() {
  rtc.begin();
  loc.setPosix(TIMEZONE);
  oled_init();

  if (isReset()) {
    syncTime();
  }   
  displayTime();

  esp_sleep_enable_timer_wakeup(SLEEP * 1000000);                
  esp_deep_sleep_start();
}

void loop() {
}

Die einzigen Ergänzungen und Änderungen am Code sind die Funktion oled_init(), die das OLED initialisiert, und die Funktion displayTime(), die Zeit und Datum auf dem OLED anzeigt. Wenn du den Code auf deinen ESP32 hochlädst, solltest du Folgendes auf dem OLED sehen:

Time and date displayed on OLED
Zeit und Datum auf OLED angezeigt

Und das war’s! Du solltest jetzt in der Lage sein, das DS3231 RTC zusammen mit einem ESP32 zu verwenden.

Fazit

In diesem Tutorial hast du gelernt, wie du ein DS3231 Real Time Clock (RTC) Modul mit einem ESP32 verwendest.

Der ESP32 lite mit einer Echtzeituhr eignet sich besonders für batteriebetriebene Projekte, die genaue Zeit benötigen und wenig Strom verbrauchen. Häufige Anwendungsfälle sind data loggers oder elektronische Uhren. Für Letztere empfehle ich e-Paper-Displays, da sie fast keinen Strom verbrauchen. Siehe unsere Tutorials, Analog Clock on e-Paper Display und Digital Clock on e-Paper Display.

Wenn du mehr über Uhren mit verschiedenen Displaytypen und Zeitsynchronisation lernen möchtest, schau dir das Digital Clock on e-Paper Display an,

Wenn du Fragen hast, kannst du sie gerne im Kommentarbereich stellen.

Viel Spaß beim Tüfteln ; )