Nebenuhrsteuerung mit ESP32-C3 und DRV8871 - kompakt und günstig
Inzwischen habe ich schon einige Steuerungen für Nebenuhren gebaut, die grundsätzlich auch perfekt funktionieren. Jetzt sind wieder zwei Nebenuhren auf meinem Schreibtisch, und da hat mich der Ehrgeiz gepackt, ob es noch günstiger und kompakter geht, als bei meinen bisherigen Steuerungen.
Die Steuerung muss mehrere Aufgaben erfüllen:
- Muss sowohl für klassische Nebenuhren als auch für Bodet Klappzahlenuhren funktionieren, die eine eigene Stromversorgung für den Datums-Motor brauchen
- Einfache Inbetriebnahme der Nebenuhr
- Soll für 12 Volt und 24 Volt Nebenuhren geeignet sein
- Kompakt und so günstig wie möglich
- Einfacher Aufbau der Steuerung
Also habe ich mich auf die Suche nach geeigneten Bauteilen gemacht, mit denen ich das umsetzen kann. An dem prinzipiellen Aufbau mit einem Mikrocontroller wollte ich aber nichts ändern, da sich diese Lösung sehr bewährt hat. Auch eine Gangreserve mittels Akku sollte wieder dabei sein.
Die Bauteile
Letztendlich sind es nur vier Module, die für die Steuerung gebraucht werden:
ESP32-C3
Die bisherigen Mikrocontroller, also das LILYGO T-Display oder ein klassisches ESP32 DevKit Board, funktionieren natürlich grundsätzlich, aber diese Boards sind aufgrund der vielen Pins recht groß und für die Steuerung brauche ich eigentlich nur zwei Pins zur Ansteuerung der H-Brücke. Sucht man nach kleineren Boards, stößt man sehr schnell auf den ESP32-C3. Auf AliExpress habe ich ein Board gefunden, das sogar ein winziges 0.42 Zoll OLED Display hat. Das ist ganz praktisch, weil man so sehen kann, was der Mikrocontroller gerade macht.
Der ESP32-C3 ist Espressifs erster Mikrocontroller mit RISC-V-Architektur statt dem sonst üblichen Xtensa-Core. Mit seinem 32-Bit RISC-V Single-Core-Prozessor erreicht er Taktfrequenzen bis zu 160 MHz.
Trotz des Single-Cores bringt der C3 die bewährte ESP32-Ausstattung mit: WiFi 4 (802.11b/g/n), Bluetooth 5 LE, 400 KB SRAM und umfangreiche Peripherie inklusive SPI, I2C, UART, ADC und PWM. Für die Steuerung der Nebenuhr also mehr als ausreichend.
DRV8871 H-Brückentreiber
Der DRV8871 ist ein H-Brückentreiber mit bis zu 3,6 A Spitzenstrom. Die H-Brücke besteht aus vier N-Channel MOSFETs und wird über zwei Logik-Eingänge gesteuert, die bidirektionale Motorsteuerung und PWM-basierte Geschwindigkeitsregelung ermöglichen.
Er zeichnet sich durch einen weiten Betriebsspannungsbereich aus, der von minimal 6,5 V bis maximal 45 V reicht. Dadurch können problemlos Nebenuhren mit 12 Volt und 24 Volt betrieben werden. Theoretisch könnte auch eine Nebenuhr mit 48 Volt funktionieren, ich habe aber bisher keinen Step-Up Converter gefunden, der von 5 V auf 45 V hochregeln kann.
Die Ansteuerung ist deutlich einfacher als beim L293D. Es wird nur eine Spannungsversorgung benötigt und auch keine Enable-Eingänge. Die Eingänge (IN1 und IN2) werden ab einer Spannung von 1,5 V sicher als logische 1 (High-Pegel) erkannt, somit kann der Chip problemlos mit dem ESP32 angesteuert werden.
TP4056
Die TP4056 USB-C Li-Ion Ladeplatine ist der Standard, wenn es um kleine Module mit USB-C Anschluss geht. Der maximale Ladestrom beträgt 1 A (per SMD-Widerstand anpassbar) und die Abschaltspannung beträgt 4,2 V.
Im Gegensatz zu vielen anderen Ladeplatinen hat dieses Modul auch einen Tiefentladeschutz des Akkus von 2,5 V. Dadurch ist auch ein längerer Stromausfall kein Problem und der Akku wird nicht beschädigt. Zwei LEDs signalisieren den Ladestatus: Rot während des Ladens, Blau bei vollständig geladenem Akku.
Step-Up Converter SX1308
Der SX1308 Step-Up Converter erzeugt die nötige Spannung für die Nebenuhr. Der Eingangsspannungsbereich beträgt 2 V bis 24 V und die Ausgangsspannung kann bis 28 V geregelt werden.
Es ist das kleinste Modul, das ich finden konnte. Die Ausgangsspannung wird mit dem Potentiometer eingestellt. Man muss aber eine ganze Weile kurbeln, bis sich etwas tut. Der eigentliche Einstellungsbereich sind dann nur wenige Umdrehungen.
Die Schaltung
Das folgende Diagramm zeigt, wie die Module verbunden sind. Der Laderegler TP4056 liefert bei geladenem Akku eine Spannung von 4,2 V, die sowohl den Step-Up Converter SX1308 als auch das ESP32-C3 Modul versorgt. Das ESP32-C3 Development Board hat am 5V-Eingang eine Verpolungsschutzdiode (ca. 0,7 V Spannungsabfall), dahinter folgt der onboard ME6211 Spannungsregler mit 3,3 V Ausgangsspannung. Bei 4,2 V Eingangsspannung stehen dem Mikrocontroller somit etwa 3,1 V zur Verfügung. Das ist ausreichend, da der ESP32-C3 laut Datenblatt ab 3,0 V arbeitet.
Für den Gleichstrommotor in den Bodet Nebenuhren mit Datumsanzeige werden 3 V benötigt. Diese werden durch zwei in Reihe geschaltete Dioden (2x 0,7 V ≈ 1,4 V Spannungsabfall) aus den 4,2 V des Akkus erzeugt, was eine Motorspannung von ca. 2,8 V ergibt.
Ich habe das Setup mit verschiedenen Uhrentypen getestet. Zwei Bodet Nebenuhren mit Datumsanzeige und eine normale Zeiger-Nebenuhr. Die Schaltung arbeitet mit allen Uhren zuverlässig.
Platine
Da die Schaltung mittlerweile stabil läuft und ich ehrlich gesagt keine Lust mehr hatte, jedes Mal alles mühsam auf Lochraster zusammenzulöten, war es Zeit für eine professionelle Platine. Ich habe in KiCad eine kompakte Trägerplatine entworfen, auf die nur noch die einzelnen Module aufgelötet werden müssen.
Für die Herstellung der Platinen habe ich Unterstützung von PCBWay erhalten, die dieses Projekt gesponsert haben. An dieser Stelle ein großes Dankeschön! Der Bestellvorgang war dabei sehr einfach. Dank des PCBWay-Plugins für KiCad musste ich keine Gerber-Dateien manuell exportieren und hochladen, sondern konnte die Fertigungsdaten direkt aus der Software heraus mit nur einem Klick übermitteln.
Von der Bestellung bis zur Lieferung mit DHL sind lediglich 5 Tage vergangen und die Qualität der gelieferten PCBs ist hervorragend, was den Aufbau der Steuerung deutlich einfacher gemacht hat als das frühere Fädeln auf Lochraster.
Die fertige Steuerung schaut nun folgendermaßen aus:
Der Vergleich mit den früheren Versionen zeigt, dass die Steuerung inzwischen recht kompakt geworden ist.
Das Layout ist dabei bewusst flexibel gehalten:
- Standard-Nebenuhren: Bei normalen Uhren werden die zusätzlichen Dioden und die zugehörige Anschlussklemme nicht benötigt und können einfach weggelassen werden.
- Stromversorgung: Wer keine Batterieversorgung braucht, kann den TP4056 Laderegler weglassen. Stattdessen wird eine Drahtbrücke eingelötet, die Ein- und Ausgang (IN+ und OUT+) des Ladereglers verbindet.
- Spannungsanpassung (Bodet): Ohne den Laderegler steigt die Versorgungsspannung von 4,2 V auf 5 V. Das ist relevant, falls eine Bodet Klappzahlenuhr mit Datum angesteuert wird. Für diesen Fall ist auf der Platine Platz für eine dritte Diode vorgesehen, um die Spannung entsprechend zu reduzieren. Wird diese nicht benötigt, ersetzt man sie ebenfalls einfach durch eine Drahtbrücke.
Die Steuerungs-Software
Die Software ist mit dem ESP-IDF Framework entwickelt und besteht aus mehreren Komponenten. Jede Komponente kapselt eine spezifische Funktionalität in einer eigenen C++-Klasse. Die main.cpp fungiert als zentrale Orchestrierungsschicht, die alle Komponenten initialisiert und miteinander verbindet.
┌─────────────────────────────────────────────────────────────────┐
│ main.cpp │
│ (Orchestrierung) │
└─────────────────────────────────────────────────────────────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌─────────────┐ ┌──────────────┐ ┌─────────────┐ ┌─────────────────┐
│ SlaveClock │ │ Display │ │ Button │ │ WifiProvisioner │
│ │ │ │ │ │ │ │
│ • Motor- │ │ • OLED │ │ • Debounce │ │ • Captive Portal│
│ steuerung │ │ • Screensaver│ │ • Short/ │ │ • NTP Sync │
│ • Zeit- │ │ • I2C │ │ Long/ │ │ • Credentials │
│ verfolgung│ │ │ │ Double │ │ │
└─────────────┘ └──────────────┘ └─────────────┘ └─────────────────┘
│ │ │ │
▼ ▼ ▼ ▼
[GPIO] [I2C] [GPIO] [WiFi/SNTP]
DRV8871 SSD1306 Taster Internet
SlaveClock
Die SlaveClock-Klasse ist das Herzstück der Anwendung. Sie steuert den Lavet-Schrittmotor über einen DRV8871 H-Brücken-Treiber und speichert die aktuelle Zeigerstellung.
Impulsgebung: Nebenuhren verwenden einen polarisierten Schrittmotor, der bei jedem Wechsel der Polarität einen Schritt macht. Die Klasse verwaltet daher intern einen _polarity_level, der nach jedem Impuls invertiert wird.
// Vereinfachte Darstellung der Impulslogik
if (_polarity_level) {
gpio_set_level(_input1_pin, 1);
gpio_set_level(_input2_pin, 0);
} else {
gpio_set_level(_input1_pin, 0);
gpio_set_level(_input2_pin, 1);
}
vTaskDelay(pdMS_TO_TICKS(_pulse_width_ms));
gpio_set_level(_input1_pin, 0);
gpio_set_level(_input2_pin, 0);
_polarity_level = !_polarity_level;
Zeitverfolgung: Die Klasse speichert die aktuelle Zeigerstellung. Bei jedem Aufruf von update() wird diese mit der Systemzeit verglichen. Liegt die Zeigerstellung hinter der Systemzeit zurück, werden entsprechend viele Impulse gesendet, um aufzuholen:
int minutes_diff = Systemzeit - Zeigerstellung;
if (minutes_diff > 0) {
sendPulses(minutes_diff);
_clock_tm = timeinfo_now; // Zeigerstellung aktualisieren
}
LED-Anzeige: Optional kann ein GPIO-Pin für eine LED konfiguriert werden. Diese leuchtet während jedes Impulses auf und gibt so visuelles Feedback über die Aktivität des Motors.
Display
Die Display-Klasse kapselt ein kleines 0.42" OLED-Display (72×40 Pixel) mit SSD1306-Controller. Die Kommunikation erfolgt über I2C unter Verwendung der u8g2-Grafikbibliothek.
Layout:: Das Display zeigt zwei Informationen:
- Obere Zeile (kleine Schrift): Statusmeldungen wie "Booting...", "Connecting..." oder "Time synched"
- Untere Zeile (große Schrift): Die aktuelle Uhrzeit im Format HH:MM:SS
Screensaver-Funktion: Ein integrierter FreeRTOS-Software-Timer schaltet das Display nach einer konfigurierbaren Zeit automatisch aus. Der Timer läuft unabhängig von der Hauptschleife im Hintergrund. Wird das Display manuell eingeschaltet (z.B. per Tastendruck), startet der Timer automatisch neu.
I2C-Bus-Recovery: Ein besonderes Problem bei Mikrocontroller-Projekten ist, dass ein Reset während einer laufenden I2C-Übertragung den Bus in einem ungültigen Zustand hinterlassen kann. Der Slave (das Display) wartet dann endlos auf weitere Daten und blockiert den Bus. Die Display-Initialisierung führt daher vor dem eigentlichen Setup ein "Clock-Cycling" durch: 9 Taktpulse auf der SCL-Leitung gefolgt von einer STOP-Bedingung. Dies befreit einen eventuell hängenden Slave und stellt den Bus in einen definierten Zustand.
Button
Die Button-Klasse läuft als eigenständiger FreeRTOS-Task im Hintergrund und übernimmt die komplette Auswertung eines Tasters inklusive Entprellung und Erkennung verschiedener Druckmuster.
Entprellung (Debouncing): Mechanische Taster prellen, d.h. beim Drücken und Loslassen entstehen kurzzeitig mehrere Schaltimpulse. Die Klasse ignoriert Zustandsänderungen, die innerhalb von 50ms nach der letzten Änderung auftreten.
Ereignistypen:
- Kurzer Druck: Gedrückt und losgelassen in < 500ms
- Langer Druck: Gehalten für > 500ms
- Doppelklick: Zwei kurze Drücke innerhalb von 400ms
Callback-basierte API: Anstatt den Button-Zustand aktiv abzufragen, registriert die Anwendung Callback-Funktionen, die bei Ereignissen aufgerufen werden. Durch die Verwendung von std::function sind moderne Lambda-Ausdrücke möglich:
button.onShortPress([&oled](Button::PressType) {
oled.togglePower();
});
Der Lambda-Ausdruck übergibt die Variable oled per Referenz ([&oled]) und kann so direkt auf das Display-Objekt zugreifen, ohne dass globale Variablen nötig wären.
Wifi Provisioning
Die Wifi Provisioning Komponente löst ein fundamentales Problem von IoT-Geräten: Wie konfiguriert man die WLAN-Zugangsdaten, wenn das Gerät noch kein Netzwerk hat?
Captive Portal: Beim ersten Start (oder wenn keine gültigen Zugangsdaten gespeichert sind) öffnet der ESP32 einen eigenen Access Point mit dem Namen "Slave Clock Setup". Verbindet sich ein Smartphone oder Laptop mit diesem Netzwerk, wird automatisch eine Konfigurationsseite angezeigt. Diese Webseite zeigt:
- Eine Liste der verfügbaren WLANs (nach Signalstärke sortiert, ohne Duplikate)
- Eingabefeld für das WLAN-Passwort
- Auswahl der Zeitzone
- Eingabe der aktuellen Zeigerstellung
Ablauf der Ersteinrichtung:
┌────────────────────────┐
│ ESP32 startet als │
│ Access Point │
└───────────┬────────────┘
▼
┌────────────────────────┐
│ Benutzer verbindet │
│ sich mit "Slave Clock" │
└───────────┬────────────┘
▼
┌────────────────────────┐
│ Captive Portal zeigt │
│ Konfigurationsseite │
└───────────┬────────────┘
▼
┌────────────────────────┐
│ Benutzer wählt WLAN, │
│ gibt Passwort ein │
└───────────┬────────────┘
▼
┌────────────────────────┐
│ ESP32 verbindet sich │
│ mit dem Heim-WLAN │
└────────────────────────┘
Zeitsynchronisation: Nach erfolgreicher WLAN-Verbindung synchronisiert die Komponente die Systemzeit über das Simple Network Time Protocol (SNTP) mit einem Internet-Zeitserver. Die konfigurierte Zeitzone wird dabei berücksichtigt, sodass die Systemzeit automatisch in Lokalzeit vorliegt.
Programmablauf
Die Hauptschleife ist bewusst einfach gehalten: Einmal pro Sekunde wird die aktuelle Zeit geholt, auf dem Display angezeigt und die Nebenuhr aktualisiert. Die gesamte Komplexität, also Entprellung, Screensaver-Timer, WiFi-Events, läuft in den Komponenten im Hintergrund ab.
┌─────────────┐
│ Start │
└──────┬──────┘
▼
┌────────────────────────┐
│ Komponenten initiieren │
│ • SlaveClock │
│ • Display │
│ • Button │
└────────────┬───────────┘
▼
┌────────────────────────┐
│ WiFi Credentials │
│ vorhanden? │
└────────────────────────┘
│ │
Ja Nein
│ │
│ ▼
│ ┌─────────────────┐
│ │ Captive Portal │
│ │ starten │
│ └────────┬────────┘
│ │
▼ ▼
┌────────────────────────┐
│ Mit WLAN verbinden │
└────────────┬───────────┘
▼
┌────────────────────────┐
│ NTP Zeitsynchronisation│
└────────────┬───────────┘
▼
┌────────────────────────┐
│ Zeigerstellung setzen │
└────────────┬───────────┘
▼
┌────────────────┐
┌─────│ Hauptschleife │◄────┐
│ └────────────────┘ │
│ │
▼ │
┌──────────────┐ │
│ Zeit anzeigen│ │
│ Uhr updaten │────────────────────┘
└──────────────┘ (jede Sekunde)
Der Code der Nebenuhr-Steuerung ist auf GitHub verfügbar.
Fazit
Nach mehreren Iterationen bin ich mit der finalen Platine sehr zufrieden. Die Kombination aus ESP32-C3 und DRV8871 funktioniert zuverlässig und kostet in der Herstellung nur wenige Euro. Die Steuerung synchronisiert sich automatisch über NTP und hält auch Stromausfälle aus.
Die nächsten Bodet Klappzahlen Nebenuhren liegen schon bereit und werden demnächst umgebaut, aber dazu mehr in einem separaten Artikel.









