DS3231 – Echtzeituhr

Über den Beitrag

In diesem Beitrag möchte ich das Echtzeituhrmodul (englisch: RTC = Real Time Clock) DS3231 vorstellen. Dabei verwende ich die Bibliothek RTCLib von Adafruit, da diese aus meiner Sicht den besten Kompromiss aus Bedienbarkeit und Vollständigkeit darstellt.

Anhand von Beispielsketchen werde ich euch schrittweise in die Funktionen des DS3231 und der Bibliothek einführen. Da ich die originalen Beispielsketche etwas verwirrend fand, habe ich sie teilweise leicht verändert und noch eigene hinzugefügt.

Mithilfe der RTCLib könnt ihr auch die alternativen Echtzeituhren DS1307, PCF8523 und PCF8563 steuern. Darauf gehe ich am Ende des Beitrages kurz ein. Einen separaten Beitrag über den DS1302, den DS1307 und die Bibliothek „Rtc by Makuna“ findet ihr hier.

Wie gewohnt, werde ich erst einmal ein wenig weiter ausholen und zunächst die Eigenschaften des DS3231 Moduls vorstellen.

Was ist eine Echtzeituhr?

Anders gefragt: gibt es denn auch eine „Unechtzeituhr“? Nun, auch die Arduino Boards messen die Zeit. Über die millis() Funktion könnt ihr die Zeit in Millisekunden erfragen, die seit dem Programmstart vergangen ist. Der Arduino kennt aber keine Uhrzeit oder Datum. Echtzeituhren hingegen haben Register, in denen Datum und Uhrzeit gespeichert werden. Darüber hinaus sind sie batteriebetrieben und „vergessen“ diese Daten deswegen nicht. Diese Eigenschaften machen sie zu Echtzeituhren.

Das DS3231 Modul

DS3231 Modul, Vorder- und Rückseite
DS3231 Modul, Vorder- und Rückseite

Der DS3231 im engeren Sinne ist der große, auf dem Board gut erkennbare IC mit den 16 Pins. Ein Datenblatt zu ihm gibt es hier. Ich beziehe mich in meinem Beitrag aber grundsätzlich auf das Board, auch wenn ich in der Kurzform vom DS3231 spreche.

Technische Eigenschaften des DS3231

Die aus meiner Sicht wichtigsten Eigenschaften sind:

  • Zählt Sekunden, Minuten, Stunden, Tage, Wochentage, Monate, Jahre.
    • Einschließlich Schaltjahr Funktion.
  • Maximale Abweichung (bei 0 bis 40 °C): +/-2 ppm (entspricht +/-63 Sekunden / Jahr).
  • Spannungsversorgung: 2.3 – 5.5 Volt.
  • Batterie / Akku: CR2032 / LIR2032, integrierte Ladefunktion
    • Achtung: Hinweise unten beachten!
  • Stromverbrauch im VCC Betrieb (eigene Messung):
    • bei 5 Volt: 3.6 mA (mit LED) / 0.64 mA (LED entfernt).
    • bei 3.3 Volt: 1.8 mA (mit LED) / 0.36 mA (LED entfernt).
  • Stromverbrauch im Batteriebetrieb: 0.84 – 3.0 µA (Timekeeping Modus, also reiner Zeiterhalt).
  • Zwei programmierbare Alarme mit Interrupt Funktion.
  • Programmierbarer Ausgang für Rechtecksignale (darauf gehe ich nicht im Detail ein).
  • Integrierter Temperatursensor, allerdings mit bescheidener Genauigkeit (+/-3 °C).
  • Kommunikation: I2C, Adresse: 0x68, integrierte Pullup-Widerstände
  • Ein-/Ausgänge:
    • VCC / GND: Versorgungsspannung
    • SDA / SCL: I2C
    • SQW: Ausgang für Rechtecksignale (Squarewave) oder low-aktiver Interrupt
    • 32K: Ausgang für Rechtecksignal mit 32 kHz (fest)

Achtung bei Batterie-/Akkubetrieb

Ladefunktion des DS3231
Ladefunktion des DS3231

Der DS3231 und andere RTC Module haben eine Ladefunktion für LIR2032, also wiederaufladbare Akkus. Ihr erkennt das an dem Vorhandensein einer Diode (siehe Bild rechts). Viele Shops und liefern das Modul mit einer CR2032, also einer nicht aufladbaren Knopfzelle aus. Das kann gefährlich werden, da das Modul trotzdem versucht, die Knopfzelle zu laden. Sie kann sich dann durch Gasentwicklung aufblähen, zerstört werden und dabei sehr ungesunde Flusssäure abgeben.

Leider ist die Ladeschaltung auch nur bedingt für LIR2032 Akkus geeignet. Typischerweise wird eine LIR mit 4.2 V geladen. Bei 5 Volt Versorgungsspannung habe ich 4.75 Volt an den Kontakten des Batteriehalters gemessen.  

Meine Empfehlung: Entfernt die Diode und / oder den 200 Ohm Widerstand neben der Diode, wenn ihr eine Batterie oder einen Akku parallel zu einer externen Stromquelle verwendet. Dann seid ihr auf der sicheren Seite.

Batterie vs. Akku
Batterie vs. Akku

Im Zweifelsfall könnt ihr den DS3231 zur Prüfung ohne Batterie oder Akku an VCC anschließen und die Spannung an den Kontakten des Batteriehalters messen.

Integrierter EEPROM

Vielleicht habt ihr euch schon gefragt, was es mit dem kleinen, achtbeinigen IC und den drei Adressjumpern auf sich hat. Der IC ist ein EEPROM mit der Bezeichnung AT24CS32. Ein Datenblatt dazu gibt es hier. Der AT24CS32 hat eine Kapazität von 32 kBit. Ihr könnt also eine ganze Menge Daten auf ihm speichern. Zusammen mit dem DS3231 könntet ihr beispielsweise regelmäßig Messwerte eines Sensors aufnehmen. Oder ihr speichert Ereignisse, wie z.B. die Zeitpunkte, zu denen ein Bewegungsmelder ausgelöst hat.

Im Gegensatz zum DS3231 könnt ihr die I2C Adresse des AT24CS32 modifizieren. Sind alle Jumper offen, dann ist die Adresse is 0x57. Durch das Schließen von Jumpern könnt ihr die Adressen 0x50 bis 0x56 einstellen. Falls ihr die Adressen prüfen wollt, habe ich hier einen I2C Scanner für euch.

Die RTCLib hat keine Funktionen zur Nutzung des EEPROMs implementiert. Ich werde deswegen auch nicht weiter darauf eingehen.

Anschluss des DS3231 an einen Arduino UNO

Der DS3231 am Arduino

Wie ihr seht, ist die Verkabelung simpel. VCC könntet ihr alternativ auch an 3.3 Volt anschließen. Dann wird allerdings das Laden des Akkus nicht funktionieren. Pullups für die I2C Leitungen werden nicht benötigt. SQW verwende ich in einigen Sketchen als Interrupt Ausgang.

Installation der RTCLib für den DS3231

Ihr könnt die RTCLib (Adafruit) über die Bibliotheksverwaltung der Arduino IDE installieren:

Auswahl der RTCLib
Auswahl der RTCLib

Alternativ ladet ihr die Bibliothek hier von Github herunter.

RTCLib benötigt eine weitere Bibliothek namens Adafruit BusIO. Auch die findet ihr in der Bibliotheksverwaltung oder hier auf GitHub.

Einführung in die RTCLib

Wie angekündigt, erkläre ich euch die Funktionen der RTCLib anhand einer Reihe von Beispielsketchen.

Die Zeit stellen und auslesen

Wenn ihr den DS3231 das erste Mal nutzt, kennt er die aktuelle Uhrzeit und das Datum natürlich noch nicht. Das ist wie bei jeder anderen Uhr, die keine Funkuhr ist. Es gibt zwei Möglichkeiten zur Einstellung:

  • rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); übernimmt ganz bequem die Systemzeit eures Computers. Allerdings ist das der Zeitpunkt beim Kompilieren des Sketches. Euer DS3231 geht dann ein wenig nach.
  • rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0)); setzt das Datum nach dem Schema Jahr, Monat, Tag, Stunde, Minute, Sekunde. Weiter unten zeige ich, wie ihr die Zeit mithilfe dieser Funktion über den seriellen Monitor stellen könnt.

Für die Definition von Zeitpunkten, also Datum und Uhrzeit, gibt es in der RTCLib eine komfortable Klasse namens DateTime.

  • DateTime now = rtc.now(); erzeugt das Objekt „now“ der Klasse DateTime und ordnet ihm mit rtc.now(); die aktuelle Zeit zu.

Didaktisch ist es vielleicht ein bisschen ungeschickt das Objekt „now“ und die Funktion now();  gleich zu benennen. Das habe ich von den Original-Beispielsketchen übernommen.

Auf die Jahre, Monate, Tage, Stunden, Minuten und Sekunden der DateTime-Variable now greift ihr mit now.year();, now.month();, etc. zu.

#include "RTClib.h"

RTC_DS3231 rtc;

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

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

#ifndef ESP8266
  while (!Serial); // wait for serial port to connect. Needed for native USB
#endif

  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    abort();
  }

    Serial.println("Setting the time...");
    // When time needs to be set on a new device, or after a power loss, the
    // following line sets the RTC to the date & time this sketch was compiled
     rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }


void loop () {
    DateTime now = rtc.now();

    Serial.print(now.year(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.day(), DEC);
    Serial.print(" (");
    Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);
    Serial.print(") ");
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    Serial.print(now.second(), DEC);
    Serial.println();

    Serial.println();
    delay(3000);
}

 

Ausgabe von SetTime.ino

DS3231 Beispielsketche: Ausgabe SetTime.ino
Ausgabe SetTime.ino

Mehr Funktionen für das Auslesen

Jetzt lernt ihr ein paar weitere Basisfunktionen kennen. Mit rtc.lostPower(); prüft ihr, ob der DS3231 vom Strom getrennt war. War das der Fall, könnt ihr ihn automatisch neu stellen lassen.

Die RTCLib erlaubt es euch, komfortabel mit Zeiten zu rechnen. TimeSpan(days, hours, minutes, seconds); oder TimeSpan(seconds); legt eine Zeitspanne fest, die ihr auf ein Datum addieren oder von ihm abziehen könnt.

Mit now.unixtime(); erhaltet ihr die Sekunden, die zwischen der Zeit „now“ und dem 1.1.1970 (UTC) vergangen sind. Diese sogenannte Unixzeit wird oft als Referenz verwendet. Ihr könnt die aktuelle Unixzeit hier im Netz abrufen. Mehr Informationen zu der Unixzeit findet ihr hier.

Die vom integrierten Sensor gemessene Temperatur in Grad Celsius erhaltet ihr mit rtc.getTemperature();.

// Date and time functions using a DS3231 RTC connected via I2C and Wire lib
#include "RTClib.h"

RTC_DS3231 rtc;

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

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

#ifndef ESP8266
  while (!Serial); // wait for serial port to connect. Needed for native USB
#endif

  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    abort();
  }

  if (rtc.lostPower()) {
    Serial.println("RTC lost power, let's set the time!");
    // When time needs to be set on a new device, or after a power loss, the
    // following line sets the RTC to the date & time this sketch was compiled
    // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }

  // When time needs to be re-set on a previously configured device, the
  // following line sets the RTC to the date & time this sketch was compiled
  // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  // This line sets the RTC with an explicit date & time, for example to set
  // January 21, 2014 at 3am you would call:
  // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}

void loop () {
    DateTime now = rtc.now();

    Serial.print(now.year(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.day(), DEC);
    Serial.print(" (");
    Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);
    Serial.print(") ");
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    Serial.print(now.second(), DEC);
    Serial.println();

    Serial.print(" since midnight 1/1/1970 = ");
    Serial.print(now.unixtime());
    Serial.print("s = ");
    Serial.print(now.unixtime() / 86400L);
    Serial.println("d");

    // calculate a date which is 7 days, 12 hours, 30 minutes, 6 seconds into the future
    DateTime future (now + TimeSpan(7,12,30,6));

    Serial.print(" now + 7d + 12h + 30m + 6s: ");
    Serial.print(future.year(), DEC);
    Serial.print('/');
    Serial.print(future.month(), DEC);
    Serial.print('/');
    Serial.print(future.day(), DEC);
    Serial.print(' ');
    Serial.print(future.hour(), DEC);
    Serial.print(':');
    Serial.print(future.minute(), DEC);
    Serial.print(':');
    Serial.print(future.second(), DEC);
    Serial.println();

    Serial.print("Temperature: ");
    Serial.print(rtc.getTemperature());
    Serial.println(" C");

    Serial.println();
    delay(3000);
}

 

Ausgabe von ds3231.ino

Ausgabe von ds3231.ino
Ausgabe von ds3231.ino

Das Datum / die Uhrzeit komfortabel formatieren

Vielleicht hat euch auch gestört, dass die letzten beiden Sketche die Uhrzeiten des DS3231 ohne vorangestellte Nullen ausgegeben haben. Eine Uhrzeit wie 1:2:3 sieht irgendwie blöd aus. 01:02:03 wäre da besser. Diese und andere Formatierungen der Uhrzeit und des Datums könnt ihr mit der RTCLib ganz bequem vornehmen. Ich denke, dass das Prinzip durch den folgenden Beispielsketch klar wird und ich nichts weiter dazu schreiben muss.

#include <Wire.h>
#include <RTClib.h>

RTC_DS1307 rtc;


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

#ifndef ESP8266
  while (!Serial); // wait for serial port to connect. Needed for native USB
#endif

  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    abort();
  }

  if (! rtc.isrunning()) {
    Serial.println("RTC is NOT running, let's set the time!");
    // When time needs to be set on a new device, or after a power loss, the
    // following line sets the RTC to the date & time this sketch was compiled
    // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }

  // When time needs to be re-set on a previously configured device, the
  // following line sets the RTC to the date & time this sketch was compiled
  // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  // This line sets the RTC with an explicit date & time, for example to set
  // January 21, 2014 at 3am you would call:
  // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}

void loop() {

   DateTime now = rtc.now();

  //buffer can be defined using following combinations:
  //hh - the hour with a leading zero (00 to 23)
  //mm - the minute with a leading zero (00 to 59)
  //ss - the whole second with a leading zero where applicable (00 to 59)
  //YYYY - the year as four digit number
  //YY - the year as two digit number (00-99)
  //MM - the month as number with a leading zero (01-12)
  //MMM - the abbreviated English month name ('Jan' to 'Dec')
  //DD - the day as number with a leading zero (01 to 31)
  //DDD - the abbreviated English day name ('Mon' to 'Sun')

   char buf1[] = "hh:mm";
   Serial.println(now.toString(buf1));

   char buf2[] = "YYMMDD-hh:mm:ss";
   Serial.println(now.toString(buf2));

   char buf3[] = "Today is DDD, MMM DD YYYY";
   Serial.println(now.toString(buf3));

   char buf4[] = "DD.MM.YYYY";
   Serial.println(now.toString(buf4));

   char buf5[] = "MM/DD/YYYY";
   Serial.println(now.toString(buf5));

   Serial.println();

   delay(2000);
}

Ausgabe von toString.ino

DS3231 Beispielsketche: Ausgabe von toString.ino
Ausgabe von toString.ino

Die Zeit über den seriellen Monitor stellen

Auf Basis der bisher vorgestellten Funktionen könnt ihr die Zeit bequem über den seriellen Monitor einstellen:

#include "RTClib.h"

RTC_DS3231 rtc;

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

#ifndef ESP8266
  while (!Serial); // wait for serial port to connect. Needed for native USB
#endif

  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    abort();
  }

  if (rtc.lostPower()) {
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }
  Serial.println("Enter the new time as hh:mm:ss");
}

