Arduino Lektion 93: setzen der Uhrzeit an einer RTC DS3231 RealTimeClock (2024)

In diesem Beitrag möchte ich gesondert auf das Setzen der Uhrzeit an einer RTC DS3231 eingehen.

Ich habe bereits ein ausführliches Tutorial zur RTC DS3231 unterArduino Lektion 17: RealTimeClock RTC DS3231 erstellt, jedoch sind noch ein paar Fragen offen geblieben, welche ich nun hier ergänzend liefern möchte.

Es gibt die RTC in zwei Varianten wobei beide Ihren Vor und Nachteil haben, die kleinere hat die gleiche Funktion jedoch ist die Batterie verlötet d.h. wenn diese einmal verschlissen ist muss eine neue eingelötet worden wobei beim größeren Model die Batterie einfach ausgetauscht werden kann.

Für die große RTC mit Batteriefach für eine LIR-2032 wird eine wiederaufladbare Knopfzelle CR2032 (Bezeichnung LIR-2032) benötigt. Diese sind deutlich teurer als die “normalen” einmal Batterien. Diese wiederaufladbaren Akkus sind auf ebay.de für ca. 4€ inkl. Versandkosten erhältlich.

  • Teileliste
    • Mikrocontroller
    • Module
    • Zubehör
    • Anschluß
  • Programmieren
    • manuelle Eingabe über den seriellen Monitor der Arduino IDE
      • empfangen von Daten über einer seriellen Schnittstelle
        • definieren des Datumsformats
      • lesen eines Zeitstempels von einem deployten Sketch
      • Video
    • schreiben eines Zeitstempels auf die RTC
    • Ausgabe der aktuellen Zeit von der RTC
  • Downloads

Teileliste

Mikrocontroller

Für die nachfolgenden Beschreibungen verwende ich den kompatiblen Arduino UNO Mikrocontroller von Keyestudio.

Diesen Microcontroller habe ich bereits im TutorialArduino UNO kompatibles Board von Keyestudio ausführlich beschrieben. Jedoch ist dieser wie ein “normaler” Arduino UNO nur das dieser ein paar Pins mehr hat.

Module

Und man benötigt den Hauptakteur die RTC, wie bereits erwähnt verwende ich hier 2 verschiedene Module welche jedoch mit derselben Bibliothek betrieben werden kann und somit sich eigentlich nicht unterscheiden. Einmal habe ich die RTC DS3231 in Groß und einmal in Klein. Was beide Varianten eint ist, dass diese über I2C angeschlossen werden (SDA & SCL).

Zubehör

Des Weiteren werden einige Breadboardkabel und ggf. ein Breadboard (min. 170 Pins) benötigt.

Aufbau & Schaltung

Die beiden RTC Module verfügen jeweils über die I2C Pins somit werden diese an die analogen Pins A4 (SDA) & analogen Pin A5 (SCL) angeschlossen.

Anschluß

RTC DS3231 großRTC DS3231 kleinArduino UNO
SCLDanaloger Pin A5
SDACanaloger Pin A4
5V+5V
GNDGND

Erstmal verwende ich die kleine Variante der RTC DS3231 und möchte aufzeigen wie diese programmiert wird.

Programmieren

Nun, nachdem die RTC erfolgreich mit dem Arduino verbunden wurde, können wir diese programmieren. Zunächst einmal müssen wir die Zeit setzen, dieses können wir mit zwei Methoden tun, einmal manuell über die Eingabe des seriellen Monitors oder über einen Zeitstempel eines Sketches. Beides hat seine Vor & Nachteile aber sie funktionieren.

manuelle Eingabe über den seriellen Monitor der Arduino IDE

Als erstes möchte ich erläutern wie man das Datum und die Uhrzeit über den seriellen Monitor der Arduino IDE einstellen kann.

Es gibt auch andere Programme, über welche man eine serielle Verbindung zu einem Mikrocontroller wie dem Arduino UNO aufbauen kann, zbsp. Platform I/O oder aber Atmels eigene Entwicklung Atmel Studio. Beide Tools sind sehr gut aber auch sehr umfangreich und für kleinere Projekte viel zu umständlich (nach meiner Meinung).

Den seriellen Monitor der Arduino IDE erreicht man entweder über den Shortcut Strg + Umschalt + M , der Schaltfläche oben rechts oder aber über das Hauptmenü Werkzeuge > Serieller Monitor.

