ESP32 Uhrzeit per NTP holen: Internetuhr mit OLED-Display, Zeitzone & Sommerzeit
Dein ESP32 hat keine Ahnung, wie spät es ist – nach jedem Neustart beginnt seine Welt bei null. Dabei steckt die Lösung schon im Board: WLAN. Über das Network Time Protocol (NTP) holt sich der ESP32 die aktuelle Uhrzeit sekundengenau von Zeitservern aus dem Internet – kostenlos, ohne Zusatzhardware und mit automatischer Umstellung zwischen Sommer- und Winterzeit. In diesem Tutorial baust du Schritt für Schritt eine Internetuhr mit OLED-Display: von der ersten NTP-Abfrage über den richtigen Zeitzonen-String für Deutschland bis zu zeitgesteuerten Aktionen wie einem täglichen Schaltvorgang – und du erfährst, wann sich zusätzlich eine DS3231-Echtzeituhr als Offline-Backup lohnt.
NTP, RTC oder beides? Die Möglichkeiten im Überblick
Für eine korrekte Uhrzeit im Mikrocontroller-Projekt gibt es zwei Wege: eine Echtzeituhr (RTC) wie das DS3231-Modul mit Pufferbatterie – oder eben NTP übers WLAN. Beide haben ihre Berechtigung:
| Kriterium | NTP (Internet) | DS3231 (RTC-Modul) |
|---|---|---|
| Zusatzhardware | keine | Modul + Knopfzelle |
| Genauigkeit | Millisekunden, driftfrei | ±2 ppm, driftet minimal |
| Funktioniert ohne WLAN | nein | ja |
| Sommerzeit automatisch | ja (per TZ-String) | nein, manuell |
| Zeit nach Stromausfall | neu abrufen (Sekunden) | sofort verfügbar |
Kurz gesagt: Hat dein Projekt sowieso WLAN, ist NTP die eleganteste Lösung – keine Batterie, kein Drift, keine manuelle Sommerzeit-Fummelei. Für Projekte ohne Netz bleibt die DS3231-Echtzeituhr erste Wahl – und die Kombination aus beidem ergibt eine Uhr, die auch einen Routerausfall übersteht.
Das brauchst du
U8g2 (oder Adafruit SH110X)Verkabelung
Das OLED hängt am I2C-Bus des ESP32 – mehr als vier Leitungen sind nicht nötig:
| OLED-Pin | ESP32-Pin | Funktion |
|---|---|---|
| VCC | 3.3V | Stromversorgung |
| GND | GND | Masse |
| SCL | GPIO 22 | I2C-Takt |
| SDA | GPIO 21 | I2C-Daten |
ℹ️ Displaytyp prüfen: 1,3-Zoll-OLEDs nutzen meist den SH1106-Controller, die kleineren 0,96-Zoll-Displays den SSD1306. In U8g2 wählst du einfach den passenden Konstruktor. Wie du die I2C-Adresse deines Displays herausfindest, zeigt das I2C-Scanner-Tutorial.
1Erste NTP-Abfrage: Uhrzeit im Seriellen Monitor
Der ESP32-Core bringt alles mit – keine zusätzliche Bibliothek nötig. Der Dreh- und Angelpunkt ist configTime() mit dem TZ-String für deine Zeitzone. Für Deutschland lautet er CET-1CEST,M3.5.0,M10.5.0/3 – er beschreibt Winterzeit (CET, UTC+1), Sommerzeit (CEST) und die Umstellungstermine (letzter Sonntag im März bzw. Oktober). Die Umstellung passiert damit vollautomatisch:
#include <WiFi.h>
#include <time.h>
const char* WLAN_NAME = "DeinWLAN";
const char* WLAN_PASSWORT = "DeinPasswort";
// Zeitzone Deutschland: automatische Sommer-/Winterzeit
const char* TZ_INFO = "CET-1CEST,M3.5.0,M10.5.0/3";
const char* NTP_SERVER = "pool.ntp.org";
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(WLAN_NAME, WLAN_PASSWORT);
Serial.print("Verbinde mit WLAN");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(" verbunden!");
// NTP starten: Zeitzone + Server angeben
configTzTime(TZ_INFO, NTP_SERVER);
// warten, bis die Zeit synchronisiert ist
struct tm zeit;
while (!getLocalTime(&zeit)) {
Serial.println("Warte auf NTP-Synchronisation ...");
delay(1000);
}
Serial.println("Zeit synchronisiert!");
}
void loop() {
struct tm zeit;
if (getLocalTime(&zeit)) {
// Ausgabe z. B.: Donnerstag, 23.07.2026 14:35:07
Serial.println(&zeit, "%A, %d.%m.%Y %H:%M:%S");
}
delay(1000);
}
Nach dem Upload siehst du im Seriellen Monitor die aktuelle Uhrzeit – sekundengenau und in der richtigen Zeitzone. Der ESP32 synchronisiert sich danach in regelmäßigen Abständen selbstständig nach und gleicht die Drift seines internen Oszillators aus.
💡 Die Format-Codes von strftime: %H:%M:%S ergibt 14:35:07, %d.%m.%Y das Datum, %A den Wochentag. Mit setenv-Locale bleibt der Wochentag englisch – für deutsche Namen nutzt du einfach ein eigenes Array, wie im OLED-Beispiel gleich zu sehen.
2Die OLED-Internetuhr
Jetzt kommt die Zeit aufs Display: große Uhrzeit in der Mitte, Datum und Wochentag darunter. Das Beispiel nutzt die U8g2-Bibliothek mit dem SH1106-Konstruktor für das 1,3-Zoll-Display:
#include <WiFi.h>
#include <time.h>
#include <U8g2lib.h>
const char* WLAN_NAME = "DeinWLAN";
const char* WLAN_PASSWORT = "DeinPasswort";
const char* TZ_INFO = "CET-1CEST,M3.5.0,M10.5.0/3";
const char* NTP_SERVER = "pool.ntp.org";
// 1,3 Zoll OLED (SH1106) - fuer 0,96 Zoll: U8G2_SSD1306_128X64_NONAME_F_HW_I2C
U8G2_SH1106_128X64_NONAME_F_HW_I2C display(U8G2_R0, U8X8_PIN_NONE);
const char* WOCHENTAGE[] = {"Sonntag", "Montag", "Dienstag", "Mittwoch",
"Donnerstag", "Freitag", "Samstag"};
void setup() {
Serial.begin(115200);
display.begin();
display.clearBuffer();
display.setFont(u8g2_font_7x14_tf);
display.drawStr(10, 35, "Verbinde WLAN ...");
display.sendBuffer();
WiFi.mode(WIFI_STA);
WiFi.begin(WLAN_NAME, WLAN_PASSWORT);
while (WiFi.status() != WL_CONNECTED) delay(500);
configTzTime(TZ_INFO, NTP_SERVER);
struct tm zeit;
while (!getLocalTime(&zeit)) delay(500);
}
void loop() {
struct tm zeit;
if (!getLocalTime(&zeit)) return;
char uhrzeit[9]; // HH:MM:SS
char datum[11]; // TT.MM.JJJJ
strftime(uhrzeit, sizeof(uhrzeit), "%H:%M:%S", &zeit);
strftime(datum, sizeof(datum), "%d.%m.%Y", &zeit);
display.clearBuffer();
// grosse Uhrzeit
display.setFont(u8g2_font_logisoso24_tn);
display.drawStr(4, 34, uhrzeit);
// Datum + Wochentag darunter
display.setFont(u8g2_font_7x14_tf);
display.drawStr(22, 52, datum);
display.drawStr(22, 64, WOCHENTAGE[zeit.tm_wday]);
display.sendBuffer();
delay(250); // 4x pro Sekunde reicht voellig
}
Fertig ist die driftfreie Wohnzimmer-Uhr! Wenn du lieber analoge Zeiger auf einem runden Display magst, schau dir das Projekt GC9A01 Runde Uhr an – dort kannst du die DS3231 direkt durch die NTP-Zeit aus diesem Tutorial ersetzen.
3Zeitgesteuerte Aktionen: täglich schalten wie eine Zeitschaltuhr
Eine Uhr anzeigen ist nett – richtig nützlich wird NTP, wenn dein ESP32 zeitgesteuert handelt: Licht um 7:00 an, Bewässerung um 20:30, Nachtabschaltung um 23:00. Der Trick: Jede Minute prüfen, ob Stunde und Minute einer Schaltzeit entsprechen – und mit einem Merker dafür sorgen, dass pro Minute nur einmal geschaltet wird:
#include <WiFi.h>
#include <time.h>
const char* WLAN_NAME = "DeinWLAN";
const char* WLAN_PASSWORT = "DeinPasswort";
const char* TZ_INFO = "CET-1CEST,M3.5.0,M10.5.0/3";
const char* NTP_SERVER = "pool.ntp.org";
const int RELAIS_PIN = 26;
// Schaltzeiten: {Stunde, Minute, Zustand}
struct Schaltzeit { int stunde; int minute; bool an; };
Schaltzeit plan[] = {
{ 7, 0, true }, // 07:00 einschalten
{23, 30, false }, // 23:30 ausschalten
};
int letzteGeschalteteMinute = -1;
void setup() {
Serial.begin(115200);
pinMode(RELAIS_PIN, OUTPUT);
digitalWrite(RELAIS_PIN, LOW);
WiFi.mode(WIFI_STA);
WiFi.begin(WLAN_NAME, WLAN_PASSWORT);
while (WiFi.status() != WL_CONNECTED) delay(500);
configTzTime(TZ_INFO, NTP_SERVER);
struct tm zeit;
while (!getLocalTime(&zeit)) delay(500);
Serial.println("Zeitschaltuhr aktiv.");
}
void loop() {
struct tm zeit;
if (!getLocalTime(&zeit)) return;
int minuteDesTages = zeit.tm_hour * 60 + zeit.tm_min;
// pro Minute nur einmal pruefen
if (minuteDesTages != letzteGeschalteteMinute) {
for (auto &s : plan) {
if (zeit.tm_hour == s.stunde && zeit.tm_min == s.minute) {
digitalWrite(RELAIS_PIN, s.an ? HIGH : LOW);
Serial.printf("%02d:%02d - Relais %s\n",
s.stunde, s.minute, s.an ? "AN" : "AUS");
letzteGeschalteteMinute = minuteDesTages;
}
}
}
delay(1000);
}
Das ist im Kern eine Zeitschaltuhr ohne RTC-Modul – die Hardware-Variante mit DS3231 und ausführlicher Relais-Beschaltung findest du im Zeitschaltuhr-Tutorial, alles zum Relais selbst im KY-019 Guide.
ℹ️ WLAN weg – Zeit weg? Nein! Einmal synchronisiert, läuft die interne Uhr des ESP32 auch ohne Netz weiter – sie driftet nur langsam (einige Sekunden pro Tag). Kritisch ist nur ein Neustart ohne WLAN: Dann startet die Uhr bei 1970. Genau hier glänzt die Kombination mit der batteriegepufferten DS3231: NTP stellt bei jeder Gelegenheit die RTC, und nach einem Stromausfall liefert die RTC sofort die korrekte Zeit zurück.
Ideen zum Weiterbauen
💡 Was du mit exakter Uhrzeit alles anstellen kannst
- Datenlogger mit echtem Zeitstempel: Kombiniere NTP mit dem SD-Karten-Datenlogger – jede Messung bekommt Datum und Uhrzeit
- Wetterstation mit Zeitanzeige: Ergänze die ESP32-Wetterstation um Uhrzeit und Datum im Display
- Smart-Home-Anbindung: Schicke zeitgestempelte Sensordaten per MQTT an Home Assistant
- Stromsparende Uhr: Mit Deep Sleep nur einmal pro Minute aufwachen und die Anzeige aktualisieren
- Morgen-Dashboard: Uhrzeit plus Innentemperatur vom BMP280-Sensor auf einem Display vereint
Perfekt für: Uhren, Zeitschaltuhren, Datenlogger, Dashboards, Bewässerungssteuerungen
Troubleshooting
⚠️ getLocalTime() schlägt dauerhaft fehl: Meist blockiert die Firewall/der Router UDP-Port 123, oder das WLAN ist gar nicht verbunden. Prüfe WiFi.status() und teste alternativ den Server de.pool.ntp.org oder time.google.com.
⚠️ Uhrzeit ist eine Stunde daneben: Der TZ-String stimmt nicht. Für Deutschland exakt CET-1CEST,M3.5.0,M10.5.0/3 übernehmen – ein häufiger Fehler ist ein fehlendes /3 am Ende oder die Nutzung von configTime(3600, 3600, ...) mit festen Offsets, die die Sommerzeit nicht automatisch umstellen.
⚠️ Zeit springt nach Neustart auf 1970: Normal, solange kein WLAN da ist – die interne Uhr ist flüchtig. Abhilfe: DS3231 als Backup oder beim Booten auf die Synchronisation warten, bevor zeitkritische Logik startet.
⚠️ OLED bleibt schwarz: Falscher Controller-Konstruktor (SH1106 vs. SSD1306) oder falsche I2C-Adresse. Der I2C-Scanner bringt Klarheit – typisch ist 0x3C.
⚠️ Sekundenanzeige ruckelt oder friert kurz ein: Blockierende Aufrufe im loop() – Display-Updates und Netzwerkzugriffe entkoppelst du am besten mit dem millis()-Muster.
⚠️ Wochentag falsch berechnet: tm_wday zählt ab Sonntag (0 = Sonntag, 1 = Montag ...) – das Array muss entsprechend sortiert sein.
🚀 Dein Internetuhr-Starterpaket
Alles für dieses Projekt bekommst du im Shop:
- ESP32 Entwicklungsboard (USB-C) – WLAN und NTP-Support inklusive
- 1,3 Zoll I2C OLED Display – große, gut lesbare Zeitanzeige
- 0,96 Zoll OLED Display (SSD1306) – die kompakte Alternative
- DS3231 Echtzeituhr-Modul – batteriegepuffertes Offline-Backup
- KY-019 Relais-Modul – für die Zeitschaltuhr-Erweiterung
- Breadboard 400 Kontakte – für den schnellen Aufbau
Fazit
Mit drei Zeilen – WLAN verbinden, configTzTime() aufrufen, getLocalTime() abfragen – hat dein ESP32 eine sekundengenaue, driftfreie Uhr, die Sommer- und Winterzeit vollautomatisch umstellt. Du hast eine OLED-Internetuhr gebaut, per strftime-Formatcodes Uhrzeit und Datum formatiert und mit einem Schaltzeiten-Array eine softwarebasierte Zeitschaltuhr umgesetzt.
Merke dir: Der TZ-String CET-1CEST,M3.5.0,M10.5.0/3 ist für Deutschland die halbe Miete, die interne Uhr läuft nach der Synchronisation auch ohne WLAN weiter, und für Projekte, die einen Neustart ohne Netz überstehen müssen, kombinierst du NTP mit einer DS3231. Damit hat die Frage „Wie spät ist es eigentlich?“ in deinen Projekten für immer eine präzise Antwort – viel Spaß beim Bauen!