void loop () {
  DateTime now = rtc.now();
  static int currentSecond = 61; // currentSecond to be different from first nextSecond
  int nextSecond = now.second();
  
  if(nextSecond != currentSecond){
    char buf1[] = "hh:mm:ss";
    Serial.println(now.toString(buf1));
    currentSecond = nextSecond;
  }

  if(Serial.available()){
    int newHour = Serial.parseInt();
    int newMinute = Serial.parseInt();
    int newSecond = Serial.parseInt();
    rtc.adjust(DateTime(now.year(),now.month(),now.day(),newHour, newMinute, newSecond));
    while(Serial.available()){
      Serial.read();
    }
  }
}
 

Ihr gebt die aktuelle Stunde, Minute und Sekunde im seriellen Monitor ein. Das Jahr, der Monat und der Tag werden von der aktuell eingestellten Zeit übernommen.

Einen Alarm im DS3231 einrichten

Ihr könnt den DS3231 wie einen Wecker oder einen Timer einrichten. Zwei Alarme sind einstellbar, wir fangen aber erst mal mit einem an. Mithilfe eines Interrupts könnt ihr euch über den Alarm informieren lassen oder ihr fragt aktiv regelmäßig ab, ob ein Alarm ausgelöst wurde.

Zuvor lernt ihr die neue Funktion rtc.disable32K(); kennen. Damit schaltet ihr die Ausgabe des 32 kHz Signals am Pin „32K“ aus.

Mit  rtc.setAlarm1(); oder rtc.setAlarm2();  stellt ihr den Alarm ein. Die Funktion erwartet zwei Argumente. Das erste Argument ist ein Zeitpunkt in Form eines DateTime Objekts. In diesem Beispielsketch wird rtc.now(); + TimeSpan(); verwendet, d.h.  der DS3231 wird als Timer eingesetzt. Das zweite Argument lässt sich am besten an Beispielen erklären:

  • DS3231_A1_Hour ist die klassische Weckereinstellung. Der Alarm wird ausgelöst, wenn Stunde, Minute und Sekunde mit der festgelegten Alarmzeit übereinstimmen.  Ein Alarm wird pro Tag ausgelöst.
  • DS3231_A1_Second führt jedes Mal zu einem Alarm, wenn die aktuelle Sekunde mit der Sekundeneinstellung in der Alarmzeit übereinstimmt. Der Alarm wird einmal pro Minute ausgelöst.

Es gibt noch mehr Möglichkeiten und verwirrender Weise sind die erlaubten Parameter für Alarm1 und Alarm2 unterschiedlich:

Alarmzeitparameter für den DS3231
Alarmzeitparameter für den DS3231

Weitere Funktionen für die DS3231 Alarme

  • rtc.clearAlarm(1/2); löscht den Alarm. Ein Alarm bleibt so lange bestehen, bis er gelöscht wird.
  • rtc.writeSqwPinMode(DS3231_OFF); deaktiviert das Rechtecksignal an SQW, damit der Pin als Interrupt Pin genutzt werden kann.
  • rtc.disableAlarm(1/2); deaktiviert die Alarmfunktion 1 oder 2.
  • rtc.alarmFired(1/2); prüft, ob Alarm1 bzw. Alarm2 ausgelöst wurde (Registerprüfung).

Im folgenden Sketch wird ein Alarm in 10 Sekunden ausgelöst.

/* Example implementation of an alarm using DS3231
 *
 * VCC and GND of RTC should be connected to some power source
 * SDA, SCL of RTC should be connected to SDA, SCL of arduino
 * SQW should be connected to CLOCK_INTERRUPT_PIN
 * CLOCK_INTERRUPT_PIN needs to work with interrupts
 */

#include <RTClib.h>
// #include <Wire.h>

RTC_DS3231 rtc;

// the pin that is connected to SQW
#define CLOCK_INTERRUPT_PIN 2

void setup() {
    Serial.begin(9600);
    // initializing the rtc
    if(!rtc.begin()) {
        Serial.println("Couldn't find RTC!");
        Serial.flush();
        abort();
    }
    
//    if(rtc.lostPower()) {
//        // this will adjust to the date and time at compilation
//        rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
//    }
    
    //we don't need the 32K Pin, so disable it
    rtc.disable32K();
    
    // Making it so, that the alarm will trigger an interrupt
    pinMode(CLOCK_INTERRUPT_PIN, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(CLOCK_INTERRUPT_PIN), onAlarm, 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);
    
    // stop oscillating signals at SQW Pin
    // otherwise setAlarm1 will fail
    rtc.writeSqwPinMode(DS3231_OFF);
    
    // turn off alarm 2 (in case it isn't off already)
    // again, this isn't done at reboot, so a previously set alarm could easily go overlooked
    rtc.disableAlarm(2);
    
    // schedule an alarm 10 seconds in the future
    if(!rtc.setAlarm1(
            rtc.now() + TimeSpan(10),
            DS3231_A1_Second // this mode triggers the alarm when the seconds match. See Doxygen for other options
    )) {
        Serial.println("Error, alarm wasn't set!");
    }else {
        Serial.println("Alarm will happen in 10 seconds!");  
    }
}

void loop() {
    // print current time
    char date[10] = "hh:mm:ss";
    rtc.now().toString(date);
    Serial.println(date);
    // resetting SQW and alarm 1 flag
    // using setAlarm1, the next alarm could now be configurated
    if(rtc.alarmFired(1)) {
        rtc.clearAlarm(1);
        Serial.println("Alarm cleared");
    }
    
    delay(2000);
}

void onAlarm() {
    Serial.println("Alarm occured!");
}

 

Ausgabe von ds3231_alarm_10s.ino

DS3231 Beispielsketche: Ausgabe von ds3231_alarm_10s.ino
Ausgabe von ds3231_alarm_10s.ino

Der Alarm wird jede Minute erneut ausgelöst, wenn die Sekunden mit der Alarmzeit übereinstimmen.

Zwei Alarme stellen

Wenn ihr verstanden habt, wie ein Alarm eingestellt wird, dann sind zwei Alarme auch kein Problem. Hier stelle ich keine neuen Funktionen vor und ich glaube, der Sketch ist in Verbindung mit der Ausgabe selbsterklärend.

/* Example implementation of an alarm using DS3231
 *
 * VCC and GND of RTC should be connected to some power source
 * SDA, SCL of RTC should be connected to SDA, SCL of arduino
 * SQW should be connected to CLOCK_INTERRUPT_PIN
 * CLOCK_INTERRUPT_PIN needs to work with interrupts
 */

#include <RTClib.h>
// #include <Wire.h>

RTC_DS3231 rtc;

// the pin that is connected to SQW
#define CLOCK_INTERRUPT_PIN 2

void setup() {
    Serial.begin(9600);
    // initializing the rtc
    if(!rtc.begin()) {
        Serial.println("Couldn't find RTC!");
        Serial.flush();
        abort();
    }
    
//    if(rtc.lostPower()) {
//        // this will adjust to the date and time at compilation
//        rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
//    }
    
    //we don't need the 32K Pin, so disable it
    rtc.disable32K();
    
    // Making it so, that the alarm will trigger an interrupt
    pinMode(CLOCK_INTERRUPT_PIN, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(CLOCK_INTERRUPT_PIN), onAlarm, 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);
    
    // stop oscillating signals at SQW Pin
    // otherwise setAlarm1 will fail
    rtc.writeSqwPinMode(DS3231_OFF);
    
    // turn off alarm 2 (in case it isn't off already)
    // again, this isn't done at reboot, so a previously set alarm could easily go overlooked
    rtc.disableAlarm(2);

    Serial.print("Start time: ");
    printTime();
    
    // schedule an alarm 30 seconds in the future
    rtc.setAlarm1(rtc.now() + TimeSpan(10), DS3231_A1_Second); // this mode triggers the alarm when the seconds match. See Doxygen for other options
    Serial.println("Alarm 1 will happen in 10 seconds!");  

    // schedule an alarm 2 minutes in the future   
    rtc.setAlarm2(rtc.now() + TimeSpan(0,0,2,0), DS3231_A2_Minute); // this mode triggers the alarm when the minutes match. See Doxygen for other options
    Serial.println("Alarm 2 will happen in 2 minutes (when full minutes match)!");
    Serial.println();  
   
}

void loop() {
    // resetting SQW and alarm 1 flag
    // using setAlarm1, the next alarm could now be configurated
    if(rtc.alarmFired(1)) {
        printTime();
        rtc.clearAlarm(1);
        Serial.println("Alarm 1 cleared");
        Serial.println();
    }

    // resetting SQW and alarm 1 flag
    // using setAlarm1, the next alarm could now be configurated
    if(rtc.alarmFired(2)) {
        printTime();
        rtc.clearAlarm(2);
        Serial.println("Alarm 2 cleared");
        Serial.println();
    }
}

void printTime(){
     // print current time
    char date[10] = "hh:mm:ss";
    rtc.now().toString(date);
    Serial.println(date);
}

void onAlarm() {
    Serial.println("Alarm occured!");
}

 

Ausgabe von ds3231_2_alarms.ino

DS3231 Beispielsketche: Ausgabe von ds3231_2_alarms.ino
Ausgabe von ds3231_2_alarms.ino

Hier erkennt man schön die Wirkung des Parameters D3231_A2_Minute.

Einen Alarm an einem Datum / Uhrzeit

Auch in diesem Sketch werden keine neuen Funktionen vorgestellt. Er dient nur der Vertiefung.

#include <RTClib.h>
// #include <Wire.h>

RTC_DS3231 rtc;

// the pin that is connected to SQW
#define CLOCK_INTERRUPT_PIN 2
volatile bool alarm = false;

void setup() {
    Serial.begin(9600);
    if(!rtc.begin()) {
        Serial.println("Couldn't find RTC!");
        Serial.flush();
        abort();
    }
    
//    if(rtc.lostPower()) {
//        // this will adjust to the date and time at compilation
//        rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
//    }
    
    //we don't need the 32K Pin, so disable it
    rtc.disable32K();
    
    // Making it so, that the alarm will trigger an interrupt
    pinMode(CLOCK_INTERRUPT_PIN, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(CLOCK_INTERRUPT_PIN), onAlarm, 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);
    
    // stop oscillating signals at SQW Pin
    // otherwise setAlarm1 will fail
    rtc.writeSqwPinMode(DS3231_OFF);
    
    // turn off alarm 2 (in case it isn't off already)
    // again, this isn't done at reboot, so a previously set alarm could easily go overlooked
    rtc.disableAlarm(2);
    
    // schedule an Alarm for a certain date (day of month), hour, minute, and second
    DateTime alarmTime (2021, 1, 24, 21, 33, 0);
    rtc.setAlarm1(alarmTime, DS3231_A1_Date); 
    Serial.print("Current time: ");
    printTime();
    Serial.println();
}

void loop() {
    if(alarm){
        if(rtc.alarmFired(1)) {
            Serial.println("Alarm occured, current time: ");
            printTime();
            rtc.clearAlarm(1);
            Serial.println("Alarm 1 cleared");
        }
    }
}

void printTime(){
    // print current time
    DateTime now = rtc.now();
    char date[] = "DD.MM.YYYY, ";
    Serial.print(now.toString(date));
    char time[] = "hh:mm:ss";
    rtc.now().toString(time);
    Serial.println(time);
}

void onAlarm() {
    alarm = true;
}

 

Ausgabe von DS3231_alarm_fixed_time_and_date.ino

DS3231 Beispielsketche: Ausgabe von DS3231_alarm_fixed_time_and_date.ino
Ausgabe von DS3231_alarm_fixed_time_and_date.ino

Den DS3231 mit einem DCF77 stellen

Vielleicht wollt ihr den DS3231 automatisch über ein DCF77-Modul stellen? Das hätte auch den Vorteil, dass ihr euch nicht um die Umstellung der Sommer- und Winterzeiten kümmern müsst. Hier die Schaltung mit einem DCF77-Modul von Canaduino:

DCF77 und DS3231 am Arduino Nano

In meinem Beispielsketch dazu stelle ich den DS3231 alle fünf Minuten nach. Das macht natürlich in der Praxis keinen Sinn, aber damit könnt ihr in ein paar Minuten überprüfen, dass es tatsächlich funktioniert. Die Nachstellperiode lässt sich einfach anpassen.

#include <util/parity.h> //comment out if you don't use an AVR MCU
#include "RTClib.h"
#define DISPLAY_FQY 3000
#define ALARM_1 1
#define ALARM_2 2
#define TIME_ADJUST_PERIOD 300 // adjusment every 5 mins
int dcf77InterruptPin = 2;
int pcnPin = 7; // for Canaduino DCF77

volatile unsigned long lastDisplay = 0;
volatile unsigned long lastInt = 0;
volatile unsigned long long currentBuf = 0;
volatile unsigned long long nextBuf = 0;
volatile bool timeUpdateAvailable = false;
volatile byte bufCounter;

RTC_DS3231 rtc;

void setup(){
  Serial.begin(115200);
  pinMode(pcnPin, OUTPUT); // only for Canaduino DCF77
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    abort();
  }
  
  rtc.adjust(DateTime(2000, 1, 1, 0, 0, 0));
  rtc.disable32K();

  pinMode(dcf77InterruptPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(dcf77InterruptPin), DCF77_ISR, CHANGE);
  rtc.clearAlarm(ALARM_1);
  rtc.clearAlarm(ALARM_2);
  rtc.disableAlarm(ALARM_2);
  rtc.writeSqwPinMode(DS3231_OFF);
}

void loop(){
  static DateTime now = rtc.now();
  
  if(timeUpdateAvailable){
    evaluateSequence();
    detachInterrupt(digitalPinToInterrupt(dcf77InterruptPin));
    Serial.println("Time Updated");
    // if you change TIME_ADJUST_PERIOD, you might also need to change DS3231_A1_Minute
    rtc.setAlarm1(rtc.now() + TimeSpan(TIME_ADJUST_PERIOD), DS3231_A1_Minute);
    rtc.clearAlarm(ALARM_1);
    timeUpdateAvailable = false;
  }

  if(rtc.alarmFired(ALARM_1)){
    rtc.clearAlarm(ALARM_1);
    attachInterrupt(digitalPinToInterrupt(dcf77InterruptPin), DCF77_ISR, CHANGE);
    Serial.println("Next time adjustment started...");
  }
  
  if (millis() - lastDisplay > DISPLAY_FQY){
    displayDateTime(&now);
  }

}

void displayDateTime(DateTime now){
  lastDisplay = millis();
  now = rtc.now();
  char buf1[] = "Today is DDD, MMM DD YYYY";
  Serial.println(now.toString(buf1));
  char buf2[] = "Current time is hh:mm:ss";
  Serial.println(now.toString(buf2));
  Serial.println();  
}

void DCF77_ISR(){
  unsigned int dur = 0;
  dur = millis() - lastInt; 
  
  if(digitalRead(dcf77InterruptPin)){
    if(dur>1500){
      if(bufCounter==59){
        timeUpdateAvailable = true;
        currentBuf = nextBuf;
      }
      bufCounter = 0;
      nextBuf = 0;
    }
  }
  else{
    if(dur>150){
      nextBuf |= ((unsigned long long)1<<bufCounter);
    }
    bufCounter++;
  }
  lastInt = millis();
}

void evaluateSequence(){
  byte dcf77Year = (currentBuf>>50) & 0xFF;    // year = bit 50-57
  byte dcf77Month = (currentBuf>>45) & 0x1F;       // month = bit 45-49
  byte dcf77DayOfWeek = (currentBuf>>42) & 0x07;   // day of the week = bit 42-44
  byte dcf77DayOfMonth = (currentBuf>>36) & 0x3F;  // day of the month = bit 36-41
  byte dcf77Hour = (currentBuf>>29) & 0x3F;       // hour = bit 29-34
  byte dcf77Minute = (currentBuf>>21) & 0x7F;     // minute = 21-27 
  bool parityBitMinute = (currentBuf>>28) & 1;
  bool parityBitHour = (currentBuf>>35) & 1;
  bool parityBitDate = (currentBuf>>58) & 1;

  if((parity_even_bit(dcf77Minute)) == parityBitMinute){
    if((parity_even_bit(dcf77Hour)) == parityBitHour){
      if(((parity_even_bit(dcf77DayOfMonth) + parity_even_bit(dcf77DayOfWeek) 
           + parity_even_bit(dcf77Month) + parity_even_bit(dcf77Year))%2) == parityBitDate){
        rtc.adjust(DateTime(rawByteToInt(dcf77Year) + 2000, rawByteToInt(dcf77Month), 
            rawByteToInt(dcf77DayOfMonth), rawByteToInt(dcf77Hour), rawByteToInt(dcf77Minute), 0));
      }
    }
  }
}

unsigned int rawByteToInt(byte raw){
  return ((raw>>4)*10 + (raw & 0x0F));
}

//uncomment the following lines if you don't use an AVR MCU
//bool parity_even_bit(byte val){
//  val ^= val >> 4;
//  val ^= val >> 2;
//  val ^= val >> 1;
//  val &= 0x01;
//  return val;
//}

 

Ich gehe hier nicht auf die Details ein. Alles bezüglich des DS3231 sollte mithilfe dieses Beitrages verständlich sein. Für den DCF77 schaut in diesen Beitrag.

PCF8523, PCF8563 und DS1307

Mithilfe der RTCLib könnt ihr auch die alternativen Echtzeituhren PCF8523, PCF8563 und DS1307 steuern. Den PCF8523 und den DS1307 habe ich ausprobiert. Die Beispielsketche der RTCLib für diese RTCs liefen anstandslos. Die Einstellung und das Lesen von Uhrzeit und Datum funktioniert wie beim DS3231. Das Gleiche gilt für die angenehmen Rechenfunktionen mittels TimeSpan und die zahlreichen Formatierungsoptionen für die Ausgabe.

Der PCF8523 (Datenblatt hier) und der PCF8563 (Datenblatt hier) sind dem DS3231 recht ähnlich. Zumindest hatte ich den Eindruck beim Überfliegen der Register im Datenblatt. Leider hat die RTCLib aber anscheinend nicht alle vorhandenen Funktionen implementiert. Es gibt einen Countdown Timer, aber keine klassische Alarmfunktion.

Alternative zum DS3231: der PCF8523
Alternative zum DS3231: der PCF8523

Der DS1307 ist etwas einfacher gestrickt. Er besitzt an sich keine Alarmfunktion. Dafür hat er wie der DS3231 einen EEPROM integriert und eine Akkuladefunktion.

Alternative zum DS3231: der DS1307

Software RTC

Ihr könnt die Uhrzeit- und Datumsfunktionen der RTCLib aber auch ganz ohne RTC Modul nutzen. Dazu wird die aktuelle Zeit beim Kompilieren von der Systemzeit des Computers einmalig übernommen und dann mit der millis() Funktion aktualisiert. Der Beispielsketch softrtc.ino (den ich hier nicht noch einmal abdrucke) zeigt euch, wie es geht. Die Methode hat zwei Nachteile:

  • Die millis() Methode ist wesentlich ungenauer.
  • Bei jedem Reset oder jeder Stromunterbrechung ist die aktuelle Zeit verloren.

Für die Fortgeschrittenen: Die AVR basierten Arduino Boards benutzen für millis() den Timer0 und den zugehörigen Timer0 Overflow Interrupt. Ihr findet die Definitionen unter Arduino\hardware\arduino\avr\cores\arduino\wiring.c.

DS3231 Bibliotheken: Alternativen zu RTCLib

Ich habe mir auch eine Reihe anderer Bibliotheken für den DS3132 auf Github angeschaut. Wenn ihr die RTCLib als zu groß und komplex empfindet, dann könntet ihr euch die DS3231_Simple Bibliothek einmal anschauen. Der Name ist Programm. Sie besitzt alle Basisfunktionen für die Uhrzeit, einschließlich der Alarmprogrammierung. Was ihr fehlt (Stand Februar 2020), sind die Alarminterrupts am SQW Pin. Ihr müsst also regelmäßig abfragen, ob ein Alarm vorliegt. Ein eindeutiger Vorteil hingegen ist, dass die DS3231 auch Funktionen für das Beschreiben des EEPROMs besitzt.

Sehr akkurat und sehr vollständig ist die Bibliothek DS3132RTC. Nur ist sie vielleicht für den nicht so erfahrenen Arduino Fan ein bisschen schwer verdaulich. Schaut euch die Beispielsketche an, dann wisst ihr was ich meine.

Danksagung

Der Firma Adafruit danke ich für die Veröffentlichung ihrer Bibliothek.

Die Grundlage meines Beitragsbildes, in das ich den DS3231 hineinmontiert habe, stammt von andreas N (domeckopol) auf Pixabay.

131 thoughts on “DS3231 – Echtzeituhr

  1. Hallo die Beiträge sind allesamt sehr Interressant. Fast alles wurde bereits besprochen, nur eins nicht. Wie bekomme ich die Serielle Zeitanzeige (Datum, Uhrzeit) auf einem TFT Display angezeigt?

    1. Hallo Thomas,

      wie du die Ausgabe auf ein Display bekommst, hängt vom Display ab und von der Bibliothek, die du dafür verwendest. Wie man aus dem Datum und der Zeit einen String nach seinen Wünschen baut, habe ich im Sketch toString.ino gezeigt. Die Bibliotheken für Displays können normalerweise auch Strings verarbeiten.

      Wenn du eines dieser günstigen Displays mit SSD1306 Treiber benutzt, also so etwas:
      https://amzn.eu/d/01fEEPMg
      ….dann könntest du die Bibliothek Adafruit_SSD1306 verwenden:
      https://github.com/adafruit/Adafruit_SSD1306
      Die lässt sich über die Arduino Bibliotheksverwaltung installieren. Da gibt es auch Beispielsketche. Wenn du z.B. diesen hier nimmst
      https://github.com/adafruit/Adafruit_SSD1306/blob/master/examples/ssd1306_128x64_i2c/ssd1306_128x64_i2c.ino
      , dann gibst du das, was du normalerweise über Serial.println() ausgibst über display.println() aus.
      Oder wo ist das Problem?

      Ich würde ich erst einmal ohne DS3231 mit dem Display herumexperimentieren. Versuche erst einmal einen String so darzustellen, wie du es haben möchtest und dann vereinigst du den Display-Teil und den DS3231-Teil.

      VG, Wolfgang

  2. Hallo Wolfgang,
    sehr schöne Erklärung vom DS3231. Hat mir sehr geholfen.Ich hab den 2Alarm Sketch auf meinen Uno hochgeladen, und doktore daran herum um einen Alarm alle halbe Stunde zu generieren. Das mit der Zeit hab ich jetzt raus…
    Füge ich aber deinen Sketch 1:1 in meinen ein, bringt er in der Zeile 33:

    attachInterrupt(digitalPinToInterrupt(CLOCK_INTERRUPT_PIN), onAlarm, FALLING);

    die Fehlermeldung:

    ….In function ‚void setup()‘:
    ….:137:65: error: ‚onAlarm‘ was not declared in this scope

    Den Speicherpfad hab ich mal weggelassen…
    Ich bin kurz davor meinen Psychater anzurufen….

    ganz unten in der Fehlermeldung steht: Compilation error: ‚onAlarm‘ was not declared in this scope

    1. Hallo Jens,

      das mit dem Psychiater können wir, so glaube ich, verhindern. Wenn onAlarm nicht bekannt ist, dann scheint die Funktion onAlarm zu fehlen. Die ist in meinen Beispielsketchen jeweils ganz unten. Könnte das das Problem sein?

      VG, Wolfgang

      1. Danke für deine Antwort, aber es ist auch in meinem Programm drin. Auch ganz unten…
        Aber es ist immer aufgetreten, wenn ich was im Programm geändert habe, was gar nichts damit zu tun hatte. Aber es ist jetzt anscheinend weg…bis zum nächsten mal….
        Aber Danke für deine Antwort und deine tolle Beschreibung und Erklärung !!!
        Als nächstes kommt Deep Sleep….deswegen auch die 2 Alarme… da habe ich mir deine Erkläung dazu in die Favotiten geklemmt !

        Was ich viel schlimmer finde, das ich meinene NANO´s (hab 10 Stück) nicht mit IDE 2.XXX hochladen kann… Alles probiert… anderer COM, neue Kabel, anderer Rechner… alles Bullshit !! Als Quertest meinen alten Pro Mini mit externem 340 dran…geht auch nicht…
        IDE 1.8.19 mobile drauf…. alles funzt super !!! never update a running system….

        1. Hallo Jens,

          zum Nano und der IDE 2.xxx: Unter Werkzeuge -> Processor musst du die Einstellung „ATmega328P (Old Bootloader)“ vornehmen. Ist es vielleicht das? Ansonsten könntest du mir mal die Fehlermeldungen zuschicken (wolfgang.ewald@wolles-elektronikkiste.de), vielleicht finde ich ja was.

          VG, Wolfgang

          1. Hallo Wolfgang,

            ich habe wirklich alles probiert…oldbootloader und alles was sonst in irgendwelchen Foren vorgeschlagen wurde (siehe oben)….. aber selbst mein Pro Mini den ich seit 5 Jahren immer neu programmiert habe um Sachen zu testen ging mit der neuen IDE nicht mehr….
            Und sorry….ich installiere die IDE 2.xxx garantiert nicht mehr…. ich arbeite noch an meinem jetzigen Projekt und alles läuft super – keine hochladeprobleme mehr…..
            bis auf das Problem das mir so langsam der Speicherplatz ausgeht….lol…aber da kann die IDE nichts für. Ich bin grade auf deiner SleepModes Seite….das letzte was in meinem Programm noch fehlt….und der ganz oben beschriebene Fehler kommt auch nicht mehr.

            1. Ja, wenn du keine Lust mehr hast, dann kann ich das verstehen. Ist aber schon sehr merkwürdig und auch kein übliches Problem. Gegenwärtig kann man die alte IDE auch noch nutzen, aber wahrscheinlich ist es nur eine Frage der Zeit, bis das nicht mehr möglich ist. Wir werden sehen. In dem Fall würde ich dann alle Versionen löschen und nur die Version 2 installieren.

              1. Hallo Wolfgang

                ich hatte vorher die Version 1.8.19 und habe sie geupdatet..danach ging nix mehr…. und ich habe jetzt in anderen Foren nachgeschaut und dort sind diese Probleme auch aufgetreten….nach downgrade gings wieder….das finde ich schon komisch. Und ich hatte immer nur eine Version auf den Rechnern- gerade um internen Konflikten zwischen den Versionen aus dem Weg zu gehen, Also muss die neuere IDE ne Macke haben. Nicht für alle Boards aber für die mit 340 er Chip. Mein Uno hat keine Probleme mit der neuen IDE.
                Ich finde es aber trotzden toll, das du so schnell antwortest. und dich so reinkniest !!! chapo ! Meine Hochachtung ! – und… mein Programm läuft seit 13 Uhr Störungsfrei ! incl. deepsleep – dank dir und deiner Seite !

                VG Jens

  3. Hallo Wolfgang,

    erstmal Respekt für die viele detailierte Arbeit hier. Es gibt nur sehr wenige Seiten, die so gut erklären.
    Jetzt spiele ich auch schon eine ganze Weile mit den Arduinos und ESP32 rum und hab mir auch mal den HW-111 (DS1307) für ein kleines Projekt besorgt. Jetzt hat der aber folgendes Phenomen:
    – lade ich den ersten Beispielsketch hoch und stelle die Zeit ist alles ok.
    – dann den Sketch nochmal ohne erneute Zeiteinstellung -> auch ok
    – trenne ich dann aber den Arduino von der Versorgungsspannung bleibt die Zeit stehen
    – starte ich den Arduino nach bsp 5min neu, fängt die Zeit an da weiter zu laufen, wo sie stehen geblieben ist -> vor 5min

    Ich hätte erwartet, dass das Modul mit seiner CR2032 die Zeit auch ohne Arudino weiter fortschreibt.

    hab ich hier irgendwo einen Denkfehler?

    Gruß Christian

    1. Hallo Christian,

      hoppala, der Kommentar ist mir durchgerutscht. Sorry! Ich bin erst durch die Anwort von Tristan darauf gestoßen. Und ja, ich würde erst einmal die Batterie prüfen.

      Welche Bibliothek verwendest du? Wenn du die von Adafruit verwendest, dann musst du den DS1307 mit rtc.isrunning() anschubsen, damit die Zeit läuft. Ich schätze, dass rtc.begin() für einen definierten Ausgangszustand sorgt und die Uhr anhält. Das ist anders als beim DS3231.

      VG, Wolfgang

  4. Hallo
    ich möchte mit meinem Arduino die Minuten in eine Variable schreiben:
    int Minuten;

    Minuten=now.minute();
    und damit arbeiten.
    Manchmal zeigt die Variabel den richtigen Wert an, aber manchmal zufällige Werte oder den Wert anderer Parameter. Gibt es einen richtigen Weg an diesen Wert zu kommen?
    Danke

    1. Hallo Hanne,
      sofern du die Zeit richtig ausgelesen hast:
      DateTime now = rtc.now();
      …solltest du mit now.minute() auch die richtigen Minuten erhalten.
      Was da bei dir schiefläuft, kann ich dir nicht sagen. Dafür müsstest du schon ein paar mehr Details teilen, z.B. wie hast du now ermittelt, welche Bibliothek nutzt du, nutzt du den DS3231 oder ein anderes Modul oder Software RTC?
      VG, Wolfgang

      1. Hallo Wolfgang,

        im Moment muss ich für ein Schulprojekt eine Funkuhr mit einem Arduino bauen. Dabei benutze ich den Arduino Uno, ein DCF 77 Reichelt Modul und eine 7 Segmentanzeige (4-Digit-Display V1.2). Deine Seite hat mir dabei sehr weiter geholfen und ich empfange mittlerweile die Passende Uhrzeit, die ich dann im Serial Monitor ablesen kann. Nun scheitere ich leider daran diese Uhrzeit auf meiner 7 Segmentanzeige anzeigen zu lassen. Ich würde mich sehr über eine Rückmeldung freuen.

        MfG Emilius

        1. Hi, das müsste so ein TM1637 basiertes Teil sein, richtig? Dann würde die folgende Bibliothek funktionieren:

          https://github.com/avishorp/TM1637

          Die Bibliothek findest du auch über den Bibliotheksmanager der Arduino IDE. Da gibt es auch einen Beispielsketch. Für die Uhrzeit sollte es so gehen:
          Ausgabe = Stunden × 100 + Minuten
          und dann:
          display.showNumberDec(Ausgabe, true);
          VG, Wolfgang

  5. Guten Abend und frohe Weinachten nachträglich,
    Wie sieht es mit der Zeit Umstellung aus, gibt es da eine Möglichkeit das, das Modul die Zeit automatisch umstellt?
    LG Migel

    1. Ebenso frohes Fest nachträglich! Die Sommer- und Winterzeit ist landesspezifisch und ist deswegen, anders als etwa Schaltjahre, nicht implementiert. Um die Umstellung automatisch vorzunehmen, bleibt dann nur:
      1) Einen Alarm einbauen für den Zeitpunkt der nächsten Zeitumstellung.
      2) Regelmäßig abfragen nach dem Schema: „wenn ein bestimmtes Datum und eine bestimmte Uhrzeit, dann stelle eine Stunde vor / zurück“.
      3) Koppeln mit einer Funkuhr: https://wolles-elektronikkiste.de/dcf77-funkuhr
      VG, Wolfgang

      1. Hallo Wolfgang
        Als ziemlicher Anfänger in Sachen Mikrokontroller, habe ich mich an den ESP8266-12E mit integr.
        OLED-Display, 0.91″gewagt.
        Damit möchte ich zwei Temperaturen messen, das funktioniert, die Werte, über SPI, auf eine SD-Karte schreiben, das funktioniert auch, und auch den Zeitstempel zu den Temp.-Werten von einer RTC (DS1307Z) auf der SD-Karte protokollieren, und das funktioniert noch nicht.

        Wenn auf dem ESP nur ein I2C-Scanner läuft und nur die RTC an
        zwei frei programmierbaren Pin’s angeschlossen ist, kann ich die Adresse 0x68 auslesen.
        Ist die RTC aber an 4SDA und 5SCL angeklemmt, funktioniert Seriell nicht mehr.
        Wird SCL abgeklemmt, geht Seriell wieder, upload auch.
        Leider hat der ESP keine frei belegbaren Anschlüsse mehr auf die ich ausweichen könnte

        Laut dem DB des ESP8266-E12 sind Pin 4(SDA), 5(SCL), 16(RST) vom OLED belegt,
        können aber laut DB aber ach für I2C genutzt werden.

        Hast Du einen Tipp? Oder jemand aus der Leserschaft?

        Besten Dank!

        1. Hallo Kurt,
          so ein Teil hast du:
          https://www.az-delivery.de/en/products/esp8266-mikrocontroller-mit-integrierten-0-91-oled-display ?
          Ich könnte mir ja erklären, dass sich Display und RTC aus irgendwelchen Gründen an den Pins 4 und 5 stören, aber warum dann Serial nicht funktioniert und kein Upload möglich ist, darauf kann ich mir noch keinen Reim machen. Hast du vielleicht einen Link zum Datenblatt? Und läuft das Display auch über I2C? Was erhältst du, wenn du den I2C Scanner laufen lässt, ohne dass irgendetwas zusätzlich an 4 und 5 angeschlossen ist?
          VG, Wolfgang

          1. Hallo Wolfgang, besten Dank für die prompte Antwort!
            Diese Teile habe ich in meinem Aufbau
            https://www.az-delivery.de/en/blogs/azdelivery-blog-fur-arduino-und-raspberry-pi/thermometer-mit-oled-display und
            https://www.az-delivery.de/products/datenlogger-modul.
            Habe den Bsp.-code von der Thermometer-Seite geladen, mit meinen Ergänzungen für RTC und SD.

            In der Hoffnung auf Erfolg, habe ich diese Zeile im Bsp.-code von AZdelivery

            //Display initialisieren
            U8G2_SSD1306_128X32_UNIVISION_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ 5, /* data=*/ 4, /* reset=*/ 16);

            geändert in:
            U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0,/* reset=U8X8_PIN_NONE*/16, /* clock=*/ 5, /* data=*/ 4);
            also von SW_I2C auf HW_I2C (glaube ich 🙂 ).
            Hat leider nix gebracht.

            Der scanner findet die Adresse 0x3c, an 4SDA und 5SCL, wenn nichts weiter an dem ESP-Modul angeschlossen ist.
            Die I2c-Adresse des RTC 0x68, sehe ich, wenn SDA und SCL auf die Pin’s, 12, 13 gelegt sind, also eine zweite I2C-Schnitstelle. In diesem Fall meldet der scanner auch
            an 4, 5 die Adresse 0x3c.
            Tja, ohne Zeitstempel sind die Temp.-Werte nicht wirklich was wert.
            Nochmals Danke für Deine Bemühungen!

            1. Ich habe mir auch mal so ein ESP8266 mit Display bestellt. Ich habe auch eine Vermutung. Das werde ich dann mal ausprobieren, aber du kannst das selbst auch probieren. Gehe mal in deinen Library Ordner und dort in u8g2/cppsrc und entkommentiere in U8x8lib.h die Zeile 62: #define U8X8_DO_NOT_SET_WIRE_CLOCK
              Dann speicher die Datei und versuche es noch einmal. Ich komme darauf, da über der Zeile steht:
              „Uncomment this to switch off Wire.setClock() invocations.
              This is useful if you connect multiple devices to the same I2C bus that
              is used for the monochrome display.“

              Wenn es nicht geht, dann kommentiere die Zeile einfach wieder aus.

              1. Hallo Wolfgang, leider hat die Änderung nicht zum Erfog geführt.
                Hier der Ablauf wenn ich NUR den I2C-scanner laden will und
                in meinem Aufbau, SDA vom Thermom.-Modul mit SDA vom Data-Logger verbinde, dann sieht es im seriellen Monitor so aus:
                //Serial port COM7
                //Connecting…….._____….._____….._____….._____….._____….._____….._____
                //A fatal esptool.py error occurred: Failed to connect to ESP8266: Timed out //waiting for packet header.
                Wird während des Verbindungsversuch’s die SDA-Verbindung abgezogen,
                wird die Verbindung mit COM7 aufgebaut, upload ds I2C-scanners läuft durch. Danach zeigt der scanner auch die I2C-Adresse des Thermom.-Moduls. Verbinde ich jetzt SDA wieder, kommen am Monitor irgendwelche Zeichen an, so als hätte sich die Baud-Rate geändert.
                Bin gespannt was Du mit Deinem Display herausfindest.
                Viele Grüße
                Kurt

                1. Hallo Kurt, das Modul ist jetzt angekommen. Wie im E-Book von AZDelivery empfohlen, habe ich als Board „Ich habe ein DS3231 an die mit SDA und SCL gekennzeichneten Pins gehängt und meinen I2C Scanner laufen lassen:
                  https://wolles-elektronikkiste.de/i2c-scanner
                  Ergebnis: 0x3C wird gefunden. Das scheint das Display zu sein.

                  Dann habe ich im Scanner Sketch Wire.begin() durch Wire.begin(D4, D5) ersetzt. Ergebnis: 0x57 und 0x68 werden gefunden. Das sind die RTC und das EEPROM auf dem Modul.

                  Dann bin ich wieder zurück auf Wire.begin() gegangen und habe den DS3231 an D2 (SDA) und D1 (SCL) gehängt. Ergebnis: 0x3C, 0x57 und 0x68 werden gefunden!

                  Jetzt muss man wissen, dass die „D“-Bezeichnungen von den GPIO-Nummern abweichen:
                  D2 ist 4, D1 ist 5
                  D4 ist 2, D5 ist 14

                  Also ist aus meiner Sicht etwas bei der Beschriftung des Boards schiefgelaufen. D2 sollte mit SDA beschriftet sein und D1 mit SCL.
                  SDA wiederum sollte mit D4 beschriftet sein und SCL mit D5.

                  Hänge also SDA und SCL des RTC an D2 und D1. Dann kannst du den als SDA beschrifteten Pin als digitalen Pin D4 bzw. 2 und den mit SCL beschrifteten Pin als digitalen Pin D5 bzw. 14 nutzen.

                  Allerdings gibt es noch eine Einschränkung für den Pin SDA=D4=2 und zwar mag das Board es nicht, wenn der Pin beim Booten bzw. beim Upload auf GND gezogen wird. Nutze diesen Pin also nur für etwas mit hochohmigem Eingang oder für Anschlüsse, die auf HIGH gezogen sind.

                  Oder: hau das Ding dem Lieferanten um die Ohren!

                  Hoffe, das hilft.

                  VG, Wolfgang

                  1. Hallo Wolfgang, besten Dank für Deine Bemühungen!
                    Ich habe meinen Aufbau zerlegt und nochmal, nach Deiner Beschreibung, zusammen gestöpselt und, jetzt habe ich auch einen Zeitstempel zu den Temperaturwerten 🙂 !

                    Danke und viele Grüße
                    Kurt

  6. Servus Wolfgang,
    erst mal vielen Dank für Deine äußerst hilfreichen Seiten!

    Ich habe schlechte Erfahrungen mit dem DS3107.
    In verschiedenen Anwendungen mit dem RTC ist der Arduino (idR. Nano) nach einer willkürlichen Zeit ausgestiegen. Mal schon nach einer Stunde, mal zwei, drei Tage. Programm hängt sich auf, in einer Anwendung läuft lediglich der Sekunden-„Blinker“ weiter, weil der direkt über das Timer/Counter Control Register angesteuert wird.
    Ich weiß nicht, ob ich ein systemisches Problem/Denkfehler in meinen Hardware-Designs habe. Ich arbeite eigentlich immer auch mit (teilweise mehreren) I²C-gesteuerten Displays via HT16K33, PCF8574 etc.
    Mittlerweile glaube ich, dass da irgendwann Botschaften auf dem Bus kollidieren.
    Jedenfalls liefen meine Tests ohne den DS3107 stabil. (Um die Displays selber auszuschließen, habe ich die Zeitberechnung testweise im Programm umgesetzt und mit dem internen Counter des ATMega getaktet)
    Schließlich hab ich mir ein paar DS3231 gekauft und eingesetzt. Siehe da: alles läuft stabil.
    Ich hab die genaue Ursache nicht mehr weiter gesucht, weil ich schon total genervt war.
    Wollte nur meine Erfahrungen teilen, vielleicht macht ja jemand gerade das Gleiche durch.
    Gruß
    Wolfgang

    1. Vielen Dank für das Teilen Deiner Erfahrungen. Unabhängig von den Stabilitätsproblemen, die Du berichtest, würde auch wegen der viel besseren Genauigkeit immer zum DS3231 greifen. Der Preisunterschied ist nicht so groß, dass man die Abstriche bei der Qualität machen sollte. Gruß von Wolfgang zu Wolfgang!

  7. Hallo Wolfgang,
    erst einmal vielen Dank für die ausführliche Anleitung und Dein Engagement.
    Wir suchen einen Timer, der (möglichst niedrigstromig) eine Last schalten kann (3,6V, >300mA) Der DS3231 schaltet aber nur einen Interrupt.
    Fragen:
    Gibt es ein fertiges Modul was Lasten schalten kann?
    Wie würde man den DS3231 sinnvoll (also möglichst günstig) erweitern können?
    Vielen Dank und viele Grüße,
    Thomas

    1. Hallo Thomas,

      das wäre sozusagen eine „Zeitschaltuhr für kleine Spannungen“. Ist mir nicht bekannt, was nicht heißt, dass es so etwas nicht irgendwo gibt.

      Aber es ist ja kein Problem, den Interrupt des DS3231 mit dem Mikrocontroller auszuwerten und dann die Last zu schalten. Zum Schalten brauchst du lediglich einen MOSFET und evtl. noch einen Widerstand:

      https://wolles-elektronikkiste.de/der-mosfet-als-schalter

      Der DS3231 verbraucht kaum Strom. Den Mikrocontroller kannst du in den Schlaf schicken und mit dem Interrupt wecken und der MOSFET arbeitet auch (fast) stromlos.

      VG, Wolfgang

    1. Hallo Klaus, erst einmal vielen Dank für den Hinweis. Das Problem scheint mit der neuen Arduino IDE zusammenzuhängen. Ich habe die RTCLib 2.1.1 und die ESP Board Version 1.0.6. Als Board habe ich das „ESP32 Dev Module“ gewählt. Den Beispielsketch ds3231.ino aus der Bibliothek kann ich mit der Arduino IDE 1.8.19 problemlos kompilieren und hochladen. Warum auch immer! Denn mit der Arduino IDE 2.0.3 gibt mehrere Fehlermeldungen. Ich würde allen, die Probleme haben, dazu raten, auch die alte Arduino IDE zu installieren.
      Die neue Arduino IDE Version sieht zwar schick aus, hat aber noch die eine oder andere Macke.
      VG, Wolfgang

      1. Hallo Wolfgang,
        in der IDE Umgebung verwende ich ebenfalls ein „ESP32 Dev Module“ und habe mit 2 verschiedenen Versionen (IDE 1.8.19 / IDE 2.0.0-rc3) gestestet und bekomme jeweils den o.g. Fehler. Wenn ich nur das Problem habe, schein es an meiner Umgebung zu liegen?
        Da ich eine schnelle Lösung brauche, habe eine andere Lib verwendet und die Fehlersuche eingestellt.

        PS:
        Danke für den Hinweis: „Achtung bei Batterie-/Akkubetrieb“.
        Ich habe natürlich eine CR3032 geliefert gekommen.

        Deine Empfehlung kann ich nur fett Unterstreichen!
        Was haben sich die Entwickler dabei gedacht. Einen Lithium-Ionen-Akku mit bis zu 4,75V zu laden, das ist Brandgefährlich!

        VG Klaus

        1. Oh ha, also noch komplexer…. Sehr mysteriös. Schwer zu sagen wer dann letzten Endes der „Schuldige“ ist.

  8. Hallo
    Ich konnte die Zeit am RTC Modul einfach Einstellen und der Arduino Uno liesst diese auch gut aus. Ich möchte jetzt aber, das er um 18:00 eine Led anmacht und am 18:30 wieder aus. Um 19:00 ein Servo auf 20 Grad stellen u.s.w.
    Jet ist die Frage, da ich mich noch nicht so gut auskenne, ob es dafür auch ein Command gibt. Vielleicht wie:
    If RTC (18.00)
    digitalWrite (13, HIGH);
    Können sie mir dabei helfen ?

    Lg Simon

    1. Hallo,
      ich sage mal „du“. Die komfortabelste Lösung einen Alarm einzustellen, ist über einen Alarm. Zwei Alarme kannst du programmieren, Alarm 1 und 2. Bleiben wir bei deinem Beispiel mit der LED, die um 18:00 Uhr an- und um 18:30 ausgeschaltet werden soll. Dann würde ich den Sketch ds3231_2_alarms.ino als Grundlage nehmen. Wenn heute der 10.01.2023 wäre und die Uhrzeit wäre vor 18:00 Uhr, dann würde ich erst einmal die Alarmzeiten definieren:
      DateTime ledOnTime (2023, 1, 10, 18, 0, 0);
      DateTime ledOffTime (2023, 1, 10, 18, 30,0);
      Mit
      rtc.setAlarm1(ledOnTime, DS3231_A1_Second);
      und
      rtc.setAlarm2(ledOffTime, DS3231_A1_Minute);
      stellst du die Alarme scharf.
      Mit rtc.alarmFired(1 bzw. 2). prüfst du ob die Alarme ausgelöst wurden und definierst dann die Aktionen.

      Dann kannst du die Alarme neu definieren:
      ledOnTime += TimeSpan(0,24,0,0);
      ledOffTime += TimeSpan(0,24,0,0);
      und dann neu stellen.

      Wenn mit zwei Alarmzeiten nicht auskommst, weil du z.B. noch einen Servo um 19 Uhr steuern willst, dann müsstest du einfach noch weitere Alarme definieren, z.B.:
      DateTime servoOnTime (2023, 1, 10, 19, 0, 0);
      Du arbeitest die Alarme hintereinander ab, und stellst Alarm 1 und 2 immer wieder neu.

      Wenn du ganz viele Dinge zu steuern hast und sich z.B. Ein- und Ausschaltzeiten überschneiden, dann wird es etwas unhandlich. Dann würde ich immer wieder die Zeit abfragen:
      DateTime now = rtc.now();
      und dann z.B. prüfen:
      if ( (now.hour() == 18) && (now.minute() == 30){ Aktion }

      Hoffe, das bringt dich auf den richtigen Weg.
      VG, Wolfgang

  9. Hallo,
    ich bin ein begeisterter Leser deines Blogs.
    Vielen Dank für die viele Arbeit.

    Nun habe ich eine Frage zu den Sketchen zur RTC:
    in manchen Sketchen fehlt das #include Wire.h
    in „Das Datum / die Uhrzeit komfortabel formatieren“ ist es drin
    in „Einen Alarm an einem Datum / Uhrzeit“ ist es gar auskommentiert

    Ich habe noch keine Stelle gefunden, wo ich die Pins für SDA und SCL ändern kann

    kannst du mir da auf die Sprünge helfen?
    Vielen Dank

    1. Hallo Günther,

      gut beobachtet, dass mit dem #include Wire. Es ist eigentlich überflüssig, da sich dieselbe Anweisung in den Bibliotheksdateien der RTCLib findet. Wire.h wird also automatisch mit eingebunden. Anderseits stört die doppelte Anweisung auch nicht, da Wire.h gegen doppeltes Einbinden geschützt ist. Hätte ich konsistent halten sollen.

      Die Pins für SDA und SCL lassen sich auf Boards wie dem Arduino UNO oder Nano nicht ändern. Die Funktion ist dort nicht änderbar an die Pins A4 und A5 geknüpft. Andere Boards, wie z.B. der ESP32, lassen eine Änderung auf andere Pins zu. Es kommt also ganz darauf an, was du verwendest.

      VG, Wolfgang

      1. Hallo Wolfgang,
        Danke für die rasche Antwort

        in der Tat möchte ich einen ESP nutzen.
        Ich habe mittlerweile einfach Wire.h nochmals eingebunden und danach
        Wire.begin ( SCA_PIN, SCL_PIN) genutzt
        funktioniert alles wie es soll.
        Vielen Dank
        VG Günter

  10. Hallo Wolfgang,

    danke erstmal für deine klasse Webseite.

    Ich habe da zwei Probleme, ich habe mir ein DS3231 RTC Modul zugelegt und an einen Arduino angeschlossen.
    Die Ausgabe erfolgt auf einem Seriellen Monitor, und es funktioniert soweit auch alles.
    Auf dem Arduino Board gibt es neben dem Reset-Taster 2 Anschlüsse (SDA,SCL).
    Laut der Doku von AZ-Delivery werden nur die Ports (A4 und A5) genutzt.
    Warum? Das DS3231 Modul hat auch 2 Anschlüsse (SDA und SCL). Warum werden die nicht für die
    Datenübertragung genutzt?

    Noch was anderes ich würde gerne die Ausgabe des DS3231 Moduls auf ein OLED – Display
    anzeigen lassen. Als Display habe ich mir eins von AZ-Delivery rausgesucht.
    https://www.az-delivery.de/products/0-96zolldisplay, allerdings weiss ich auch hier nicht genau welche
    Anschlüsse ich am Arduino UNO nutzen kann. Und laut der Doku von AZ-Delivery wird auch das OLED-Display
    an die Analogen Pins(A4 und A5) angeschlossen und nicht wie angenommen an SDA und SCL.
    Wenn ich das DS3231 und das Display anschließe, kann ich dann die Pins Parallel nutzen?

    Danke und Grüße Christian

    1. Hallo Christian,

      es ist egal ob du SDA / SCL oder A4 / A5 für die I2C Übertragung nimmst. Die Anschlüsse SDA und A4 bzw. SCL und A5 sind auf dem Board direkt bzw. sie führen zu denselben Pins des ATmega328P, als dem zugrundeliegenden Mikrocontroller. Du kannst dir also aussuchen, welche Pins du nutzen willst. Du kannst auch die einen für den DS3231 und die anderen für das Display nutzen – es ist ein- und derselbe I2C Bus. Warum man die Pins doppelt ausgeführt hat, das kann ich die nicht sagen.

      VG, Wolfgang

      1. Danke Wolfgang,
        das waren die Infos die ich brauchte.
        Mach weiter so, und

        Grüße Christian

  11. Hallo,
    ich versuche, Auffälligkeiten zu klären/analysieren.
    Leider bin ich bei der Recherche nicht auf wirkliche Lösungen gekommen.

    Meine RTC scheint sporadisch einen Reset zu machen, also stellt sich wieder auf 01.01.2000.
    Wenn ich die Uhr dann stelle, läuft erstmal wieder alles.
    Es gibt da keinen festen Zyklus, wann das passiert, oder ähnliches.

    Hat das sonst noch jemand beobachtet?

    Gruss,
    Alex

    1. Hi Alex, läuft dein DS3231 auf Batterie? Oder nutzt du eine andere Stromquelle, die vielleicht mal einen Aussetzer hat? Bei Batteriebetrieb sollte eigentlich nichts passieren, solange diese genug Saft hat und alle Kontakte i.O. sind. Zumindest habe ich bisher nicht von Problemen dieser Art gehört. VG, Wolfgang

      1. Hallo,

        die RTC läuft mit Batterie.
        Das Problem tritt nur sporadisch auf.

        Da meine Suche nichts ergeben hat, dachte ich, ich frage mal rum.
        Dabei bin ich dann auf Deine Page geraten.

        Die RTC kommt in einem mobilen Aufbau zum Einsatz.
        Dann ist es vielleicht doch was Mechanisches, durch Transport und/oder Erschütterungen.
        Die Batterie sitzt eigentlich sicher, aber man weiss ja nie.

        Ich habe z.Z. einen Dauertest laufen.
        (Zeit ist eingestellt, bisher stabil)
        Ich schaue mir das in ein paar Tagen wieder genauer an.

        Die Schaltung wurde durch Profis designed und aufgebaut.

        Danke für das schnelle Feedback,
        Alex

  12. Vielen Dank für die klasse Beschreibung, hat mir sehr weitergeholfen.
    Ich habe die DS3231 an einem ESP8266 über 3.3 Volt angeschlossen.
    An den Kontakten für die CR2032 messe ich weniger als 3.3 Volt.
    Wie schätzt Du die Gefahr mit der Ladefunktion ein?
    Eine LIR2032 zu nutzen macht ja wohl bei der Spannung dann überhaupt keinen Sinn.

    1. Ich weiß wirklich nicht, wie groß die Gefahr ist, die Ladfunktion zu nutzen. Ich klemme sie grundsätzlich ab und nutze Batterien. Im Allgemeinen bin ich ein Freund von Akkus, aber eine konventionelle Batterie versorgt den DS3231 für Jahre.

  13. Hallo Wolfgang,
    ich sage danke für den super Beitrag.
    Eine Frage noch:
    Beim Stellen der Uhrzeit über
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); übernimmt ganz bequem die Systemzeit eures Computers.
    habe ich wegen der Übersetzungsdauer eine Zeitdifferenz von 3 bis 4 Minuten. Die hätte ich gern korrigiert.
    Doch, mit meiner Idee
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)+300));
    funktioniert die Übersetzung nicht.
    Hast du evtl. noch eine Idee?

    VG Johannes

    1. Hallo Johannes,
      3-4 Minuten Übersetzungsdauer? Ganz schön krass!
      Zwei Möglichkeiten fallen mir ein:
      1) Du schreibst ins Setup:
      DateTime now = rtc.now();
      rtc.adjust(now + 300);
      Die Verzögerungszeit sollte ja immer dieselbe sein, deswegen könnte man das recht exakt hinbekommen. Auch wenn das pragmatisch ist, ist es doch ziemlich unbefriedigend, finde ich.
      2) Du schreibst einen Sketch, der es dir erlaubt, die Uhr des DS3231 über den seriellen Monitor zu stellen. Dazu lässt du erst einmal die Uhr über rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); stellen. Und dann gibst du den Korrekturwert in Sekunden über den seriellen Monitor ein. Auf dem seriellen Monitor wird die korrigierte Zeit ausgegeben. So könnte dein loop() aussehen:

      static int corrSecs = 0;
      if(Serial.available()){
       corrSecs = Serial.parseInt();
       DateTime now = rtc.now();
       rtc.adjust(now + corrSecs);
       now = rtc.now();
       char buf1[] = „hh:mm:ss“;
       Serial.println(now.toString(buf1));
       while(Serial.available()){
        Serial.read();
       }
      }

      Ich habe das gerade ausprobiert und es geht. Als ersten Wert gibst du 0 ein.
      Sofern der DS3231 am Strom bzw. am Akku bleibt, vergisst er die Zeit ja nicht mehr.

      VG, Wolfgang

      1. Hallo Wolfgang,
        danke für deine schnelle Antwort.
        Beide Varianten werden mir weiterhelfen, ich danke dir.
        Ich fand es auch komisch, diese große Zeitdiff. obwohl die Übersetzungsdauer gefühlt nur 1-2 Min. sind.
        Deshalb mein Korrektur-Gedanke.
        VG Johannes

        1. Hi Johannes,

          ich war mit meinen vorgeschlagenen Lösungen immer noch nicht ganz zufrieden und habe den Beitrag um einen Sketch zum Stellen der Zeit über den seriellen Monitor ergänzt.

          VG, Wolfgang

          1. Hallo Wolfgang,
            prima, dass der Beitrag wieder online ist. Ich habe etliches daraus entnehmen können.
            Noch zur Aufklärung:
            rtc.adjust(now + 300); ist nicht erforderlich! Weiß nicht was da hing.
            rtc.adjust(now + 50); ist OK..
            —–
            Jezt habe ich folgenden Stand:
            1. RTC soll nach „SPANNUNGSLOS“, neu über das Makro
            rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); gestellt werden, funktioniert OK.

            2. RTC soll mit/nach JEDER Kompilierung neu gestellt werden, funktioniert noch nicht! ++++++++++++

            3. RTC bleibt nach Arduino-Reset unverändert, also wird nicht neu gestellt. funktioniert OK.

            Ob es zu 2. eine Lösung geben wird? 😉

            1. Hallo, es gab ein paar Probleme mit einem WordPress Update. Ich hoffe, jetzt funktioniert die Seite wieder dauerhaft.

              Wenn die Anweisung rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); im Sketch steht sollte das bei jedem Kompilieren funktionieren. Bei mir tut es das auch. Ich habe keine Erklärung falls das bei dir anders ist.

              Ich würde die Zeit nur einmal stellen, z.B. mit meinem neu hinzugefügten Sketch, und dann mit Akku dauerhaft erhalten.

              VG, Wolfgang

              1. Richtig:
                Wenn die Anweisung rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); im Sketch steht sollte das bei jedem Kompilieren funktionieren.
                Dann nimmt er aber bei Reset auch die Compilerzeit.
                Deshalb habe ich das so gelöst.
                // ———————————-RTC real time clock —————————
                while (!Serial); // for Leonardo/Micro/Zero
                if (! rtc.begin()) {
                Serial.println(„Couldn’t find RTC“);
                while (1);
                }
                if (! rtc.isrunning())
                { // dann Uhrzeit neu stellen, z.B. nach Batt.wechsel,
                Serial.println(„RTC is NOT running!, Strom war weg „);
                rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // aus dem Compiler Makro
                DateTime now = rtc.now(); // Anzeige Time
                rtc.adjust(now + 15); // 15 Sek Vorlauf, Korrektur
                Serial.println(„RTC adjusted! mit Korrektur“);
                }
                else Serial.println(“ RTC NICHT neu aus dem Compiler geladen „);
                char buf1[] = „hh:mm:ss“; DateTime now = rtc.now(); // laden zur Anzeige
                Serial.print(„Uhrzeit aus RTC: „); Serial.println(now.toString(buf1)); // Ausgabe der Uhrzeit hh:mm:ss, top
                // ————————-End——RTC real time clock —————————

  14. Hallo,

    erst einmal vielen Dank für diese gelungene Einführung.
    Nur an einer Stelle ist Dir leider ein kleiner Fehler herein gerutscht. Mit dem Befehl rtc.disable32K() wird nicht der Oszillator, sondern lediglich der Ausgang an Pin 1 des ICs abgeschaltet. Der DS3231 verfügt ja nur über einen einzigen Taktgeber. Und wenn man den abschaltet, dann steht die Uhr. Den 32 kHz – Ausgang abzuschalten wird zwar bei Weitem nicht soviel Energie sparen, wie im Falle des Oszillators, aber Kleinvieh macht ja bekanntlich auch Mist. Insofern ist es sicherlich sinnvoll.
    Für das EEPROM verwende ich übrigens die AT24Cxx – Library. Damit lässt sich alles sehr einfach bewerkstelligen.
    Ich finde es super, dass Du diesen hoch informativen Blog auf die Beine gestellt hast. Hier bin ich schon öfter fündig geworden.

    Viele Grüße
    Ralf

  15. Hallo Wolfgang,
    eine Frage zu diesen Uhren-Modulen habe ich noch:
    Geben sie auch die Umschaltung von Sommer- auf Winterzeit und umgekehrt bekannt?
    Also Stunde vor bzw. zurück?
    Weißt Du das?
    Gruß, Friedrich

    1. Hallo Friedrich,

      um die Umstellung Sommer/Winter musst du dich selbst kümmern. Das Modul weiß nicht in welchem Land mit welcher Sommer-/Winterzeitverordnung du lebst.

      VG, Wolfgang

  16. Hallo Wolfgang,
    der DS3231 hat mir Probleme gemacht. Da bin ich auf das RTC DS1307 Modul umgestiegen.
    Hast Du nähere Informationen zu diesem Modul?
    Nirgends habe ich die I2C Adresse gefunden. Mit einem Testprogramm zum Feststellen der I2C-Adressen werden nun von meinen 7 x I2C – Adressen nun die Adressen 0x21 und 0x25 nicht mehr angezeigt. Sie sind wohl doppelt belegt mit dem DS1307 Modul.
    Weißt Du ob sich der Adressbereich von 0x2# auf einen anderen Bereich verschieben lässt?
    Der DS3231 hatte seine Adressen auf 0x68 und 0x57.
    Vielen Dank im voraus für Deine Mühe.
    VG, Friedrich

    1. Hallo Friedrich, die Adresse des DS1307 ist 0x68. Das steht im Datenblatt auf Seite 12:
      https://ecksteinimg.de/Datasheet/DS1307.pdf
      Es scheint nicht vorgesehen zu sein, die Adresse zu ändern. Mit meinem I2C Scanner „sehe“ ich für das Modul 0x68 Adresse und 0x50, wobei 0x50 die Adresse des EEPROMs auf dem Modul ist. Dass der DS1307 eine 0x2x Adresse hätte, ist mir unbekannt. Wenn du mehrere DS1307 auf einem I2C Bus verwendest, funktioniert es nicht. Zwei davon lassen sich getrennt mit einem ESP32 ansprechen, da der zwei I2C Schnittstellen hat. Ansonsten könntest du einen I2C Multiplexer einsetzen:
      https://wolles-elektronikkiste.de/tca9548a-i2c-multiplexer
      VG,Wolfgang

      1. Hallo Wolfgang,
        vielen Dank für Deine schnelle Antwort und habe den eigentlichen Fehler gefunden:
        Beim Entlöten des DS3231 habe ich den Anschluss der +5V und des SDA von den Pads mittels Haarriss getrennt. Deswegen waren auch die beiden Adressen 0x21 und 0x25 nicht mehr sichtbar. Inzwischen sind alle Bausteine ansprechbar und ich kann an das Programmieren des WEMOS D1 mini gehen. Es wird eine Uhr mit LEDs.
        Nochmals vielen Dank für Deine schnelle Hilfe.
        VG, Friedrich

  17. Servus Wolfgang !
    Danke vielmals für deinen super Beitrag über den DS3231. Über die RTClib ist der Zugriff wirklich sehr
    komfortabel, anders als nur mit dem Wire.
    So wie es aussieht, ist der RTC default auf 24h Format eingestellt.
    Ich vermute, dass ich da gar nichts umstellen muss; ich kenne da einen Befehl:
    RTC.setClockMode(false); // set to 24h
    so einen ähnlichen brauche ich also gar nicht ! ??
    Vielen Dank, LG, Karl

    1. Hallo Karl,

      mir war gar nicht bewusst, dass man beim DS3231 zwischen 12- und 24-Stundenformat umstellen kann. Aber es steht tatsächlich im Datenblatt. Bei Benutzung der RTCLib ist allerdings alles auf 24h voreingestellt. Die RTCLib verabeitet Zeiten über DateTime Objekte. Die DateTime Klasse ist Teil der RTCLib. Zumindest beim Querlesen, schienen mir die 24h für die DateTime Objekte als unveränderlich vorgegeben.

      Also ja: Das 24h Stundenformat ist bei der RTCLib voreingestellt und meines Wissens auch gar nicht auf 12h änderbar.
      VG, Wolfgang

  18. Hallo Wolle,
    Dein Blog war sehr interessant. Leider stimmt meine Praxis nicht mit Deiner Theorie überein und ich habe keine Erklärung dafür:
    Das Stellen der RTC-Uhr, was mittels Kompilation erfolgt und ca. 15 Sek. vor der vollen Minute gestartet wird lässt eine sekundengenaue Stellmöglichkeit nicht zu, je nachdem wie lange das Programm ist, aber auf die Sekunde genau brauche ich es sowieso nicht.
    Nach dem Strom abschalten über Nacht fehlen am nächsten Abend zwei Minuten (eine DCF-Uhr dienst als Referenz). Bei einem weiteren Tag ohne Spannungsversorgung – der LIR 2032 sollte geladen sein – wird folgendes ausgegeben: Datum:65.85.165 25:65
    Das Wort „Datum:“ ist von mir.
    Ich habe den LIR2032 herausgenommen und gemessen: 3,7V
    Es ist das zweite Projekt, bei dem ich dieses Verhalten beobachtet habe. RTC und LIR wurden nicht anschließend in meinem aktuellen Projekt verbaut. Der LIR hatte 4,8V. Möglicherweise habe ich die RTC beim Nachlöten im vorherigen Projekt (mit eingesetztem LIR) gelyncht.
    Für mein aktuelles Projekt habe ich extra eine Leiterplatte anfertigen lassen und eine neue RTC mit neuem LIR eingesetzt. Der RTC wird ordnungsgemäß über I2C 0x68 erkannt.
    Kannst Du Dir ein solches Verhalten erklären?
    Vielleicht hat jemand anders ähnliche Erfahrungen gemacht.
    Im voraus vielen Dank für die Hilfe.

    1. Hallo Friedrich,

      du hast recht, die Verzögerung zwischen Kompilierung, Upload und Start lässt einen ein paar Sekunden verlieren. Das lässt sich nicht ganz verhindern. Mit einer „manuellen“ Einstellung, z.B. über den seriellen Monitor, ließe sich das sekundengenau machen.

      Das größere Problem ist die Batterie bzw. der Akku. Anscheinend ist die sehr simplistische Ladeschaltung bestehend aus Diode und Widerstand selbst für Akkus ungeeignet, u.a. weil die Ladespannung zu hoch ist. Interessanterweise hat mich darauf ein anderer Leser vor ein paar Tagen aufmerksam gemacht. Ich war nur noch nicht dazu gekommen, den Beitrag anzupassen, hole das jetzt aber nach. Das Fazit ist: entweder Akku-/Batteriebetrieb oder externe Stromversorgung.

      Ob eine Schädigung des Akkus zu den Sekundenverlusten führt, vermag ich allerdings nicht zu sagen. Nach meiner Erfahrung ist eine solche Abweichung innerhalb so kurzer Zeit jedenfalls nicht normal.

      VG, Wolfgang

      1. Hallo Wolfgang,
        jetzt bin ich etwas verblüfft, weil ich dann nicht verstehe, wie ich ohne Akku arbeiten soll.
        So im Nachhinein wäre es wohl vorteilhaft die Ladeschaltung außer Betrieb zu setzen und nur mit einem CR 2032 zu arbeiten.
        Ohne Akku/Batterie verstehe ich nicht, wie ich die RTC setzen soll? Solange noch der PC über den D1 mini die Spannung liefert kann ich die RTC mit Daten versorgen und Datum, sowie Uhrzeit setzen. Wenn nur noch eine externe Stromversorgung habe – bei mir mittels 7805 realisiert – kann ich Datum und Uhrzeit nicht mehr stellen. Also sehe ich nur einen Betrieb mit einem CR 2032 mit inaktiver Ladeschaltung als einzige vernünftige Lösung.
        Velen Dank für Deine Hilfe.
        Viele Grüße, Friedrich

        1. Hallo Friedrich,

          wenn der DS3231 kurzzeitig mit 5 Volt betrieben wird dann wird das einen darin befindlichen LIR2032 nicht gleich zerstören. Auch ein CR2032 wird dir nicht sofort um die Ohren fliegen, wenn es nur um einen Upload geht. Und du musst ja auch nicht 5 Volt Versorgungsspannung verwenden.
          Aber in der Tat halte ich es für die sicherste Variante die Ladeschaltung zu entfernen. Der DS3231 ist ja sehr genügsam im Stromverbrauch, so dass du mit einer CR2032 Batterie ziemlich lange auskommen wirst.
          VG, Wolfgang

          1. Hallo Wolfgang,
            der DS3231 hat mir Probleme gemacht. Da bin ich auf das RTC DS1307 Modul umgestiegen.
            Hast Du nähere Informationen zu diesem Modul?
            Nirgends habe ich die I2C Adresse gefunden. Mit einem Testprogramm zum Feststellen der I2C-Adressen werden nun von meinen 7 x I2C – Adressen nun die Adressen 0x21 und 0x25 nicht mehr angezeigt. Sie sind wohl doppelt belegt mit dem DS1307 Modul.
            Weißt Du ob sich der Adressbereich von 0x2# auf einen anderen Bereich verschieben lässt?
            Der DS3231 hatte seine Adressen auf 0x68 und 0x57.
            Vielen Dank im voraus für Deine Mühe.
            VG, Friedrich

  19. Hallo,
    ich möchte meinen ESP32 nur einmal am Tag aufwecken und dann wieder schlafen legen.
    Ich habe alles nach der Anleitung programmiert, doch leider wird mein ESP32 nach dem schlafen sofort wieder aufgeweckt.

    rst:0x5 (DEEPSLEEP_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
    configsip: 0, SPIWP:0xee
    clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
    mode:DIO, clock div:1
    load:0x3fff0018,len:4
    load:0x3fff001c,len:1216
    ho 0 tail 12 room 4
    load:0x40078000,len:10944
    load:0x40080400,len:6388
    entry 0x400806b4
    Boot number: 3
    Wakeup caused by external signal using RTC_IO
    Going to sleep now
    This will never be printed
    21:04:26
    ets Jun 8 2016 00:22:57

    rst:0x5 (DEEPSLEEP_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
    configsip: 0, SPIWP:0xee
    clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
    mode:DIO, clock div:1
    load:0x3fff0018,len:4
    load:0x3fff001c,len:1216
    ho 0 tail 12 room 4
    load:0x40078000,len:10944
    load:0x40080400,len:6388
    entry 0x400806b4
    Boot number: 4
    Wakeup caused by external signal using RTC_IO
    Going to sleep now
    This will never be printed
    21:04:30
    ets Jun 8 2016 00:22:57

    Bin ziemlich hilflos.
    Hat jemand eine Idea?
    Würde mich sehr über eine Hilfe freuen

      1. Hallo Wolfgang,
        schön dass Du dich gemeldet hast.
        Habe schon einen Kommentar geschrieben, dass ich den Fehler gefunden habe.
        Ich habe es zu erst mit Sekunden, dann mir Minuten und zum Schluss mit Hour getestet. Es funktioniert.
        Hier der Code:
        /* Example implementation of an alarm using DS3231
        *
        * VCC and GND of RTC should be connected to some power source
        * SDA, SCL of RTC should be connected to SDA, SCL of arduino
        * SQW should be connected to CLOCK_INTERRUPT_PIN
        * CLOCK_INTERRUPT_PIN needs to work with interrupts
        */

        #include
        // #include

        RTC_DS3231 rtc;

        // the pin that is connected to SQW
        #define CLOCK_INTERRUPT_PIN 39

        void setup() {
        Serial.begin(115200);
        // initializing the rtc
        if(!rtc.begin()) {
        Serial.println(„Couldn’t find RTC!“);
        Serial.flush();
        abort();
        }

        // if(rtc.lostPower()) {
        // // this will adjust to the date and time at compilation
        // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
        // }

        //we don’t need the 32K Pin, so disable it
        rtc.disable32K();

        // Making it so, that the alarm will trigger an interrupt
        pinMode(CLOCK_INTERRUPT_PIN, INPUT_PULLUP);
        attachInterrupt(digitalPinToInterrupt(CLOCK_INTERRUPT_PIN), onAlarm, FALLING);

        esp_sleep_enable_ext0_wakeup(GPIO_NUM_39,0); //1 = High, 0 = Low

        // 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);

        // stop oscillating signals at SQW Pin
        // otherwise setAlarm1 will fail
        rtc.writeSqwPinMode(DS3231_OFF);

        // turn off alarm 2 (in case it isn’t off already)
        // again, this isn’t done at reboot, so a previously set alarm could easily go overlooked
        rtc.disableAlarm(2);

        // schedule an alarm 10 seconds in the future
        // if(!rtc.setAlarm1(
        // rtc.now() + TimeSpan(10),
        // DS3231_A1_Second // this mode triggers the alarm when the seconds match. See Doxygen for other options
        // )) {
        // Serial.println(„Error, alarm wasn’t set!“);
        // }else {
        // Serial.println(„Alarm will happen in 10 seconds!“);
        // }
        DateTime alarmTime1(2022, 1, 6, 23, 34, 00); // Kümmere dich nicht um das Jahr, Monat, Tag -> 18:30 Uhr
        rtc.setAlarm1(alarmTime1, DS3231_A1_Hour); // Es sollten eigentlich (musst du mal probieren) nur die Stunden/Minuten/Sekunden verglichen werden.
        //
        delay(500);
        Serial.println(„Gehe jetzt schlafen!“);
        esp_deep_sleep_start();
        //

        }

        void loop() {
        // print current time
        char date[10] = „hh:mm:ss“;
        rtc.now().toString(date);
        Serial.println(date);
        // resetting SQW and alarm 1 flag
        // using setAlarm1, the next alarm could now be configurated
        if(rtc.alarmFired(1)) {
        rtc.clearAlarm(1);
        Serial.println(„Alarm cleared“);
        Serial.println(„Gehe in 2 Sekunden schlafen!“);
        delay(2000);
        esp_deep_sleep_start();
        }

        delay(500);
        }

        void onAlarm() {
        Serial.println(„Alarm occured!“);
        }

        Die Funktion habe ich in diesen Sketch ausprobiert.
        Nun kann ich diese Funktion in mein eigentliches Programm einer Bienenwaage implementieren.
        Vielen Dank
        LG
        Josef

    1. Habe das Problem gelöst.
      esp_sleep_enable_ext0_wakeup(GPIO_NUM_39,0); //1 = High, 0 = Low
      Als ich den Parameter auf Low gestellt habe, hat es funktioniert. Die Reboot Dauerschleife war mit der vorgegebenen und vorgeschlagen Parameter High.
      Für alle, die vielleicht die gleiche Problematik haben werden.
      Gruß
      Josef

  20. Guten Morgen

    Auf der Suche nach der RTClib hab ich hier her gefunden, sehr gut erklärt.
    Nun hab ich mithilfe der sevseg Bibliothek die Uhrzeit (hhmmss) auf 6 Segmenten anzeigen können.

    Beim Versuch der Zuweisung -> Display.setNumber(now.toString(buf1)); hätte ich die Anzeige des Datums „DDMMYY“ anstelle einer krummen Zahl erwartet. Habe ich da Denkfehler?

    MfG Ronny

    ———————————————————————————————–

    #include „SevSeg.h“
    #include
    #include
    SevSeg Display;
    RTC_DS1307 rtc;

    unsigned long number = 0;

    void setup () {

    byte numDigits = 6;
    byte digitPins[] = {5, 4, 3, 2, 0, 1};
    byte segmentPins[] = {6, 7, 8, 9, 10, 11, 12, 13};
    bool resistorsOnSegments = true;
    byte hardwareConfig = COMMON_CATHODE;
    bool updateWithDelays = false;
    bool leadingZeros = true;
    bool disableDecPoint = false;

    Display.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments, updateWithDelays, leadingZeros, disableDecPoint);
    Display.setBrightness(100);

    rtc.begin();

    }

    void loop() {

    DateTime now = rtc.now();

    //buffer can be defined using following combinations:
    //hh – the hour with a leading zero (00 to 23)
    //mm – the minute with a leading zero (00 to 59)
    //ss – the whole second with a leading zero where applicable (00 to 59)
    //YYYY – the year as four digit number
    //YY – the year as two digit number (00-99)
    //MM – the month as number with a leading zero (01-12)
    //MMM – the abbreviated English month name (‚Jan‘ to ‚Dec‘)
    //DD – the day as number with a leading zero (01 to 31)
    //DDD – the abbreviated English day name (‚Mon‘ to ‚Sun‘)

    char buf1[] = „DDMMYY“;
    Serial.println(now.toString(buf1));
    char buf2[] = „YYMMDD-hh:mm:ss“;
    Serial.println(now.toString(buf2));
    char buf3[] = „Today is DDD, MMM DD YYYY“;
    Serial.println(now.toString(buf3));
    char buf4[] = „DD.MM.YYYY“;
    Serial.println(now.toString(buf4));
    char buf5[] = „MM/DD/YYYY“;
    Serial.println(now.toString(buf5));
    Serial.println();

    number = (now.hour() * 100 + now.minute()) * 100L + now.second();

    Display.setNumber(number);
    Display.refreshDisplay();

    }

    1. Hi,

      es gibt mehrere SevSeg Bibliotheken auf GitHub. Benutzt du die von DeanIsMe? Falls ja, die Funktion setNumber in dieser Bibliothek erwartet eine Zahl, genau genommen ein int32_t (also ein „long“). now.toString(buf1) ist aber ein String. Dein Sketch passt aber nicht zu deiner Frage, denn dort schreibst du:
      Display.setNumber(number); mit number als long, was ja richtig ist. Also was liefert die krumme Zahl:

      setNumber(number); oder setNumber(now.toString(buf1))?

      VG, Wolfgang

      1. Wow, Danke für die schnelle Reaktion 🙂

        Ja die Sevseg ist von DeanIsMe. Also anfänglich funktionierte mit
        die Ausgabe der Uhrzeit auf 4 Stellen. Mit Erweiterung auf 6 Stellen musste das „int“ zu „long“
        werden was dann klappte. Mit wird die Zeit korrekt dargestellt. Jetzt war die
        Idee, durch zu ersetzen, in der Hoffnung
        eine Anzeige zu erreichen (im serial.print klappt das und wird so dargestellt DDMMYY).

        Das Ziel ist eine abwechselnde Anzeige der Uhrzeit und des Datums. Vermutlich fehlt mir noch das Verständniss zwischen den vielen Formaten.

        MfG Ronny

        1. Sorry, ich hatte Zeilen in spitzen Klammern gesetzt, die verschwunden sind. Also nochmal neu

          Ja die Sevseg ist von DeanIsMe. Also anfänglich funktionierte mit setNumber(number);
          die Ausgabe der Uhrzeit auf 4 Stellen. Mit Erweiterung auf 6 Stellen musste das „int“ zu „long“
          werden was dann klappte. Mit number = (Hour * 100 + t.min) * 100L + t.sec; wird die Zeit korrekt dargestellt. Jetzt war die Idee, setNumber(number); durch setNumber(now.toString(buf1));
          zu ersetzen, in der Hoffnung eine Anzeige zu erreichen (im serial.print klappt das und wird so dargestellt DDMMYY).

          Das Ziel ist eine abwechselnde Anzeige der Uhrzeit und des Datums. Vermutlich fehlt mir noch das Verständniss zwischen den vielen Formaten.

          MfG Ronny

          1. Hi Ronny,
            ja die Datenformate können Stolperfallen sein. Auch mir passieren da noch manches mal Fehler, z.B. dass eine Zahl zu groß wird und man von integer zu long wechseln muss. Das weiß ich zwar, aber manchmal vergesse ich das. Wenn setNumber eine Zahl erwartet, musst du der Funktion eine Zahl bieten. Und mit der Uhrzeit hast du es ja schon richtig gemacht. Bei dem Datum machst du es einfach entsprechend. Der Tag wird dir mit now.day() als Zahl geliefert, der Monat mit now.month(), das Jahr mit now.year(). Beim Jahr musst du allerdings 2000 abziehen oder du schreibst now.year() % 2000 (also Rest beim Teilen durch 2000), da du ja 21 und nicht 2021 schreiben willst. Und dann setzt du die drei Zhalen so zusammen wie du es schon gemacht hast. Also alles gut, du bist schon auf dem richtigen Weg!
            Viel Spaß mit der Uhr und viele Grüße, Wolfgang

  21. Leider wird beim Compilieren trotz Einbindens der Adafruit_Bus-IO-Bibliothek immer der gleiche Fehler angezeigt:

    ^~~

    getc

    exit status 1

    a function-definition is not allowed here before ‚{‚ token

    1. Die Fehlermeldung ist mir noch nicht über den Weg gelaufen. Folgende Angaben wären hilfreich:

      Welcher Sketch? Sketch unverändert? Welches Board? Welches STC Modul?

      Und ist das die gesamte Fehlermeldung? Bitte mal in der Arduino IDE in Datei – Voreinstellungen, einen Haken bei „Ausführliche Ausgabe während: Kompilierung“ und „Hochladen“ setzen. Vielleicht sagt mir die Fehlermeldung dann mehr.

      VG, Wolfgang

  22. Hallo Wolfgang,

    zuerst einmal ein frohes, neues nachträglich an alle hier. Ich hoffe, Ihr seid alle gesund ins neue Jahr gekommen.

    Ich hätte mal eine Frage zur DS-3231, bzw. dessen Temperatursensor.
    Die letzten Tage kam es mir bei meinem Testaufbau meiner Funkuhr so vor, als wäre die gemessene Temperatur des DS-3231 Moduls viel zu hoch.
    Gefühlt schätzte ich anfänglich die Raumtemperatur auf vielleicht 22°C, das Modul meinte jedoch es wäre 26-27°C?!

    Nun habe ich ein gekauftes digitales Schätzeisen daneben gestellt, das hoffentlich etwas genauer sein dürfte, und das zeigt mir tatsächlich 25.0°Can, während das Vergleichsthermometer 22°C misst.

    Kann mir hier jemand sagen, ob man das DS-3231 ggf. kalibrieren kann? Ich habe bislang in keiner Dokumentation etwas dazu gefunden.
    Vielleicht gibt es hier ja jemanden, der ähnliches berichten kann und der vielleicht eine Lösung dafür hat.

    Danke im Voraus,

    Andreas.

    1. Hi Andreas, leider gibt es keine Kalibrierfunktion als Bestandteil des DS3231. Und das Datenblatt gibt als Fehlergrenze tatsächlich +/-3 °C an. Wenn Kalibrierung, dann nur über deinen Sketch. Du könntest mal ein Thermometer deines Vertrauens nehmen und Vergleichsmessungen bei verschiedenen Temperaturen machen. Im besten Fall gibt es einen festen Offset, den du einfach abziehen musst. Vielleicht musst du auch eine Ausgleichsgerade verwenden. Wenn es ganz doof läuft, dann sind die Werte nicht reproduzierbar. Das musst du einfach mal ausprobieren.
      VG, Wolfgang

      1. Hallo Wolfgang,

        das habe ich jetzt auch gemacht. Bei einer Außentemperatur von 10°C ist die Abweichung in etwa gleich groß, wie im Zimmer, wenn ich das Modul auf eine Heizung lege. Es sind auf ca. 0.2-0.3°C genau/ungenau, womit ich aber locker leben kann.
        Daher habe ich einen festen Offset eingebaut, der bei 2.5°C liegt.
        Aber danke für den raschen Hinweis, so konnte mir weiteres googlen ersparen und das ganze verkürzen.

        Ab jetzt wird es (für mich) etwas schwieriger, denn ich versuche ein Menüsystem in den 32kB eines NANO unterzubringen… Bin schon sehr gespannt…

        Mit DCF77, RTC, OLED Display (AdafroutGFX) und Ton (Piezosummer) habe ich knapp 80% Speicher voll.
        Ein interessantes Experiment beginnt 😉

        Gruß,

        Andreas.

  23. Hallo Wolfgang,

    danke für diesen ausführlichen und informativen Beitrag! Ich renne leider immer wieder in das selbe Problem. Es betrifft die DS1307 und die DS3231. Die Uhr rechnet die Zeit einfach nicht weiter. Ich nutze die DS3231 um Messwerte auf einer Speicherkarte mit einem Zeitstempel zu versehen.

    setup() {

    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

    (…)

    loop() {

    (…)

    DateTime now = rtc.now();

    (…)

    Wenn ich das Programm auf den Arduino hochlade, stellt sich die Uhr entspreched dem Befehl in der Setup ein, die korrekte Zeit wird im Seriellen Monitor angezeigt, z.B. 31.12.2021 14:00:32
    Wenn ich nun das USB-Kabel trenne (den seriellen Monitor lasse ich offen) und 5 min warte, dann erneut das USB-Kabel verbinde, startet die Uhr wieder bei 31.12.2021 14:00:32 ! Wo liegt mein Fehler?

    Achja, guten Rutsch ins neue Jahr 2022!

    1. Hallo Max, ich gehe mal davon aus, dass dein Arduino seinen Strom über den USB-Anschluss bekommt, d.h. das heißt dein Programm startet neu.
      Die Anweisung: rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
      stellt die Zeit zum Zeitpunkt des Kompilierens. Bei einem Neustart gehst du wieder auf diesen Zeitpunkt zurück. Du müsstest also das Programm einmal auf den Arduino laden, dann die Zeile rtc.adjust(….usw herausnehmen und dann noch einmal hochladen. Dann kannst du den Sketch neu starten ohne dass der DS3231 oder DS1307 neu gestellt wird.
      VG und guten Rutsch, Wolfgang

      1. Hallo Wolfgang,

        da wäre ich nicht von alleine drauf gekommen! Vielen, vielen Dank für die schnelle Hilfe!

        Viele Grüße

        Max

  24. Hallo Wolfgang,

    ich baue auch gerade eine Funkuhr mit einem Arduino NANO, wobei ich aber gleichzeitig auch noch RGB Leds (WS2812B) ansteuere. Das Thema Alarm versuche ich ohne die integrierten Alarme zu lösen und gehe daher wie folgt vor (noch Theorie):
    Ich möchte 3 einstellbare Alarme haben. Die Zeiten (HH:MM und an welchem Wochentag sie auslösen sollen, lege ich im EEPROM des NANO ab.

    Jede Sekunde wird dann getestet, ob eine der 3 Zeiten erreicht ist und ist dies der Fall, wird ein Piezo Piepser gesteuert. Die Alarme sind also max. 1 Minute lang hörbar, was für mich ok ist.
    Um den Alarm vorzeitig abzuschalten, verwende ich Taster, die auch zur Menüsteuerung benutzt werden (Klick >> playalarm = false; oder so ähnlich…)

    Es wird noch eine Weile dauern, aber wenn es Dir lieb ist, könnte ich dieses Bauprojekt später hier verlinken. Vielleicht können dieses dann noch andere als Beispiel verwenden.

    Auf alle Fälle hast Du klar verständlich beschreiben, wie man die Uhr benutzt und auch stellt, denn das fehlt scheinbar in den Beispielscripten. Daher an dieser Stelle nochmals DANKE für Deine äußerst ausführlichen Beschreibungen und Beispiele.

    Liebe Grüße, schöne Weihnachten und allen einen guten Rutsch,

    Andreas.

    1. Hi Andreas, ja, wenn das Projekt fertig ist, dann integeriere ich gerne einen Link. Vielen Dank!

      Dir auch ein schönes Weihnachtsfest und einen guten Rutsch in ein hoffentlich gesundes Jahr 2022.

      Wolfgang

  25. Auch von meiner Seite Anerkennung und Lob für deine Seite. Konnte mit deinen Infos mein RTC-Modul innerhalb von wenigen Minuten zum Leben erwecken.

    Ein kleiner Hinweis: Die RTC war das erste I2C-Device, das ich an den Arduino angeschlossen habe. Deshalb musste ich neben der RTCLib auch noch die I2C-Lib (von Adafruit) manuell installieren. Die Abhängigkeit ist in der RTCLib-Beschreibung erwähnt, sie wird aber nicht automatisch aufgelöst. Vielleicht magst du in deiner Installationsanleitung noch einen Passus ergänzen.

    1. Guter Punkt, danke. Fällt einem naturgemäß nicht auf, wenn man die Bibliothek schon installiert hat. Hab’s ergänzt.

  26. Hallo Wolle!

    Zunächst mal: Dein Blog ist spitze! Superb! Unglaublich reichhaltige und fundierte Informationen!

    Wie würdest du mit DS1307 eine Alarmfunktion implementieren? Ich möchte Weihnachtsbeleuchtung morgens und abends zu bestimmten Uhrzeiten an- und abschalten.

    Viele Grüße

    Roman

    1. Hallo Roman,

      vielen Dank für das Lob. Wie man zwei Alarme stellt, zeigt ja der Sketch ds3231_2_alarms.ino. Eigentlich brauchst du vier Alarme, zwei zum An- und zwei zum Abschalten. Eine Möglichkeit ist, die Einschaltzeiten programmieren und das Ausschalten über eine millis() Abfrage zu steuern. Oder du definierst die vier Zeiten: morgens_an, morgens_aus, abends_an, abends_aus. Dann stellst du erst die beiden Alarme rtc.setAlarm1(morgens_an) und rtc.setAlarm(morgens_aus). Wenn du mit morgens_aus durch bist, stellst du (bzw. der Sketch!) die Alarme um auf abends_an und abends_aus. Nach abends_aus stellt der Sketch wieder um. Ich denke, die letztere Methode ist die elegantere.

      Dann wünsche ich eine schöne Vorweihnachtszeit mit stimmungsvoller Beleuchtung!

      VG, Wolfgang

      1. Hallo Wolfgang,

        vielen Dank für deine schnelle Antwort. Das Problem ist, dass beim DS1307 anders als beim DS3231 keine Alarmfunktion implementiert ist, die ich als interrupt nutzen könnte – daher meine Nachfrage.

        Viele Grüße

        Roman

        1. Ooops – den Teil mit dem DS1307 hatte ich überlesen. Das ist aber eigentlich auch kein Problem. Du definierst vier DateTime Objekte, z.B.
          DateTime morgens_an(2030, 12, 24, 7, 30, 0); -> 24.12.2030, 7:30:00
          Jahr, Monat, Tag spielen keine Rolle.

          Dann definierst du noch ein DateTime Objekt für die aktuelle Zeit, so wie in den obigen Sketchen („now“). „now“ musst du regelmäßig aktualisieren.

          Und dann kannst du morgens_an.hour() und morgens_an.minute() mit now.hour() und now.minute() vergleichen. Dann weißt du zum Beispiel: Es ist später als morgens_an. Dann vergleichst du now mit morgens_aus und weißt, ob du dich im Zeitfenster für das Anschalten befindest. Ist über eine Reihe von if…then…else Konstruktionen machbar.

          Anstelle immer erst die Stunden und dann die Minuten zu vergleichen würde ich alles auf Minuten zurückführen, als aktuelle_Minute = 60 * now.hour() + now.minute().

          Viel Erfolg damit! VG, Wolfgang

          1. Danke, danke, danke Wolfgang!

            Und nochmal größtes Lob für deine grandiose Seite!

            Alles Gute für dich!

            Roman

          2. Nachtrag:

            Für Interessierte hier der Code für eine zeitgeschaltete Fernsteuerung für Funksteckdosen.

            Verwendet wurden ein Arduino Uno, ein RTC-Modul mit DS1307 („TinyRTC“), ein 433 MHz-Sendemodul, und 433 MHz-Funksteckdosen.

            /* Code for a remote control time switch using an Arduino Uno, a generic RTC module with DS1307 chip, a generic 433 MHz RF module, and generic 433 MHz remote controlled power sockets.
            Application is switching Christmas lights autonomously in the morning and evening */

            #include „RTClib.h“
            #include <RCSwitch.h>

            RTC_DS1307 rtc;
            RCSwitch mySwitch;

            const byte RF_PIN = 10; //Arduino Uno pin that is connected to 433 MHz-Transmitter module data pin
            const int SERIAL_CLOCK_DELAY = 5000; //delay between loops, mostly to throttle clock output on serial monitor
            bool LIGHT_IS_ON = false; //boolean to store power socket relay state. Could be declared in the loop(), as a static bool in that case.

            void setup() {

            Serial.begin(57600);

            while (!Serial) { //wait for serial connection
            }

            if (!rtc.begin()) { //start over if connection to RTC is not established yet
            Serial.flush();
            abort();
            }

            //uncomment the following section to set the RTC if power was lost
            // if (!rtc.isrunning()) {
            // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
            // }

            mySwitch.enableTransmit(RF_PIN);

            }

            void loop() {

            DateTime now = rtc.now(); //poll time from RTC and store it in the DateTime now object

            //format now-time from the RTC and print it on serial monitor
            Serial.print(now.hour(), DEC);
            Serial.print(‚:‘);
            Serial.print(now.minute(), DEC);
            Serial.print(‚:‘);
            Serial.print(now.second(), DEC);
            Serial.println();

            //YYYY/MM/DD must be provided, but can be chosen randomly as they do not matter for a daily time switch. HH/MM/(SS) are the switch on/off times
            DateTime morgensAn(2030, 12, 24, 6, 30, 0);
            DateTime morgensAus(2030, 12, 24, 7, 30, 0);
            DateTime abendsAn(2030, 12, 24, 16, 30, 0);
            DateTime abendsAus(2030, 12, 24, 22, 45, 0);

            //convert hours and minutes to just minutes. Makes the if-condition more straightforward.
            int aktuelleMinute = 60 * now.hour() + now.minute();
            int morgensAnMinute = 60 * morgensAn.hour() + morgensAn.minute();
            int morgensAusMinute = 60 * morgensAus.hour() + morgensAus.minute();
            int abendsAnMinute = 60 * abendsAn.hour() + abendsAn.minute();
            int abendsAusMinute = 60 * abendsAus.hour() + abendsAus.minute();

            //check if the present minute is within the morning or evening timeslots
            if ( ((aktuelleMinute >= morgensAnMinute) && (aktuelleMinute = abendsAnMinute) && (aktuelleMinute <= abendsAusMinute)) ) {
            //if so, check if the lights are off. This check is not strictly necessary. Without it, the command would be resent redundantly.
            if (!LIGHT_IS_ON) {
            //if the light is off, switch it on. The string values are the DIP switch configurations of the power sockets.
            mySwitch.switchOn("10001", "10000");
            //change lights status bool. In the following loop, the second if condition is not met and the command does not get resent redundantly.
            LIGHT_IS_ON = true;
            }
            }

            //if the present minute is outside the timeslots, switch the lights off. Set lights status bool, see above.
            else if (LIGHT_IS_ON) {
            mySwitch.switchOff("10001", "10000");
            LIGHT_IS_ON = false;
            }

            //delay, mostly to throttle clock output on serial monitor
            delay(SERIAL_CLOCK_DELAY);
            }

            1. Ergänzung: beim zweiten #include fehlt oben die library RCSwitch.h. Wurde aus unerfindlichen Gründen aus dem code gelöscht, vielleicht wegen der erforderlichen Vergleichszeichen vor und nach dem Wort RCSwitch.h

              1. Hallo Roman, erstmal vielen Dank. Das Problem mit dem „größer als“ und „kleiner als“ kenne ich – muss man dann durch < bzw > ersetzen. Nervt! Ich habe es mal eingefügt.

  27. Hallo Wolfgang,
    im Alarmfall, wenn dieser auch aktiviert wurde, welches Signal gibt der SQW-Ausgang aus? wird der nur auf LOW-Pegel dauerhaft gezogen?
    Habe ein Kuriosität mit so einer RTC-Platine.
    Ich habe eine Nebenuhr zum laufen gebracht und gehe auf den DS3231 näher ein.
    https://www.youtube.com/watch?v=tCUvbN9azBw&t=1516s
    Es giebt hier einige Abweichungen, die ich feststellen konnte. Zumindest ist es so, dass die Uhr sich von allein ca. 4 Stunden vor oder 8 Stunden stehen bleibt, aber erst nach ca. 2 Monaten!
    Jetzt habe ich einen neuen DS3231 bestell, aufgelötet und er hat diese Ungenauigkeiten bei den 32k Ausgang und die Schwankungen im Takt nicht!
    Ich könnte mir vorstellen, dass der Chip der Übeltäter ist, muss ich aber erst Monate abwarten…

    Beste Grüße
    Marco

    1. Hallo Marco, wenn der Alarm eingestellt wird liefert SQW ein Dauer-High-Signal. Wird der Alarm dann ausgelöst, geht SQW auf LOW. Der Alarm muss aktiv gelöscht werden, damit SQW wieder auf HIGH geht. Wenn du SQW als Lieferanten für ein Rechtecksignal nutzen möchtest, verträgt sich das nicht mit dem Alarm. Mehr fällt mir zu den Abweichungen nicht ein.
      VG, Wolfgang

  28. Hallo Wolfgang,

    vielen Dank für den Artikel. Ich konnte einiges davon mitnehmen und bin damit mit meinem ersten Arduino Projekt ein paar Schritte weitergekommen. Mir ist dabei aufgefallen, dass es beim Rechnen mit einem Datum vor der Jahrtausendwende zu Problemen kommt. Angenommen ich setze ein Datum:
    DateTime geburtstag (1999, 1, 18, 0, 0, 0);
    dann wird daraus 01.18.2007.

    Ich hab das letzte Mal vor 25 Jahren gelötet und wollte dich deshalb fragen, was man zu beachten hat wenn man die Onboard-LED und den 200 Ohm Widerstand entfernen möchte. Einfach mit der heißen Lötkolbenspitze die Teile wegpulen? Nimmt man da auch 350Grad oder eher weniger?

    Danke!

    Gruß
    Mark

    1. Hallo Mark,

      offen gestanden habe ich den Widerstand mit einem spitzen Metallstück heruntergebrochen. Auslöten klingt aber einfach besser ;-).

      Normal löte ich ich mit 300°C – da gibt es viele Meinungen. Je heißer der Lötkolben, desto schneller können umliegende Teile Schaden nehmen.

      Interessant und schade, dass DateTime nicht mit Daten aus dem letzten Jahrtausend funktioniert. Ist nicht meine Bibliothek, sonst könnte ich vielleicht was daran ändern. Ich schaue mir das aber gelegentlich noch mal an – vielleicht kann man irgendwie „drum herum“ programmieren. Danke für den Hinweis!

      VG, Wolfgang

      1. Ich habe noch mal in der Bibliothek geschaut und tatsächlich sind DateTime Objekte nur für die Jahre 2000 – 2099 definiert. Das lässt sich auch nicht so ohne Weiteres ändern oder umgehen.

  29. Hallo Wolfgang, danke für deine Zeit, deinen guten Artikel über die DS3231 Echtzeituhr. Hat mir sehr bei meinem Projekt „Sonnenaufgangs- und Untergangsimulator“ geholfen. Was schade ist – das Teil hat keinen Flag gibt, mit dem man auslesen könnte, ob es gerade Sommer- oder Normalzeit ist.

    1. Hallo Thomas,
      du kannst aber berechnen, ob gerade Sommerzeit ist. Du schaffst DateTime Objekte:
      summerBegin = DateTime(2021, 3, 28, 2, 0, 0)
      winterBegin = DateTime(2021, 10, 31, 3, 0, 0)
      Und wenn dein Objekt für die aktuelle Zeit now heißt, dann kannst du rechnen:
      if (summerBegin.unixtime() > now.unixtime()) { es ist Winter }
      Dann berücksichtigt man noch den Winterzeitbeginn und muss noch berücksichtigen, dass der Sommer- und Winterzeitbeginn in jedem Jahr unterschiedlich ist. Das lässt sich aber auch machen.
      Nicht komfortabel, aber geht.
      VG, Wolfgang

  30. Hallo Wolfgang,

    deine Tipps hier sind wirklich Gold wert.
    Im Moment spiele ich bei meinem Projekt das Szenario durch, bei dem die Uhr, oder besser der Arduino, evtl. für mehrere Monate stromlos gemacht werden könnte. Meine Idee der Kompensation sieht so aus, dass ich bei erfolgreicher monatlicher Korrektur (zur Erinnerung: +4 Sek,) einen Alarm programmiere. Nicht, um ihn auszulösen, sondern als eine Art kodierten Speicher für den Monat der letzten Korrektur. Wenn ich also den Monat in die Alarmstunde und das Jahr in die Alarmminute eintrage, ließe sich hinterher leicht ermitteln, wie lange die letzte Korrektur her ist. Ich müsste nur bei Reaktivierung des Arduino die Werte des eingestellten Alarms zurück „kodieren“ und mit dem aktuellen Monat vergleichen und so pro vergangenem Monat 4s auf die Unixtime addieren. Theoretisch sind damit weit über 30 Jahre „Pause“ für den Arduino möglich. Das wäre ein simple, zuverlässige Methode.
    Das Problem, dass ich aktuell nicht lösen kann, ist dass ich keinen Abfragebefehl für das Auslesen des aktuell eingestellten Alams finde. Ich habe mir auch schon andere Bibliotheken für die DS3231 angesehen, aber dort gibt es entweder keine Unixzeit oder kein Auslesen der Alarmzeit.
    Meine letzte Rettung wäre das EEPROM aber irgendwie will ich nicht verstehen, dass ich einen eingestellten Alarm nicht wieder ausgeben/prüfen kann.
    Möglicherweise ist alles auch viel einfacher und nur ich bin zu blind oder zu doof?

    1. Hallo mk,
      in der von mir beschriebenen Bibliothek gibt es tatsächlich nur eine setAlarm Funktion und keine getAlarm Funktion. Natürlich ist der Alarm im Prinzip auslesbar. Die Information steht ja in den Registern des DS3132. Aber dafür müsste man die Bibliothek um eine entsprechend Funktion erweitern. Du könntest das als Issue auf GitHub anmelden:
      https://github.com/adafruit/RTClib/issues
      VG, Wolfgang

  31. Hallo Wolfgang,
    zunächst einmal möchte ich dir sagen, dass ich ein kleiner Fan deines Blogs bin. Die Tiefe, mit der du dich hier den Themen widmest sucht seines gleichen im deutschsprachigen Raum.
    Interessant finde ich hier die Feststellung, dass die DS3231 per se einen Akku laden möchte und man deswegen den Widerstand oder die Diode (oder doch beides??) entfernen sollte.
    Nun aber zu meiner Frage:
    Ich plane ein Projekt, bei dem ich dieses Modul so lange wie möglich autark, ohne es wieder stellen zu müssen betreiben möchte. Die auslösenden Ereignisse würden aber durchaus zeitkritisch sein und sollten langfristig nicht mehr als 30 Sekunden daneben liegen. Meine eigenen Erfahrungen mit diesem Modul sind die, dass es ausnahmslos immer nach geht. Kannst du das bestätigen? In diesem Fall könnte man praktisch per Sketch einmal monatlich mit +5 Sekunden justieren und hätte vermutlich erst nach mehreren Jahren eine Differenz von eben diesen 30 Sekunden.

    Was denkst du?

    LG
    mk

    1. Hallo mk, ich habe auch festgestellt, dass die Module tendenziell nachgehen. Wieviel, kann ich gar nicht sagen. Es wäre auf jeden Fall eine pragmatische Lösung, die Abweichung zu bestimmen und in gewissen Abständen auszugleichen. Alternativ könntest du auch einen DCF77 Empfänger integrieren und den DS3231 damit z.B. täglich stellen:
      https://wolles-elektronikkiste.de/dcf77-funkuhr
      Zum Widerstand und der Diode: es reicht eins der Bauteile zu entfernen, da sie in Reihe geschaltet sind. Oder du verwendest halt einen Akku statt Batterie. Oder du setzt die Batterie erst kurz vor Schluss ein, kurz bevor du das Modul vom Strom trennst. VG, Wolfgang

      1. Hallo Wolfgang,
        vielen Dank für die schnelle Antwort.
        Ein DCF77 kommt wegen schwieriger Empfangslage nicht in Frage.
        Ich denke, die Justierung um +4 Sekunden pro Monat wird das Gröbste regeln.
        Über die Batterielaufzeit (nein, kein Akku) muss ich mir anscheinend keine Gedanken machen. Drei Jahre sollte sie durchalten.
        Die LED zu entlöten ist, wie ich finde, die bessere Wahl. Das rote Licht könnte den baldigen Geschenkempfänger irritieren.

        Wenn ich soweit bin werde ich das Projekt vlt auf instructrables.com veröffentlichen. Ich schicke dir dann den Link.

        LG

      2. Hallo Wolfgang,

        mittlerweile bin ich in der Realisierungsphase meines kleinen Projekts und überlege, wie ich das Korrekturproblem am elegantesten im Code löse. Am einfachsten wäre selbstverständlich eine if-Schleife, die bei bestimmten Bedingungen, bsp. jeden Monatsersten um 12:00:00 Uhr drei oder vier Sekunden aufaddiert. Noch einfacher erscheint es mir allerdings, wenn ich direkt die Unix-Zeit alle 2592000 Sekunden (30 Tage) um diese drei oder vier Sekunden vorstellen könnte. Allerdings finde ich hierfür keine Hinweise, dass es überhaupt geht.
        Kennst du einen Trick, mit dem sich die DS3231 per Unix-Zeitcode stellen lässt?

        LG und schon mal vielen Dank fürs Nachdenken

        1. Ich würde den Zeitpunkt der Einstellung über einen Alarm festlegen. Die Einstellung selbst könntest du folgendermaßen realisieren:
          DateTime now = rtc.now();
          now += TimeSpan(5); // für 5 Sekunden
          rtc.adjust(now);

          Oder so:
          DateTime now = rtc.now();
          uint32_t nowUnixTime = now.unixtime();
          nowUnixTime += 5;
          rtc.adjust(DateTime(nowUnixTime));

          rtc.adjust() erwartet ein DateTime Objekt als Argument. Das DateTime Objekt kannst du aber direkt aus der Unix Zeit erzeugen. Die Autoren der Bibliothek haben das recht flexibel gestaltet

          VG, Wolfgang

  32. Hallo Wolfgang,
    Sehr gute Beschreibung danke!
    Ich versuche gerade ein Relais zu einer eingestellten Zeit einzuschalten und so auch wieder auszuschalten. (z.B Aquariumlicht.)
    Ich bringe die Bedingung aber nicht hin, dass dies sauber läuft. Hast du vielleicht zu dem ein Beispiel?

    Liebe Grüsse

    Marin

    1. Hi Marin,
      so müsste es gehen

      Bau auf dem Beispiel mit den zwei Alarmen auf und definiere die Einschalt- und Abschaltzeit:

      DateTime alarmTime1(2021, 1, 24, 18, 30, 0); // Kümmere dich nicht um das Jahr, Monat, Tag -> 18:30 Uhr
      rtc.setAlarm1(alarmTime1, DS3231_A1_Hour); // Es sollten eigentlich (musst du mal probieren) nur die Stunden/Minuten/Sekunden verglichen werden.

      DateTime alarmTime2(2021, 1, 24, 23, 0, 0); // Zweiter Alarm um 23:00 Uhr.
      rtc.setAlarm2(alarmTime1, DS3231_A2_Hour);

      Oder du modifizierst / erweiterst das Beispiel mit dem Alarm an einem bestimmten Tag entsprechend.

      Wenn es nicht geht, dann schicke mir mal deinen Sketch an Wolfgang.Ewald@wolles-elektronikkiste.de.

      VG, Wolfgang

  33. Hallo Wolfgang, Danke für diesen Blog und Danke Tante Google das sie mich hergeführt hat 😀 . Ich arbeite noch nicht sehr lange mit Arduino und Co und suchte nach einem Tut über den DS3231. Benötige das für mein Projekt. Nun habe ich was ich wollte 🙂 2 Alarme pro Tag die mir zu festgelegten Uhrzeiten eine Statusmeldung senden…. Thx …
    Gruß Ralf

  34. Hallo,
    sehr gute und klare Beschreibung. Interessanter Blog. Vielen Dank.
    Folgender Hinweis: Bei mir ergab sich beim Laden des Programms „Einen Alarm an einem Datum / Uhrzeit“ die folgende Fehlermeldung:
    exit status 1
    ‚volatile bool alarm‘ redeclared as different kind of symbol
    Durch Umbenennen der Variablen läuft das Programm o.k.
    Gruß
    Klaus

    1. Hallo Klaus,

      mit dem Arduino UNO bekomme ich keine Fehlermeldung. Darf ich fragen, welches Board du verwendest? Wahrscheinlich ist da „alarm“ schon an anderer Stelle definiert.

      Vielen Dank auf jeden Fall für den Hinweis! VG, Wolfgang

      1. Hallo Ewald,
        diese Fehlermeldung ist bei Verwendung eines ESP 32 aufgetreten. Vielleicht liegt es daran?
        Gruß Klaus

    2. Guten Tag, darf ich mal fragen wie Sie es gemacht haben?
      Bei mir funktioniert es auch nicht
      LG Marcell

      1. Hi Marcell, wenn du dieselbe Fehlermeldung wie Klaus bekommst, dann kannst du das Problem lösen, indem du die Variable „alarm“ umbenennst. Anscheinend ist „alarm“ irgendwo in der ESP32 Umgebung schon einmal als Bezeichner verwendet worden. Ersetze „alarm“ z. B. durch „myAlarm“. VG, Wolfgang

  35. Hallo
    Du gibst eine Vcc von 2,5V bis 5,5V an. Nach AZ dem Hersteller sollen es aber max. 3,3V sein.
    Da ein Anschluss von 5V erfolgt bruache ich keinen Pegelwandler, bei Vcc von 3,3V zu 5V aber schon.
    Was ist mit Int (SQW) Kann der bei Vcc 3,3V ohne Probleme auf eine Leitung mit 5V gelegt werden?

    1. Hallo, mit AZ meinst du AZ Delivery? Auch die schreiben in ihrem e-Book zu dem Teil:
      „Dank der 2,3-5,5 V Spannungstoleranz des DS3231 ist die RTC sehr Controller-freundlich und lässt sich mit allen Arduino-Typen Dank der 2,3-5,5 V Spannungstoleranz des DS3231 ist die RTC sehr
      Controller-freundlich und lässt sich mit allen Arduino-Typen
      verwenden.

      Im Datenblatt des DS3231 ICs werden 3.3 Volt oben im Datenblatt angegeben. Weiter unten findet man die Recommended Operating Conditions. Dort werden 2.3 – 5.5 Volt angegeben. Diese Toleranz sollte für alle Ein- und Ausgänge gelten.

      Wo man eigentlich aufpassen müsste, ist bei den I2C – Leitungen. Da wird als Grenze VCC + 0.3 angegeben. D.h. bei Betrieb mit 3.3 Volt und Anschluss an einen Microcontroller der mit 5 Volt läuft, ist man drüber. Allerdings habe ich bei meinen Recherchen niemanden gesehen, der das berücksichtigt. Was der Int/SQW Pin in Bezug auf VCC verträgt, dazu habe ich keine Angabe gefunden. Funktionieren tut es bei 5 Volt, das habe ich probiert. Ob es langfristig gut ist, kann ich nicht sagen.

  36. Ein sehr Interessanter Blog. Dieses Thema Interessiert mich sehr und wollte einfach mal Danke sagen. Echt top zusammen gefasst.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert