

Ziele des Projekts
- die aktuelle Zeit von einem NTP-Server holen und auf das RTC-Modul DS3231 übertragen
- der DS3231 sorgt für die fortlaufende Zeit
- Wochentag, Datum und Uhrzeit auf dem 4-zeiligen LCD mit I²C-Schnittstelle anzeigen
- im 60 Sekunden Rhythmus die Messwerte des BME280 ermitteln und aktualisieren
- optional: zu einstellbaren Zeiten den Zeitstempel des DS3231 mit der Zeit eines NTP-Servers abgleichen

Benötigte Bauteile
- ⇒4-zeiliges LCD mit I²C-Schnittstelle
- ⇒RTC-Modul DS3231
- ⇒BME280
- Leitungsdrähte
Board installieren
Als Mikrocontroller können beliebige Module aus der Familie der ESP-Mikrocontroller verwendet werden.
- ESP32-Wroom
- Arduino Nano ESP32
- ESP32-C6
- XIAO-ESP32-C3
- ESP32-C3 Zero
- ESP32-C3 Super Mini
- ESP32-C6 Zero
- Wemos D1 Mini
NTP-Server
Die korrekte Zeit ist für die Bearbeitung vielfältiger Aufgaben in der Datenverarbeitung notwendig. Daher muss die Zeit regelmäßig mit im Internet zur Verfügung stehenden NTP (Network Time Protocol)-Servern synchronisiert werden.
In Europa sind Zeitserver zu einem länderspezifischen Pool zusammen geschlossen. In Deutschland heißt der Pool de.pool.ntp.org
Als zusätzliche Information muss dem Programm die Zeitzone mitgeteilt werden:
Der Schaltplan
Beispiel ESP32-Wroom
Alle Module werden über I²C angeschlossen: die SCL-Pins werden mit 22, die SDA-Pins mit 21 verbunden.

I²C-Pins anderer Mikrocontroller
- ESP32C6
- ESP32-C6 Zero
- Arduino Nano ESP32
- XIAO-ESP32-C3
- ESP32-C3 Zero
- ESP32-C3 Super Mini
- ESP32-S3
- ESP32 Super Mini
- Wemos D1 Mini
Benötigte Bibliotheken



Das Programm
Bibliotheken und Variable
- die ifdef-Abfrage sorgt dafür, dass je nach verwendeten Mikrocontroller die korrekte WiFi-Bibliothek verwendet wird
- das ⇒Array daysOfTheWeek sorgt für die Darstellung der Wochentage
da die Namen unterschiedlich lang sind, müssen sie mit Leerzeichen aufgefüllt werden - Die ⇒Struktur tm besteht aus verschiedenen Elementen:
tm_hour -> Stunde: 0 bis 23
tm_min -> Minuten: 0 bis 59
tm_sec -> Sekunden 0 bis 59
tm_mday -> Tag 1 bis 31
tm_mon -> Monat: 0 (Januar) bis 11 (Dezember)
tm_year -> Jahre seit 1900
tm_yday -> vergangene Tage seit 1. Januar des Jahres
tm_isdst -> Wert > 0 = Sommerzeit (dst = daylight saving time) - define Zeitzone „CET-1CEST,M3.5.0/02,M10.5.0/03“
definiert die Zeitzone
CET = Central European Time -1 -> 1 Stunde zurück
CEST = Central European Summer Time von
M3 = März, 5.0 = Sonntag 5. Woche, 02 = 2 Uhr
bis M10 = Oktober, 5.0 = Sonntag 5. Woche 03 = 3 Uhr - SekundenAnzeigen entscheidet darüber, ob die Sekunden bei der Darstellung der Uhrzeit fortlaufend angezeigt werden
true/false - KontaktNTP bestimmt, ob zu den angegebenen Zeiten (im Beispiel 6 und 18 Uhr) der NTP-Server kontaktiert werden darf
false: das Programm stellt nur beim Start eine Internetverbindung her
true: zu den angegebenen Zeiten wird der NTP-Server kontaktiert und die Zeit mit dem RTC-Modul abgeglichen
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
#ifdef ESP32 #include "WiFi.h" #else #include "ESP8266WiFi.h" #endif #include "time.h" #include "LCDIC2.h" #include "Adafruit_BME280.h" #include "RTClib.h" RTC_DS3231 rtc; // Name des BME280 Adafruit_BME280 bme; // 4-zeiliges LCD LCDIC2 lcd(0x27, 20, 4); char Router[] = "Router_SSID"; char Passwort[] = "xxxxxxxx"; // NTP-Server aus dem Pool #define Zeitserver "de.pool.ntp.org" #define Zeitzone "CET-1CEST,M3.5.0/02,M10.5.0/03" // time_t enthält die Anzahl der Sekunden seit dem 1.1.1970 0 Uhr time_t aktuelleZeit; tm Zeit; // Array der Wochentag Namen mit Leerzeichen auffüllen char daysOfTheWeek[7][12] = {"Montag ", "Dienstag ", "Mittwoch ", "Donnerstag", "Freitag ", "Samstag ", "Sonntag "}; // nur zur Verschönerung der Anzeige der WiFi-Verbindung/NTP-Server int Zaehler = 0; int Zeile = 0; bool SekundenAnzeigen = true; bool KontaktNTP = true; // true -> Start BME280 erfolgereich, false -> Start BME280 gescheitert bool BMEStart; |
Der setup-Teil
Beim Start des Programms werden Statusmeldungen angezeigt:


- Der BME280 wird in verschiedenen Ausführungen und mit unterschiedlichen HEX-Adressen angeboten: 0x76 oder 0x77
Du musst in Zeile 64 die korrekte Adresse einsetzen.
die HEX-Adresse kannst du mit diesem Programm ⇒herausfinden. - beim ersten Kontakt mit dem NTP-Server entspricht die übermittelte Zeit noch dem UNIX-Zeitstempel, der Zeit in Sekunden, die seit dem 1.1.1970 vergangen sind.
es wird für maximal 40 Sekunden im Sekundentakt die Zeit solange abgefragt, bis die Jahreszahl nicht mehr 1970 ist, also die Zeit korrekt ist
bei Misserfolg wird das Programm beendet - Nachdem die Zeit mit dem NTP-Server synchronisiert wurde, wird sie dem RTC-Modul übermittelt.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
void setup() { Serial.begin(9600); // Schalter für erfolgreiche Synchronisierung der Zeit int Suche = -1; rtc.begin(); // LCD starten lcd.begin(); // Cursor "verstecken" lcd.setCursor(false); lcd.clear(); // WiFi starten // Stations-Modus WiFi.mode(WIFI_STA); WiFi.begin(Router, Passwort); Zaehler = 0; Zeile = 0; lcd.setCursor(Zaehler, Zeile); while (WiFi.status() != WL_CONNECTED) { delay(200); lcd.print("."); // wenn . Zeilenende erreichen -> nächste Zeile Zaehler ++; if(Zaehler > 19) { Zeile ++; Zaehler = 0; } lcd.setCursor(Zaehler, Zeile); // keine WiFi-Verbindung if(Zeile > 3) { lcd.clear(); lcd.setCursor(0, 0); lcd.print("WiFi-Verbindung"); lcd.setCursor(0, 1); lcd.print("fehlgeschlagen!"); lcd.setCursor(0, 3); lcd.print("Programmende"); while(1); } } lcd.clear(); lcd.setCursor(0, 0); lcd.print("WiFi gestartet!"); // Zeitzone: Parameter für die zu ermittelnde Zeit configTzTime(Zeitzone, Zeitserver); lcd.setCursor(0, 1); // BME280 starten Hex-Adressen ox76 oder 0x77 if(bme.begin(0x76)) { lcd.print("BME280 gestartet!"); BMEStart = true; } else { else { BMEStart = false; lcd.print("BME nicht vorhanden "); } // aktuelle Zeit holen time(&aktuelleZeit); // localtime_r -> Zeit in die lokale Zeitzone setzen localtime_r(&aktuelleZeit, &Zeit); // nur ESP32 #ifdef ESP32 { // beim Start entspricht das Datum der Unixtime: 1.1.1970 // das Datum soll erst angezeigt werden, wenn es korrekt ist String Jahr = String(Zeit.tm_year + 1900); int Zaehler = 0; // String Jahr nach "1970" durchsuchen int Suche = Jahr.indexOf("1970"); Zaehler = 0; Zeile = 2; lcd.setCursor(Zaehler, Zeile); // solange die Suche nicht erfolgreich ist // maximal 40 Sekunden while (Suche != -1 && Zaehler < 40) { // aktuelle Zeit holen time(&aktuelleZeit); // localtime_r -> Zeit in die lokale Zeitzone setzen localtime_r(&aktuelleZeit, &Zeit); Jahr = String(Zeit.tm_year + 1900); // String Jahr nach "1970" durchsuchen Suche = Jahr.indexOf("1970"); delay(1000); lcd.setCursor(Zaehler, Zeile); lcd.print("."); Zaehler ++; if(Zaehler > 19) { Zeile ++; Zaehler = 0; } // Zeit konnte innerhalb 40 Sekunden nicht geholt werden if(Zeile > 3) { lcd.clear(); lcd.setCursor(0, 0); lcd.print("Verbindung zum"); lcd.setCursor(0, 1); lcd.print("Zeitserver"); lcd.setCursor(0, 2); lcd.print("fehlgeschlagen!"); lcd.setCursor(0, 3); lcd.print("Programmende"); while(1); } } } #endif // Datum/Zeit erfolgreich synchronisiert lcd.setCursor(0, 3); lcd.print("Zeit synchronisiert"); delay(3000); lcd.clear(); if(BMEStart) MessungAnzeigen(); // Datum/Zeit erfolgreich synchronisiert if (Suche == -1) ZeitAktualisieren(); } |
Der loop-Teil
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
void loop() { DateTime aktuell = rtc.now(); /* Datumsformat festlegen DD -> Tag mit führender 0 MM -> Monat mit führender 0 YYYY -> vollständige Angabe des Jahres */ char Datum[] = "DD.MM.YYYY"; lcd.setCursor(0, 0); // Wochentag anzeigen lcd.print(daysOfTheWeek[aktuell.dayOfTheWeek()]); // Datum in Zeichenkette (String) umwandeln und anzeigen lcd.setCursor(0, 1); lcd.print(aktuell.toString(Datum)); // Format der Zeitangabe festlegen char ZeitFormat[] = "hh:mm:ss"; lcd.setCursor(11, 1); // Zeitangabe in Zeichenkette (String) umwandeln und anzeigen String Uhrzeit = aktuell.toString(ZeitFormat); if(SekundenAnzeigen) lcd.print(Uhrzeit); else lcd.print(Uhrzeit.substring(0, 5)); // neue Messung zur vollen Minute if(aktuell.second() == 0) { if(BMEStart) MessungAnzeigen(); // wegen der zeitlichen Verzögerung Zeit neu holen DateTime aktuell = rtc.now(); } // die Variable KontaktNTP bestimmt, ob der NTP-Server kontaktiert werden darf if(KontaktNTP) { if(aktuell.hour() == 6 && aktuell.minute() == 0) ZeitAktualisieren(); if(aktuell.hour() == 18 && aktuell.minute() == 0) ZeitAktualisieren(); } } |
Funktionen
Im loop-Teil werden zwei ⇒Funktionen aufgerufen:
- ZeitAktualisieren()
zu einstellbaren Zeiten kann die Zeit erneut mit dem NTP-Server synchronisiert werden
als Vorschlag 6 Uhr und 18 Uhr
Ist die Variable KontaktNTP auf false gesetzt, wird der NTP-Server nur beim Start kontaktiert - MessungAnzeigen()
nach jeder vollen Minute werden die Messwerte des BME280 abgefragt und angezeigt
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
void ZeitAktualisieren() { // aktuelle Zeit holen time(&aktuelleZeit); // localtime_r -> Zeit in die lokale Zeitzone setzen localtime_r(&aktuelleZeit, &Zeit); rtc.adjust(DateTime(Zeit.tm_year, Zeit.tm_mon + 1, Zeit.tm_mday, Zeit.tm_hour, Zeit.tm_min, Zeit.tm_sec)); } void MessungAnzeigen() { String Temperatur = String(bme.readTemperature(), 1); String Luftfeuchtigkeit = String(bme.readHumidity(), 1); String Luftdruck = String(int(bme.readPressure() / 100.0)); // float verwendet den . als Dezimaltrennzeichen // . mit replace durch , ersetzen Temperatur.replace(".", ","); Luftdruck.replace(".", ","); Luftfeuchtigkeit.replace(".", ","); lcd.setCursor(0, 2); lcd.print(Temperatur + "\337C "); lcd.setCursor(8, 2); lcd.print(Luftdruck + " hPa "); lcd.setCursor(0, 3); lcd.print(Luftfeuchtigkeit + "%"); } |
Quellen
- NodeMCU WiFi-API
- Espressif WiFi-API
- Adafruit RTC DS3231
- Adafruit BME280
- Definition der Zeitzonen
Letzte Aktualisierung: