Die eigene Internet-Uhr: Ein ESP8266-Projekt für Einsteiger

Von DerSchneider


Einleitung

Uhren gehen vor oder nach. Bei jeder Zeitumstellung muss nachjustiert werden. Computer hingegen beziehen ihre Zeit seit Jahrzehnten automatisch von NTP-Servern – sie sind per Definition immer auf die Sekunde genau. Warum sollte man diese Präzision nicht auch für eine eigene Wanduhr nutzen?

Die Antwort lautet: ESP8266. Dieses winzige, preiswerte Modul kann sich per WLAN mit einem Zeitserver verbinden, die aktuelle UTC-Zeit abrufen und auf einem kleinen OLED-Display anzeigen. Was auf den ersten Blick nach einem fortgeschrittenen Projekt klingt, ist mit der richtigen Anleitung auch für Einsteiger machbar.

Dieser Artikel führt Schritt für Schritt durch die Entwicklung einer voll funktionsfähigen Internet-Uhr auf Basis des ESP8266 NodeMCU. Das Besondere: Die Uhr berücksichtigt automatisch Sommer- und Winterzeit und zeigt nicht nur die Uhrzeit, sondern auch Datum, Wochentag und Kalenderwoche an. Ein Anfängerprojekt mit überraschendem Tiefgang.


Was ist eine Internet-Uhr und wie funktioniert sie?

Das Herzstück ist das Network Time Protocol (NTP). Dieses seit 1985 existierende Protokoll ermöglicht die hochpräzise Synchronisation von Uhren in Computernetzen. Weltweit existieren öffentlich zugängliche NTP-Server – darunter der bekannte pool.ntp.org –, die von jedem internetfähigen Gerät angefragt werden können.

Der ESP8266 fragt in regelmäßigen Abständen einen solchen Server ab und erhält die aktuelle UTC-Zeit (koordinierte Weltzeit). Da der ESP8266 selbst keine interne Echtzeituhr besitzt, wird die Zeit bei jedem Abruf neu synchronisiert – ein einfacher, aber effektiver Ansatz, der auf eine externe Batterie verzichtet.

Die eigentliche Herausforderung besteht nicht im Zeitabruf, sondern in der korrekten Umrechnung der UTC-Zeit in die lokale Zeit unter Berücksichtigung der Sommerzeit. Die europäische Regelung – letzter Sonntag im März bis letzter Sonntag im Oktober – muss im Code abgebildet werden.


Benötigte Komponenten

KomponenteTypAnmerkung
MikrocontrollerESP8266 NodeMCU (ESP-12E)Alternativ Wemos D1 Mini
Display0,96″ OLED SSD1306, 128×64 Pixel, I²C4-polige Variante
VerbindungskabelJumper-Kabel (female–female)4 Stück
StromversorgungMicro-USB-Kabel + NetzteilOder USB-Port des Computers

Das SSD1306-OLED-Display benötigt keine Hintergrundbeleuchtung und verbraucht nur dann Energie, wenn einzelne Pixel leuchten – ideal für den Dauerbetrieb. Die I²C-Variante benötigt nur vier Anschlüsse: VCC, GND, SCL und SDA.


Schritt 1: Die Entwicklungsumgebung einrichten

Die Programmierung erfolgt über die kostenlose Arduino IDE. Folgende Schritte sind notwendig:

  1. Arduino IDE installieren (Version 1.8.19 oder neuer) von arduino.cc.
  2. ESP8266-Unterstützung nachinstallieren: Unter Datei → Voreinstellungen wird bei Zusätzliche Boardverwalter-URLs folgende Adresse eingefügt:texthttps://arduino.esp8266.com/stable/package_esp8266com_index.json
  3. Board-Paket installierenWerkzeuge → Board → Boardverwalter, nach esp8266 suchen und die aktuellste Version installieren.
  4. Board auswählenWerkzeuge → Board → ESP8266 Boards → NodeMCU 1.0 (ESP-12E Module).
  5. Bibliotheken installieren:
    • U8g2Sketch → Bibliothek einbinden → Bibliotheken verwalten, nach U8g2 suchen und installieren. Diese Bibliothek steuert das OLED-Display.
    • NTPClient: Ebenfalls über den Bibliotheksverwalter installieren.
    • TimeLib: Wird für die Zeitberechnung benötigt.