Man kann in diesem Fenster nicht nur Daten betrachten welche vom Mikrocontroller zbsp. per Serial.print(), Serial.println() gesendet werden, sondern man kann auch Daten absenden. Die Daten werden in dem Eingabefeld eingegeben und mit Enter bestätigt.

empfangen von Daten über einer seriellen Schnittstelle

Um Daten in einem Sketch zu verarbeiten, müssen wir zunächst auf das Empfangen von Daten reagieren. Dazu prüft man zunächst ob Daten anliegen und kann diese danach auswerten.

void setup() { //beginn der seriellen Kommunikation mit //9600 baud Serial.begin(9600);}void loop() { //Wenn Daten verfügbar sind dann... if (Serial.available() > 0) { //lesen der Daten von der seriellen Schnittstelle int data = Serial.read(); //Ausgeben und Formatieren der Daten Serial.println(data, DEC); } }

Dabei wird jedes Zeichen als ASCII Nummer zurück geliefert.

Wir wollen jedoch keine ASCII Zeichen speichern, sondern Text, daher benötigen wir ein Char Array. Das Char Array bietet uns vereinfacht gesagt “out of the Box” die Möglichkeit unsere ASCII Nummern Werte in lesbare Zeichen umzuwandeln. Des Weiten benötigen wir noch eine Funktion um zu prüfen, ob nun in diesem Char Array die Zeichenkette “set” vorkommt, denn nur dann soll die Auswertung der Eingabe erfolgen.

void setup() { //beginn der seriellen Kommunikation mit //9600 baud Serial.begin(9600);}void loop() { //Char Array für das Speichern der empfangenen Daten. char linebuf[30] = {}; //Zähler für die Zeichen byte counter = 0; //Wenn Daten verfügbar sind dann... if (Serial.available() > 0) { delay(250); //solange Daten von der seriellen Schnittstelle //empfangen werden... while(Serial.available()){ //speichern der Zeichen in dem Char Array linebuf[counter] = Serial.read(); if(counter < sizeof(linebuf)-1) { counter++; } } Serial.print("Zeichenkette set in ["); Serial.print(linebuf); Serial.print("] "); //Wenn in dem Char Array das Wort "set" vorkommt dann... //Problem ist dass, das Wort "set" nicht nur am Anfang //stehen muss sondern auch in der Mitte oder am Ende der Zeichenkette. if (!strstr(linebuf,"set")){ Serial.print("nicht"); } Serial.println(" gefunden!"); } }

Ein Problem ist hier jedoch, dass nicht geprüft wird, ob die Zeichenkette “set” am Anfang oder Ende steht, es wird lediglich geprüft, dass diese vorkommt.

Nun kann man mit der Pointer Substraction aus dem Char Array die Position herausrechnen.

 if(strstr(linebuf,"set")){ Serial.print("Position "); Serial.println(s-linebuf); }

Die Programmiersprache für den Arduino ist C bzw. C++. Diese Programmiersprache arbeitet mit Pointern diese Zeigen auf Adressen im Speicher. Wie das genau funktioniert werden ich in einem späteren Tutorial erläutern, für dieses Tutorial sollte es reichen zu wissen das man in diese Speicherbereiche lesen und schreiben kann.

Nachdem wir nun die Position der Zeichenkette “set” ermitteln können, wollen wir festlegen, dass die Eingabe nur ausgewertet wird denn die Zeichenkette bei Position 0 steht (quasi am Anfang der Eingabe).

void setup() { //beginn der seriellen Kommunikation mit //9600 baud Serial.begin(9600);}void loop() { //Char Array für das Speichern der empfangenen Daten. char linebuf[30] = {}; //Zähler für die Zeichen byte counter = 0; boolean readData = false; boolean foundKeyWord = false; boolean keyWordIsOnPosZero = false; //Wenn Daten verfügbar sind dann... if (Serial.available() > 0) { delay(250); readData = true; //solange Daten von der seriellen Schnittstelle //empfangen werden... while(Serial.available()){ //speichern der Zeichen in dem Char Array linebuf[counter] = Serial.read(); if(counter < sizeof(linebuf)-1) { counter++; } } //Variable mit Pointer zu einem Speicherbereich char *s; //zuweisen eines Wertes (es wird kein neuer Speicher belegt) s = strstr(linebuf,"set"); //Wenn ein Wert hinterlegt wurde dann... if(s){ foundKeyWord = true; //Ermitteln der Position durch Pointer Substraction int pos = s-linebuf; //Wenn die Zeichenkette "set" am Anfang steht dann... if(pos == 0){ keyWordIsOnPosZero = true; } } } if(readData && foundKeyWord && keyWordIsOnPosZero){ //Auswerten der Daten } else if(readData){ Serial.println("!! Fehler !!"); //Wenn das Schlüsselwort "set" nicht gefunden wurde... if(!foundKeyWord){ Serial.println("Das Schlüsselwort \"set\" wurde nicht gefunden!"); } //Wenn das Schlüsselwort "set" nicht an Position 0 steht, dann... if(!keyWordIsOnPosZero){ Serial.print("Das Schlüsselwort \"set\" steht nicht an Position 0"); Serial.print("der Zeichenkette ["); Serial.print(linebuf); Serial.println("]"); } }}

Wenn nun die Zeichenkette nicht gefunden wird und / oder nicht an Position 1 steht, dann soll eine entsprechende Meldung ausgegeben werden.

Wir haben nun die empfangenen Daten erfolgreich validiert somit können wir nun im nächsten Gang die Zeichenkette parsen und das Datum und die Uhrzeit extrahieren.

Um den Sketch jedoch übersichtlich zu halten wird das ganze zunächst in eine eigene Funktion ausgelagert, welche als Rückgabewert ein Boolean hat. Ein Boolean kann zwei Status annehmen True & False. Die neue Funktion liefert also Boolean.True zurück, wenn das lesen der Werte okay ist und Boolean.False, wenn ein Fehler aufgetreten ist. (Die Fehlermeldungen werden weiterhin ausgegeben.)

char linebuf[30] = {};boolean readData;void setup() { //beginn der seriellen Kommunikation mit //9600 baud Serial.begin(9600);}void loop() { readDataFromSerial(); if(readData){ boolean isParameterSet = parameterIsSet(); if(isParameterSet == true){ //Hier wird nun das Datum und die Uhrzeit geparst. Serial.println("parsen des Datums und der Uhrzeit"); } }}void readDataFromSerial(){ //Char Array für das Speichern der empfangenen Daten. linebuf[30] = {}; //Zähler für die Zeichen byte counter = 0; readData = false; //Wenn Daten verfügbar sind dann... if (Serial.available() > 0) { delay(250); readData = true; //solange Daten von der seriellen Schnittstelle //empfangen werden... while(Serial.available()){ //speichern der Zeichen in dem Char Array linebuf[counter] = Serial.read(); if(counter < sizeof(linebuf)-1) { counter++; } } } else { readData = false; } return linebuf;}boolean parameterIsSet(){ boolean result = true; boolean foundKeyWord = false; boolean keyWordIsOnPosZero = false; //Variable mit Pointer zu einem Speicherbereich char *s; //zuweisen eines Wertes (es wird kein neuer Speicher belegt) s = strstr(linebuf,"set"); //Wenn ein Wert hinterlegt wurde dann... if(s){ foundKeyWord = true; //Ermitteln der Position durch Pointer Substraction int pos = s-linebuf; //Wenn die Zeichenkette "set" am Anfang steht dann... if(pos == 0){ keyWordIsOnPosZero = true; } } if(!foundKeyWord || !keyWordIsOnPosZero){ result = false; Serial.println("!! Fehler !!"); //Wenn das Schlüsselwort "set" nicht gefunden wurde... if(!foundKeyWord){ Serial.println("Das Schlüsselwort \"set\" wurde nicht gefunden!"); } //Wenn das Schlüsselwort "set" nicht an Position 0 steht, dann... if(!keyWordIsOnPosZero){ Serial.print("Das Schlüsselwort \"set\" steht nicht an Position 0"); Serial.print(" der Zeichenkette ["); Serial.print(linebuf); Serial.println("]"); } } return result;}

parsen von Datum & Uhrzeit

definieren des Datumsformats

Bevor wir mit dem Parsen des Datums und der Uhrzeit beginnen, müssen wir zunächst definieren, in welchem Format dieses geliefert werden muss. Da ich aus Deutschland komme und die meisten Besucher / Betrachter meines Blogs aus eben Deutschland sind, möchte ich erläutern wie man das Format TT.MM.JJJJ HH:mm:SS parst.

Wobei

  • TT – für den Tag mit ggf. führender 0 steht, (min. 00, max. 31)
  • MM – für den Monat mit ggf. führender 0 steht, (min. 01, max. 12)
  • JJJJ – für das Jahr in 4 stellig steht, (min. 1900, max. 9999)
  • HH – für die Stunde mit ggf. führender 0 steht, (min. 00, max. 23)
  • mm – für die Minute mit ggf. führender 0 steht, (min. 00, max. 59)
  • SS – für die Sekunde mit ggf. führende 0 steht, (min. 00, max. 59)

Das Trennzeichen für Tag,Monat und Jahr ist ein Punkt und für die Uhrzeit der Doppelpunkt.

#include <Wire.h> //Bibliothek für die kommunikation mit der RTC#define RTC_I2C_ADDRESS 0x68 // I2C Adresse des RTC DS3231char linebuf[30] = {};boolean readData;int t,mo, j, st, mi, s;unsigned long lastReadRTC = -1;void setup() { //Kommunikation über die Wire.h bibliothek beginnen. Wire.begin(); //beginn der seriellen Kommunikation mit 9600 baud Serial.begin(9600);}void loop() { readDataFromSerial(); //Wenn Daten gelesen wurden dann... if(readData){ //prüfen ob in den gelesenen Daten das Schlüsselwort "set" vorkommt, //und das Schlüsselwort an erster stelle steht, dann... boolean isParameterSet = parameterIsSet(); if(isParameterSet == true){ //Hier wird nun das Datum und die Uhrzeit geparst. String line = linebuf; //den Zeitstempel aus dem String extrahieren, //dieser beginnt bei der Position 4 String timestamp = line.substring(4); timestamp.replace("\r",""); timestamp.replace("\n",""); //prüfen ob der Zeitstempel im richtigen Format ist boolean isTimestampCorrect = timestampIsCorrect(timestamp); //wenn der Zeitstempel korrekt ist dann soll dieser auf die RTC geschrieben werden. if(isTimestampCorrect){ rtcWriteTime(); } } } //alle 5 Sekunden den Wert der RTC ausgeben. unsigned long currentMilliseconds = millis(); if((lastReadRTC + 5000) < currentMilliseconds){ lastReadRTC = currentMilliseconds; Serial.println(rtcReadTime()); }}//Funktion zum schreiben / setzen der Uhrzeit.void rtcWriteTime(){ Wire.beginTransmission(RTC_I2C_ADDRESS); Wire.write(0); // Der Wert 0 aktiviert das RTC Modul. Wire.write(decToBcd(s)); Wire.write(decToBcd(mi)); Wire.write(decToBcd(st)); Wire.write(decToBcd(0)); // Wochentag unberücksichtigt Wire.write(decToBcd(t)); Wire.write(decToBcd(mo)); Wire.write(decToBcd(j-2000)); Wire.endTransmission(); }//Ließt den aktuellen Zeitstempel aus dem RTC Modul.String rtcReadTime(){ Wire.beginTransmission(RTC_I2C_ADDRESS); //Aufbau der Verbindung zur Adresse 0x68 Wire.write(0); Wire.endTransmission(); Wire.requestFrom(RTC_I2C_ADDRESS, 7); int sekunde = bcdToDec(Wire.read() & 0x7f); int minute = bcdToDec(Wire.read()); int stunde = bcdToDec(Wire.read() & 0x3f); //Der Wochentag wird hier nicht ausgelesen da dieses mit //dem Modul RTC DS3231 nicht über die Wire.h zuverlässig funktioniert. /* wochentag =*/ bcdToDec(Wire.read()); int tag = bcdToDec(Wire.read()); int monat = bcdToDec(Wire.read()); int jahr = bcdToDec(Wire.read())+2000; char timestamp[30]; sprintf(timestamp,"%02d.%02d.%4d %02d:%02d:%02d",tag,monat,jahr,stunde,minute,sekunde); return timestamp;}boolean timestampIsCorrect(String timestamp){ boolean result = true; //Der Zeitstempel muss 19 Zeichen lang sein. if(timestamp.length() == 19){ //Das Datum muss inkl. den Punkten 10 Zeichen lang sein String datum = timestamp.substring(0,10); String tag = datum.substring(0,2); String monat = datum.substring(3,5); String jahr = datum.substring(6,10); //Die Uhrzeit beginnt ab dem 11 Zeichen aus dem Zeitstempel String uhrzeit = timestamp.substring(11); String stunde = uhrzeit.substring(0,2); String minute = uhrzeit.substring(3,5); String sekunde = uhrzeit.substring(6); //prüfen ob in den Variablen tag, monat, jahr, stunde, minute, sekunde nur Zahlen sind if(!isNummeric(tag) || !isNummeric(monat) || !isNummeric(jahr) || !isNummeric(stunde) || !isNummeric(minute) || !isNummeric(sekunde)){ result = false; } //Wenn das Format korrekt ist, d.h. zbsp. //der Tag darf nicht weniger als 0 Stunden und nicht mehr als 23 Stunden haben, //die Minute darf nicht weniger als 0 Minuten haben und nicht mehr als 59, usw. if(!isFormatCorrect(tag.toInt(), monat.toInt(), jahr.toInt(), stunde.toInt(), minute.toInt(), sekunde.toInt() )){ //Wenn das Format nicht korrekt ist dann wird "false" zurück geliefert. result = false; } else { //Wenn das Format korrekt ist dann sollen die Werte aus den Strings in die Felder geschrieben werden. t = tag.toInt(); mo = monat.toInt(); j = jahr.toInt(); st = stunde.toInt(); mi = minute.toInt(); s = sekunde.toInt(); } } else { Serial.print("Der Zeitstempel muss 20 Zeichenlang sein. ["); Serial.print(timestamp); Serial.println("]"); Serial.print("Länge des Zeitstempels "); Serial.print(timestamp.length()); Serial.println(" Zeichen"); result = false; } if(result == false){ Serial.print("Der übergebene Zeitstempelt ist nicht korrekt. ["); Serial.print(timestamp); Serial.println("]"); Serial.println("Beispiel 24.05.2019 19:35:34"); } return result;}boolean isFormatCorrect(int tag, int monat, int jahr, int stunde, int minute, int sekunde ){ boolean result = true; if(tag <0 || tag > 31){ result = false; } if(monat <1 || monat > 12){ result = false; } if(jahr <1900 || jahr > 9999){ result = false; } if(stunde <0 || stunde > 23){ result = false; } if(minute <0 || minute > 59){ result = false; } if(sekunde <0 || sekunde > 59){ result = false; } return result;}//prüfen ob in dem Parameter chars nur Zahlen enthalten sindboolean isNummeric(String chars){ boolean result = true; for(int i=0;i<chars.length();i++){ int asciiNumber = chars.charAt(i); //Die Zahlen von 0 bis 9 liegen im ASCII Raum 48 bis 57, d.h. //wenn die ASCII Zahl kleiner oder größer ist, dann ist es keine Zahl. if(asciiNumber <48 || asciiNumber>57){ result = false; break; } } return result;}void readDataFromSerial(){ //Char Array für das Speichern der empfangenen Daten. linebuf[30] = {}; //Zähler für die Zeichen byte counter = 0; readData = false; //Wenn Daten verfügbar sind dann... if (Serial.available() > 0) { delay(250); readData = true; //solange Daten von der seriellen Schnittstelle //empfangen werden... while(Serial.available()){ //speichern der Zeichen in dem Char Array linebuf[counter] = Serial.read(); if(counter < sizeof(linebuf)-1) { counter++; } } } else { readData = false; } return linebuf;}boolean parameterIsSet(){ boolean result = true; boolean foundKeyWord = false; boolean keyWordIsOnPosZero = false; //Variable mit Pointer zu einem Speicherbereich char *s; //zuweisen eines Wertes (es wird kein neuer Speicher belegt) s = strstr(linebuf,"set"); //Wenn ein Wert hinterlegt wurde dann... if(s){ foundKeyWord = true; //Ermitteln der Position durch Pointer Substraction int pos = s-linebuf; //Wenn die Zeichenkette "set" am Anfang steht dann... if(pos == 0){ keyWordIsOnPosZero = true; } } if(!foundKeyWord || !keyWordIsOnPosZero){ result = false; Serial.println("!! Fehler !!"); //Wenn das Schlüsselwort "set" nicht gefunden wurde... if(!foundKeyWord){ Serial.println("Das Schlüsselwort \"set\" wurde nicht gefunden!"); } //Wenn das Schlüsselwort "set" nicht an Position 0 steht, dann... if(!keyWordIsOnPosZero){ Serial.print("Das Schlüsselwort \"set\" steht nicht an Position 0"); Serial.print(" der Zeichenkette ["); Serial.print(linebuf); Serial.println("]"); } } return result;}//Convertiert Dezimalzeichen in binäre Zeichen.byte decToBcd(byte val){ return ( (val/10*16) + (val%10) );}//Convertiert binäre Zeichen in Dezimal Zeichen.byte bcdToDec(byte val){ return ( (val/16*10) + (val%16) );}

Wir haben nun die Werte für Tag, Monat, Jahr, Stunde, Minute und Sekunde in den Feldern t, mo, j, st, mi, s.
Im nächsten Schritt müssen wir diese Werte auf die RealTimeClock schreiben. Da diese Funktion auch für die 2. Lösung benötigt wird möchte ich zunächst erläutern wie man einen Zeitstempel aus einem deployten Sketch auslesen kann. Hier gehts zur Funktion zum schreiben eines Zeitstempels auf die RTC.

lesen eines Zeitstempels von einem deployten Sketch

Wenn ein Sketch auf den Arduino hochgeladen wird (umgangssprachlich als deployment bezeichnet), dann wird zusätzlich ein Timestamp abgelegt, dieser entspricht bis auf wenige Sekunden dem aktuellen Zeitstempel. Das Problem ist nur, dass, der Sketch einmal ausgeführt und die Zeit sofort auf die RTC geschrieben werden muss, danach darf dieses nicht erneut gestartet werden.

Daher wird das ganze nicht in der loop ausgeführt, sondern im Setup somit wird sichergestellt dass, diese Funktion nur einmal beim Starten ausgeführt wird.

Für den nachfolgenden Sketch bedienen wir uns zweier Konstanten “__DATE__” & “__TIME__” diese werden mit dem Zeitstempel des Uploads setzt.

Das Datum ist im Format “Jun 30 2019” somit müssen wir die engl. Abkürzungen für die Monate gegen den Monatsnamen prüfen. Diese Prüfung mache ich mit einer einfachen Schleife über die Monate und wenn diese Werte gleich sind, breche ich die Schleife ab und merke mir den Index. Zu diesem Index muss jedoch noch eine Zahl darauf addiert werden, denn Arrays beginnen immer bei 0 jedoch die Monate bei 1.

#include <Wire.h> //Bibliothek für die kommunikation mit der RTC#define RTC_I2C_ADDRESS 0x68 // I2C Adresse des RTC DS3231//Variablen für die Werte aus Datum und Uhrzeitint tag, monat, jahr;int stunde, minute, sekunde;//Array mit den abkürzungen für die Monateconst String months[12] = {"Jan", "Feb", "Mar", "Apr","May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};void setup() { //Kommunikation über die Wire.h bibliothek beginnen. Wire.begin(); //beginn der seriellen Kommunikation mit 9600baud Serial.begin(9600); //parsen des Datums parseDate(__DATE__); //parsen der Uhrzeit parseTime(__TIME__); //Ausgeben des Zeitstempels auf dem seriellen Monitor. char timestamp[30]; sprintf(timestamp,"%02d.%02d.%4d %02d:%02d:%02d",tag,monat,jahr,stunde,minute,sekunde); Serial.println(timestamp); rtcWriteTime();}void parseDate(String date){ String mo = date.substring(0,3); String t = date.substring(4,6); String j = date.substring(7); tag = t.toInt(); for(int i=0;i<=11;i++){ String month = months[i]; if(mo == month){ monat = i; //Da Arrays bei 0 beginnen müssen wir dem Zähler noch einen Wert dazu addieren. ++monat; break; } } jahr = j.toInt();}void parseTime(String time){ String st = time.substring(0,2); String mi = time.substring(3,5); String se = time.substring(6); stunde = st.toInt(); minute = mi.toInt(); sekunde = se.toInt(); }//Funktion zum schreiben / setzen der Uhrzeit.void rtcWriteTime(){ Wire.beginTransmission(RTC_I2C_ADDRESS); Wire.write(0); // Der Wert 0 aktiviert das RTC Modul. Wire.write(decToBcd(sekunde)); Wire.write(decToBcd(minute)); Wire.write(decToBcd(stunde)); Wire.write(decToBcd(0)); // Wochentag unberücksichtigt Wire.write(decToBcd(tag)); Wire.write(decToBcd(monat)); Wire.write(decToBcd(jahr-2000)); Wire.endTransmission(); }//Convertiert Dezimalzeichen in binäre Zeichen.byte decToBcd(byte val){ return ( (val/10*16) + (val%10) );}void loop() { // Bleibt in diesem Beispiel leer.}

Die Funktion “loop” bleibt in diesem Beispiel leer, da der Code wie bereits beschrieben nur einmal ausgeführt werden soll.

Was nun noch fehlt ist ein Sketch, um sich dann die Zeit anzuzeigen bzw. etwas damit zu tun.

#include <Wire.h> //Bibliothek für die kommunikation mit der RTC#define RTC_I2C_ADDRESS 0x68 // I2C Adresse des RTC DS3231unsigned long lastReadRTC = -1;void setup() { //Kommunikation über die Wire.h bibliothek beginnen. Wire.begin(); //beginn der seriellen Kommunikation mit 9600baud Serial.begin(9600);}//Ließt den aktuellen Zeitstempel aus dem RTC Modul.String rtcReadTime(){ Wire.beginTransmission(RTC_I2C_ADDRESS); //Aufbau der Verbindung zur Adresse 0x68 Wire.write(0); Wire.endTransmission(); Wire.requestFrom(RTC_I2C_ADDRESS, 7); int sekunde = bcdToDec(Wire.read() & 0x7f); int minute = bcdToDec(Wire.read()); int stunde = bcdToDec(Wire.read() & 0x3f); //Der Wochentag wird hier nicht ausgelesen da dieses mit //dem Modul RTC DS3231 nicht über die Wire.h zuverlässig funktioniert. /* wochentag =*/ bcdToDec(Wire.read()); int tag = bcdToDec(Wire.read()); int monat = bcdToDec(Wire.read()); int jahr = bcdToDec(Wire.read())+2000; char timestamp[30]; sprintf(timestamp,"%02d.%02d.%4d %02d:%02d:%02d",tag,monat,jahr,stunde,minute,sekunde); return timestamp;}//Convertiert binäre Zeichen in Dezimal Zeichen.byte bcdToDec(byte val){ return ( (val/16*10) + (val%16) );}void loop() { //alle 5 Sekunden den Wert der RTC ausgeben. unsigned long currentMilliseconds = millis(); if((lastReadRTC + 5000) < currentMilliseconds){ lastReadRTC = currentMilliseconds; Serial.println(rtcReadTime()); }}

Video

Hier nun ein kurzes Video.

schreiben eines Zeitstempels auf die RTC

Für das Schreiben des Zeitstempels benötigen wir zusätzlich eine Bibliothek damit wir auf die RTC zugreifen können. (Es ist die Wire Bibliothek.)

Diese Bibliothek ist der Arduino IDE bereits beigefügt somit muss in der Regel nichts installiert werden.

Zusätzlich müssen wir die Adresse der RTC definieren, diese können wir entweder dem Datenblatt entnehmen oder aber mit einem I2C Scanner herausfinden.

#include <Wire.h> //Bibliothek für die kommunikation mit der RTC#define RTC_I2C_ADDRESS 0x68 // I2C Adresse des RTC DS3231

Des Weiteren benötigen wir 2 zusätzliche Hilfsfunktionen welche uns die Integerwerte für Tag, Monat, Jahr usw. in binäre Werte umwandelt (und wieder zurück).

//Convertiert Dezimalzeichen in binäre Zeichen.byte decToBcd(byte val){ return ( (val/10*16) + (val%10) );}//Convertiert binäre Zeichen in Dezimal Zeichen.byte bcdToDec(byte val){ return ( (val/16*10) + (val%16) );}

Diese beiden Funktionen setze ich im Sketch ganz nach unten, da diese nur zur Hilfe dienen.

Wie bereits erwähnt dient, dieses Tutorial zur Ergänzung daher nutze ich im groben die Funktionen aus dem bereits bekannten Tutorial wieder. Daher, hier nun die Methode um die Zeit zu setzen:

//Funktion zum schreiben / setzen der Uhrzeit.void rtcWriteTime(){ Wire.beginTransmission(RTC_I2C_ADDRESS); Wire.write(0); // Der Wert 0 aktiviert das RTC Modul. Wire.write(decToBcd(s)); Wire.write(decToBcd(mi)); Wire.write(decToBcd(st)); Wire.write(decToBcd(0)); // Wochentag unberücksichtigt Wire.write(decToBcd(t)); Wire.write(decToBcd(mo)); Wire.write(decToBcd(j-2000)); Wire.endTransmission(); }

Diese Funktion rufen wir bei erfolgreichen lesen und parsen des Zeitstempels auf.

boolean isParameterSet = parameterIsSet(); if(isParameterSet == true){ //Hier wird nun das Datum und die Uhrzeit geparst. String line = linebuf; //den Zeitstempel aus dem String extrahieren, //dieser beginnt bei der Position 4 String timestamp = line.substring(4); timestamp.replace("\r",""); timestamp.replace("\n",""); //prüfen ob der Zeitstempel im richtigen Format ist boolean isTimestampCorrect = timestampIsCorrect(timestamp); //wenn der Zeitstempel korrekt ist dann soll dieser auf die RTC geschrieben werden. if(isTimestampCorrect){ rtcWriteTime(); }}

Ausgabe der aktuellen Zeit von der RTC

Damit wir die aktuelle Zeit lesen können benötigen wir wiederum eine Funktion, welche die Daten von der RTC liest und für uns umwandelt.

//Ließt den aktuellen Zeitstempel aus dem RTC Modul.String rtcReadTime(){ Wire.beginTransmission(RTC_I2C_ADDRESS); //Aufbau der Verbindung zur Adresse 0x68 Wire.write(0); Wire.endTransmission(); Wire.requestFrom(RTC_I2C_ADDRESS, 7); int sekunde = bcdToDec(Wire.read() & 0x7f); int minute = bcdToDec(Wire.read()); int stunde = bcdToDec(Wire.read() & 0x3f); //Der Wochentag wird hier nicht ausgelesen da dieses mit //dem Modul RTC DS3231 nicht über die Wire.h zuverlässig funktioniert. /* wochentag =*/ bcdToDec(Wire.read()); int tag = bcdToDec(Wire.read()); int monat = bcdToDec(Wire.read()); int jahr = bcdToDec(Wire.read())+2000; char timestamp[30]; sprintf(timestamp,"%02d.%02d.%4d %02d:%02d:%02d",tag,monat,jahr,stunde,minute,sekunde); return timestamp;}

In der vorlezten Zeile der Funktion, formatieren wir uns unseren Zeitstempel wieder in das deutsche Format.

Damit wir in einem Sketch einen Zeitstempel lesen und setzen können habe ich dieses so gelöst das alle 5 Sekunden ein Zeitstempel ausgegeben wird. Somit kann man eine ggf. auftretende Fehlermeldung lesen.

unsigned long lastReadRTC = -1;void loop() { ... //alle 5 Sekunden den Wert der RTC ausgeben. unsigned long currentMilliseconds = millis(); if((lastReadRTC + 5000) < currentMilliseconds){ lastReadRTC = currentMilliseconds; Serial.println(rtcReadTime()); }}

Downloads

Hier möchte ich nun die Sketche für den Arduino zum Download anbieten.

Arduino – RealTimeClock (Teil2) – setzen der Zeit per serieller SchnittstelleHerunterladen

Arduino – RealTimeClock (Teil2) – setzen der Zeit aus dem Zeitstempel des deploymentsHerunterladen

Arduino – RealTimeClock (Teil2) – lesen eines gesetzen ZeitstempelsHerunterladen

Arduino Lektion 93: setzen der Uhrzeit an einer RTC DS3231 RealTimeClock (2024)

References

Top Articles
Latest Posts
Article information

Author: Edmund Hettinger DC

Last Updated:

Views: 5992

Rating: 4.8 / 5 (78 voted)

Reviews: 85% of readers found this page helpful

Author information

Name: Edmund Hettinger DC

Birthday: 1994-08-17

Address: 2033 Gerhold Pine, Port Jocelyn, VA 12101-5654

Phone: +8524399971620

Job: Central Manufacturing Supervisor

Hobby: Jogging, Metalworking, Tai chi, Shopping, Puzzles, Rock climbing, Crocheting

Introduction: My name is Edmund Hettinger DC, I am a adventurous, colorful, gifted, determined, precious, open, colorful person who loves writing and wants to share my knowledge and understanding with you.