Schritt 2: Der Schaltplan – Verdrahtung der Hardware

Die I²C-Verbindung zwischen ESP8266 und OLED-Display ist denkbar einfach:

OLED-PinESP8266 NodeMCUBemerkung
VCC3.3VBetriebsspannung
GNDGNDMasse
SCLD1 (GPIO5)I²C-Takt
SDAD2 (GPIO4)I²C-Daten

Wichtig: Der ESP8266 arbeitet mit 3,3V-Logik. Das OLED-Display ist damit kompatibel. Ein Spannungswandler ist nicht erforderlich.

Ein häufiger Fehler: Die Pins D1 und D2 werden fälschlicherweise mit anderen GPIOs vertauscht. Für I²C sind GPIO5 (SCL) und GPIO4 (SDA) die empfohlenen Pins.


Schritt 3: Das Programm – Zeile für Zeile erklärt

Der vollständige Quellcode ist umfangreich, lässt sich aber in überschaubare Abschnitte gliedern.

3.1 Bibliotheken einbinden und Konfiguration

cpp

#include <U8g2lib.h>
#include <Wire.h>
#include <TimeLib.h>
#include <ESP8266WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
  • U8g2lib.h: Steuerung des OLED-Displays
  • Wire.h: I²C-Kommunikation
  • TimeLib.h: Zeitberechnungen (Schaltjahr, Wochentag)
  • ESP8266WiFi.h: WLAN-Funktionalität
  • NTPClient.h / WiFiUdp.h: NTP-Zeitabruf über UDP

Die WLAN-Zugangsdaten werden als Konstanten definiert:

cpp

const char* ssid = "DEIN_WLAN_NAME";
const char* password = "DEIN_WLAN_PASSWORT";

Der NTP-Client wird mit dem Zeitserver pool.ntp.org initialisiert. Der gmtOffset_sec wird auf 0 gesetzt, da die Zeitzonenumrechnung im Code selbst erfolgt – dies ist der sauberste Weg, um Sommer- und Winterzeit korrekt abzubilden.

3.2 Display-Initialisierung

cpp

U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(
    U8G2_R0, 
    /* clock=*/ 14,   // SCL an GPIO14 (D5) – Achtung: Anpassung!
    /* data=*/ 12,    // SDA an GPIO12 (D6) – Achtung: Anpassung!
    /* reset=*/ U8X8_PIN_NONE
);

Achtung: In dieser Initialisierung werden die Pins 14 (SCL) und 12 (SDA) verwendet. Wer die empfohlenen Pins D1 (GPIO5) und D2 (GPIO4) nutzen möchte, muss die Werte entsprechend anpassen:

cpp

U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(
    U8G2_R0, 
    /* clock=*/ 5,    // GPIO5 = D1
    /* data=*/ 4,     // GPIO4 = D2
    /* reset=*/ U8X8_PIN_NONE
);

3.3 Sommerzeit-Erkennung

Die Funktion isSummerTimeSimple() prüft, ob das aktuelle Datum in der europäischen Sommerzeit liegt:

cpp

bool isSummerTimeSimple(tmElements_t tm) {
    int year = tmYearToCalendar(tm.Year);
    int month = tm.Month;
    int day = tm.Day;
    int hour = tm.Hour;
    
    if (month < 3 || month > 10) return false;
    if (month > 3 && month < 10) return true;
    
    // März: Letzter Sonntag, 2:00 Uhr
    if (month == 3) {
        int lastSunday = 31 - ((5 * year / 4 + 4) % 7);
        if (day > lastSunday) return true;
        if (day == lastSunday && hour >= 2) return true;
        return false;
    }
    
    // Oktober: Letzter Sonntag, 3:00 Uhr
    if (month == 10) {
        int lastSunday = 31 - ((5 * year / 4 + 1) % 7);
        if (day > lastSunday) return false;
        if (day == lastSunday && hour >= 3) return false;
        return true;
    }
    
    return false;
}

Dieser Algorithmus berechnet den letzten Sonntag im März und Oktober – die beiden Schalttage der europäischen Sommerzeit.

3.4 Zeitumrechnung auf lokale Zeit

cpp

void adjustForLocalTime(tmElements_t &tm) {
    int hourOffset = 1;  // Winterzeit: MEZ = UTC+1
    if (isSummerTime(tm)) {
        hourOffset = 2;   // Sommerzeit: MESZ = UTC+2
    }
    tm.Hour += hourOffset;
    // Korrektur bei Stundenüberlauf
    if (tm.Hour >= 24) {
        tm.Hour -= 24;
        tm.Day++;
        // Monats- und Jahreskorrektur (vereinfacht)
        if (tm.Day > 31) {
            tm.Day = 1;
            tm.Month++;
            if (tm.Month > 12) {
                tm.Month = 1;
                tm.Year++;
            }
        }
    }
}

Die Funktion addiert je nach Jahreszeit eine oder zwei Stunden zur UTC-Zeit. Die vereinfachte Monatskorrektur ist für dieses Projekt ausreichend – für den 31. eines Monats wird auf den 1. des Folgemonats gesprungen.

3.5 Kalenderwoche berechnen

Die Kalenderwoche nach ISO 8601 (Wochenbeginn Montag) zu berechnen, ist eine der anspruchsvolleren Aufgaben. Der Algorithmus basiert auf dem Wochentag des 4. Januars – der Tag, der definiert, ob eine Woche zur ersten Kalenderwoche gehört.

cpp

int calculateWeekNumberSimple(tmElements_t tm) {
    int year = tmYearToCalendar(tm.Year);
    int month = tm.Month;
    int day = tm.Day;
    
    // Tage seit Jahresbeginn
    int daysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    bool isLeapYear = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
    if (isLeapYear) daysInMonth[1] = 29;
    
    int dayOfYear = day;
    for (int i = 0; i < month - 1; i++) {
        dayOfYear += daysInMonth[i];
    }
    
    // Wochentag des 4. Januars (Zeller's Kongruenz)
    int y = year;
    int K = y % 100;
    int J = y / 100;
    int h = (4 + (13*14)/5 + K + K/4 + J/4 + 5*J) % 7;
    int jan4Weekday = (h + 5) % 7; // 0=Sonntag, 1=Montag, ...
    
    int weekday = tm.Wday - 1; // 0=Sonntag, 1=Montag, ...
    
    int correction = 0;
    if (weekday < jan4Weekday) correction = 1;
    
    int weekNumber = (dayOfYear - jan4Weekday + 10) / 7 - correction;
    
    // Grenzfälle behandeln
    if (weekNumber < 1) return 52;
    if (weekNumber > 52) {
        // Prüfen, ob das Jahr 53 Wochen hat
        int h2 = (1 + (13*14)/5 + K + K/4 + J/4 + 5*J) % 7;
        int jan1Weekday = (h2 + 5) % 7;
        if (jan1Weekday == 3 || (isLeapYear && jan1Weekday == 2)) {
            return 53;
        }
        return 1;
    }
    return weekNumber;
}

3.6 Die Hauptschleife – Anzeige aktualisieren

Die loop()-Funktion aktualisiert die Anzeige einmal pro Sekunde:

cpp

void loop() {
    static unsigned long lastUpdate = 0;
    
    if (millis() - lastUpdate >= 1000) {
        lastUpdate = millis();
        
        timeClient.update();
        time_t currentTime = timeClient.getEpochTime();
        
        tmElements_t tm;
        breakTime(currentTime, tm);
        
        tmElements_t localTm = tm;
        bool isSummer = isSummerTimeSimple(localTm);
        adjustForLocalTime(localTm);
        int weekNumber = calculateWeekNumberSimple(localTm);
        
        // Formatieren und anzeigen
        char timeStr[20];
        snprintf(timeStr, sizeof(timeStr), "%02d:%02d", 
                 localTm.Hour, localTm.Minute);
        // ... Display-Ausgabe
    }
}

Die statische Variable lastUpdate speichert den Zeitpunkt der letzten Aktualisierung. millis() liefert die seit dem Start vergangenen Millisekunden – ein einfacher, aber effektiver Timer.


Schritt 4: Programm hochladen und testen

  1. ESP8266 per USB mit dem Computer verbinden.
  2. In der Arduino IDE den richtigen Port auswählen (Werkzeuge → Port).
  3. Auf Hochladen (Pfeil-Symbol) klicken.
  4. Die Ausgabe im Seriellen Monitor verfolgen (Werkzeuge → Serieller Monitor, 115200 Baud).

Nach erfolgreichem Upload verbindet sich der ESP8266 mit dem WLAN und synchronisiert die Zeit. Das Display zeigt Datum, Uhrzeit und Kalenderwoche an.

Fehlersuche: Zeigt das Display nichts an, kann die I²C-Adresse des OLED-Displays abweichen. Der I²C-Scanner-Sketch (in der Arduino IDE unter Beispiele → Wire → i2c_scanner) hilft, die korrekte Adresse zu ermitteln – üblich sind 0x3C oder 0x3D.


Mögliche Erweiterungen und Variationen

Das Grundgerüst lässt sich auf vielfältige Weise erweitern:

  • Wetterdaten: Abruf von Temperatur und Luftfeuchtigkeit über eine Wetter-API
  • Raumtemperatur: Anschluss eines DHT22-Sensors
  • Gehäuse: 3D-gedrucktes Gehäuse für einen stabilen Stand
  • Deep Sleep: Stromsparmodus für batteriebetriebenen Betrieb
  • OTA-Updates: Programmaktualisierung über WLAN, ohne USB-Kabel

Fazit

Die Entwicklung einer Internet-Uhr mit ESP8266 ist ein ideales Einsteigerprojekt: Die Hardware ist preiswert und einfach zu verdrahten, die Software baut auf etablierten Bibliotheken auf und das Ergebnis – eine präzise, automatisch sommerzeitumstellende Uhr – ist im Alltag tatsächlich nützlich.

Die besondere Herausforderung liegt in der korrekten Zeitumrechnung und der Berechnung der Kalenderwoche – beides Themen, die weit über das reine „Licht-an-Licht-aus“-Programmieren hinausgehen. Wer dieses Projekt verstanden hat, beherrscht die Grundlagen der Netzwerkprogrammierung, der Zeitsynchronisation und der grafischen Anzeige auf OLED-Displays.

Die nächste Zeitumstellung kommt bestimmt – mit dieser Uhr ist sie kein Problem mehr.


Quellen

  1. ESP8266 Community ForumHow to Wire an OLED Display with ESP8266 NodeMCUhttps://www.esp8266.com/viewtopic.php?f=11&t=24401 
  2. funduino.deAnaloge Uhr mit Wemos D1 Minihttps://funduino.de/analoge-uhr-mit-wemos-d1 
  3. AZ-Delivery BlogNTP-UTC-UHR mit ESP8266-01S + 0,96″ OLED-Displayhttps://www.az-delivery.de/blogs/azdelivery-blog-fur-arduino-und-raspberry-pi/ntp-utc-uhr-mit-esp8266-01s-0-96-oled-display 
  4. draeger-it.blogESP8266 Programmierung mit 0,96″ OLED Display von Ideasparkhttps://draeger-it.blog/esp8266-programmierung-mit-096-oled-display-von-ideaspark-eine-schritt-fuer-schritt-anleitung/ 
  5. Arduino ForumHeltec WIFI Kit8 Board ESP8266 – Projekt „Uhr mit Wetterdatenanzeige“https://forum.bastelgarage.ch/d/62-heltec-wifi-kit8-board-esp8266-projekt-uhr-mit-wetterdatenanzeige 
  6. GitHub – NTPClient LibraryNTP Client library for ESP8266 with full timezone supporthttps://github.com/brunnels/NTPClient 

Kommentar abschicken

Das hast du vielleicht verpasst