Vom Prototyp zur Praxis: Beispielcode für drei CYD-Displays
Einleitung
Die Idee klingt bestechend: Drei identische ESP32-2432S028-Boards – liebevoll „Cheap Yellow Display“ oder kurz CYD genannt – an einer gemeinsamen 5-Volt-Versorgung, jedes mit einer eigenen Aufgabe. Das erste zeigt Uhrzeit, Datum und Außentemperatur, das zweite überwacht die Signalstärke von drei WLAN-Netzen, das dritte präsentiert die nächsten Termine aus dem Google Kalender.
Doch wer bereits erste Gehversuche mit dem CYD unternommen hat, kennt die Ernüchterung: Ein Beispielsketch aus dem Internet, der auf dem einen Board fehlerfrei läuft, produziert auf dem nächsten nur einen schwarzen Bildschirm. Die Ursachen sind vielfältig – unterschiedliche Display-Treiber, abweichende Pin-Belegungen bei neueren Revisionen, knapper Arbeitsspeicher oder schlicht fehlende Bibliotheken. Der folgende Beitrag ergänzt die bereits dokumentierte Hardware-Konfiguration um konkreten, nachvollziehbaren Beispielcode für alle drei geplanten Displays. Dabei steht nicht der Anspruch im Vordergrund, fertige Lösungen zu präsentieren, sondern die einzelnen Funktionsblöcke so zu zeigen, dass sie sich unabhängig voneinander testen und anschließend kombinieren lassen.
Warum Beispielcode in der CYD-Welt selten „einfach funktioniert“
Bevor der eigentliche Code vorgestellt wird, lohnt ein Blick auf die strukturellen Hürden, die dieses Projekt begleiten. Wer sie kennt, spart sich manche frustrierende Stunde.
Die erste Hürde ist die Hardware-Fragmentierung. Das ursprüngliche CYD-2432S028 verwendete einen ILI9341-Controller. Neuere Varianten mit zwei USB-Anschlüssen (Micro-USB plus USB-C) setzen gelegentlich auf den ST7789. Ein französischer Technikblog dokumentiert genau diesen Fall: Der Autor musste die Treiberdefinition in der User_Setup.h von ILI9341 auf ST7789 umstellen und die SPI-Frequenz reduzieren, bevor das Display überhaupt ein Bild zeigte. Die bereits vorliegende Konfigurationsdatei deckt beide Fälle ab.
Die zweite Hürde ist der Arbeitsspeicher. Der ESP32-WROOM-32 verfügt über rund 520 Kilobyte SRAM. Sobald WLAN-Verbindung, HTTP-Anfragen, JSON-Verarbeitung und TFT-Rendering gleichzeitig stattfinden, wird es eng. Ein bekanntes Wi-Fi-Analyzer-Projekt auf GitHub wechselte aus diesem Grund von TFT_eSPI zur Arduino_GFX-Bibliothek, die weniger Speicher belegt. Ein chinesisches Wetteruhr-Projekt wiederum musste im Arduino-IDE das Partitionsschema auf „Huge APP“ umstellen, damit der kompilierte Sketch überhaupt in den Flash-Speicher passte.
Die dritte Hürde ist die Datenanbindung. Während WLAN-Scan und NTP-Zeitabfrage direkt auf dem ESP32 funktionieren, ist der Google Kalender ein anderes Kaliber. Die offizielle Google Calendar API erfordert OAuth-2.0-Authentifizierung mit Token-Erneuerung und TLS-verschlüsselten Anfragen – für einen ESP32 ohne PSRAM eine echte Herausforderung. Ein E-Paper-Kalenderprojekt, das die Google-API tatsächlich nutzt, setzt konsequenterweise auf einen ESP32-S3 mit 8 MB PSRAM und das ESP-IDF-Framework. Für die CYD-Klasse ist der Umweg über einen ICS-Feed oder einen selbst gehosteten JSON-Zwischendienst der realistischere Weg.
Display 1: Uhrzeit, Datum und Außentemperatur
Das erste Display übernimmt die Rolle einer kompakten Wetterstation. Die Zeit wird per NTP synchronisiert, die Temperatur über die OpenWeatherMap-API abgerufen. Beide Datenquellen sind kostenlos und für den ESP32 gut handhabbar.
Der vollständige Beispielsketch
cpp
/*
* CYD Display 1 – Wetterstation
* Zeigt Uhrzeit, Datum und Außentemperatur
* Hardware: ESP32-2432S028 (CYD)
*/
#include <TFT_eSPI.h>
#include <WiFi.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <time.h>
TFT_eSPI tft = TFT_eSPI();
// ---- Konfiguration ----
const char* WLAN_SSID = "DEIN_WLAN_NAME";
const char* WLAN_PASSWORT = "DEIN_WLAN_PASSWORT";
const char* OPENWEATHER_API_KEY = "DEIN_API_KEY";
const char* STADT = "Berlin";
const char* LAND = "de";
// Zeitzone: MEZ/MESZ (Europa/Berlin)
const char* NTP_SERVER = "pool.ntp.org";
const long GMT_OFFSET_SEK = 3600; // UTC+1
const int SOMMERZEIT_OFFSET = 3600; // +1h während Sommerzeit
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, NTP_SERVER, GMT_OFFSET_SEK, 60000);
// Backlight-Pin des CYD
#define TFT_BL 21
// Aktualisierungsintervalle
const unsigned long WETTER_INTERVALL_MS = 600000; // 10 Minuten
unsigned long letzteWetterAbfrage = 0;
float aktuelleTemperatur = NAN;
void setup() {
Serial.begin(115200);
delay(200);
// Backlight einschalten – häufigste Fehlerquelle!
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH);
// Display initialisieren
tft.init();
tft.setRotation(1); // Querformat (320x240)
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
// Startmeldung
tft.setTextSize(2);
tft.setCursor(10, 10);
tft.println("Wetterstation");
tft.setTextSize(1);
tft.setCursor(10, 40);
tft.println("Verbinde mit WLAN...");
// WLAN verbinden
WiFi.mode(WIFI_STA);
WiFi.begin(WLAN_SSID, WLAN_PASSWORT);
unsigned long startVersuch = millis();
while (WiFi.status() != WL_CONNECTED && millis() - startVersuch < 20000) {
delay(500);
Serial.print(".");
}
if (WiFi.status() == WL_CONNECTED) {
tft.fillScreen(TFT_BLACK);
tft.setCursor(10, 40);
tft.print("Verbunden: ");
tft.println(WiFi.localIP());
// NTP starten
timeClient.begin();
timeClient.update();
// Interne Uhr des ESP32 mit NTP-Zeit synchronisieren
configTime(GMT_OFFSET_SEK, SOMMERZEIT_OFFSET, NTP_SERVER);
} else {
tft.fillScreen(TFT_BLACK);
tft.setCursor(10, 40);
tft.println("WLAN fehlgeschlagen!");
}
}
void loop() {
// NTP-Zeit aktualisieren
timeClient.update();
// Temperatur regelmäßig abrufen
if (millis() - letzteWetterAbfrage > WETTER_INTERVALL_MS
|| letzteWetterAbfrage == 0) {
if (WiFi.status() == WL_CONNECTED) {
float neueTemp = temperaturAbrufen();
if (!isnan(neueTemp)) {
aktuelleTemperatur = neueTemp;
}
}
letzteWetterAbfrage = millis();
}
// Anzeige aktualisieren
anzeigeAktualisieren();
delay(1000); // jede Sekunde neu zeichnen
}
// ---------- Hilfsfunktionen ----------
void anzeigeAktualisieren() {
// Nur den Bereich löschen, der sich ändert – reduziert Flackern
tft.fillRect(0, 0, 320, 240, TFT_BLACK);
// Uhrzeit groß darstellen
String zeit = timeClient.getFormattedTime(); // Format HH:MM:SS
tft.setTextColor(TFT_CYAN, TFT_BLACK);
tft.setTextSize(4);
tft.setCursor(20, 20);
tft.print(zeit);
// Datum aus der internen Uhr
struct tm zeitInfo;
if (getLocalTime(&zeitInfo)) {
char datumPuffer[32];
strftime(datumPuffer, sizeof(datumPuffer),
"%d.%m.%Y", &zeitInfo);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextSize(2);
tft.setCursor(20, 80);
tft.print(datumPuffer);
// Wochentag
char wochentag[16];
strftime(wochentag, sizeof(wochentag), "%A", &zeitInfo);
tft.setCursor(20, 110);
tft.print(wochentag);
}
// Temperatur
tft.setTextSize(2);
tft.setCursor(20, 170);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
if (!isnan(aktuelleTemperatur)) {
tft.print("Aussen: ");
tft.print(aktuelleTemperatur, 1);
tft.print(" C");
} else {
tft.print("Aussen: --.- C");
}
}
float temperaturAbrufen() {
String url = String("http://api.openweathermap.org/data/2.5/weather?q=")
+ STADT + "," + LAND
+ "&units=metric&lang=de&appid=" + OPENWEATHER_API_KEY;
HTTPClient http;
http.begin(url);
int httpCode = http.GET();
float ergebnis = NAN;
if (httpCode == 200) {
String payload = http.getString();
// JSON-Größe: OpenWeatherMap-Antwort ist ca. 500-800 Bytes
DynamicJsonDocument doc(1024);
DeserializationError fehler = deserializeJson(doc, payload);
if (!fehler) {
ergebnis = doc["main"]["temp"].as<float>();
const char* beschreibung = doc["weather"][0]["description"];
Serial.print("Wetter: ");
Serial.print(ergebnis);
Serial.print(" C, ");
Serial.println(beschreibung);
} else {
Serial.print("JSON-Fehler: ");
Serial.println(fehler.c_str());
}
} else {
Serial.print("HTTP-Fehler: ");
Serial.println(httpCode);
}
http.end();
return ergebnis;
}
Hinweise zu diesem Sketch
Die Sommerzeitumschaltung ist im gezeigten Code nicht automatisch. Der NTPClient unterstützt keine Zeitzonendatenbank. Wer die Umstellung nicht manuell pflegen möchte, kann auf die Funktion configTime() mit dem POSIX-Zeitzonenstring "CET-1CEST,M3.5.0,M10.5.0/3" zurückgreifen – diese Variante ist im Code bereits vorbereitet, wird aber nur für getLocalTime() genutzt, nicht für den NTPClient. Eine konsequente Lösung wäre, ganz auf getLocalTime() zu setzen und den NTPClient nur zur Erstinitialisierung zu verwenden.
Die OpenWeatherMap-API liefert in der kostenlosen Stufe ausreichend Abfragen (60 pro Minute, 1 Million pro Monat). Ein Abruf alle zehn Minuten ist unkritisch. Der API-Key muss nach der Registrierung einige Minuten aktiviert werden – ein häufiger Stolperstein.
Display 2: RSSI-Werte von drei WLAN-Netzen
Das zweite Display ist technisch das unkomplizierteste, weil es keine externe API benötigt. Der ESP32 scannt die Umgebung, filtert die drei Zielnetzwerke heraus und stellt deren Signalstärke grafisch dar.
Der Beispielsketch
cpp
/*
* CYD Display 2 – WLAN-Signalmonitor
* Zeigt RSSI von drei konfigurierbaren Netzwerken
*/
#include <TFT_eSPI.h>
#include <WiFi.h>
TFT_eSPI tft = TFT_eSPI();
// ---- Zu überwachende Netzwerke ----
const int ANZAHL_NETZE = 3;
const char* ZIEL_NETZE[ANZAHL_NETZE] = {
"FRITZ!Box 7590",
"WLAN-Gast",
"Nachbar_AP"
};
int rssiWerte[ANZAHL_NETZE];
bool netzGefunden[ANZAHL_NETZE];
#define TFT_BL 21
const unsigned long SCAN_INTERVALL_MS = 5000;
unsigned long letzterScan = 0;
// Farbverlauf für Signalstärke
uint16_t rssiFarbe(int rssi) {
if (rssi >= -50) return TFT_GREEN;
if (rssi >= -65) return TFT_YELLOW;
if (rssi >= -80) return TFT_ORANGE;
return TFT_RED;
}
void setup() {
Serial.begin(115200);
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH);
tft.init();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
WiFi.mode(WIFI_STA);
WiFi.disconnect(); // nicht verbinden, nur scannen
for (int i = 0; i < ANZAHL_NETZE; i++) {
rssiWerte[i] = -100;
netzGefunden[i] = false;
}
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextSize(2);
tft.setCursor(10, 10);
tft.println("WLAN-Monitor");
}
void loop() {
if (millis() - letzterScan > SCAN_INTERVALL_MS || letzterScan == 0) {
scanDurchfuehren();
letzterScan = millis();
anzeigeZeichnen();
}
delay(100);
}
void scanDurchfuehren() {
Serial.println("Starte WLAN-Scan...");
int gefundene = WiFi.scanNetworks();
// Zähler zurücksetzen
for (int i = 0; i < ANZAHL_NETZE; i++) {
netzGefunden[i] = false;
}
for (int i = 0; i < gefundene; i++) {
String ssid = WiFi.SSID(i);
int rssi = WiFi.RSSI(i);
for (int j = 0; j < ANZAHL_NETZE; j++) {
if (ssid == ZIEL_NETZE[j]) {
rssiWerte[j] = rssi;
netzGefunden[j] = true;
}
}
}
WiFi.scanDelete();
}
void anzeigeZeichnen() {
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextSize(2);
tft.setCursor(10, 10);
tft.println("WLAN-Signalstaerke");
for (int i = 0; i < ANZAHL_NETZE; i++) {
int y = 50 + i * 60;
// Netzwerkname
tft.setTextSize(1);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setCursor(10, y);
tft.print(ZIEL_NETZE[i]);
// Wert
tft.setCursor(10, y + 15);
if (netzGefunden[i]) {
tft.setTextColor(rssiFarbe(rssiWerte[i]), TFT_BLACK);
tft.print(rssiWerte[i]);
tft.print(" dBm");
} else {
tft.setTextColor(TFT_DARKGREY, TFT_BLACK);
tft.print("nicht gefunden");
}
// Balken
balkenZeichnen(150, y + 5, rssiWerte[i], netzGefunden[i]);
}
}
void balkenZeichnen(int x, int y, int rssi, bool gefunden) {
const int MAX_BREITE = 150;
const int HOEHE = 20;
// Rahmen
tft.drawRect(x, y, MAX_BREITE, HOEHE, TFT_WHITE);
if (!gefunden) return;
// RSSI (-100 bis -30 dBm) auf Breite abbilden
int breite = map(rssi, -100, -30, 0, MAX_BREITE - 4);
breite = constrain(breite, 0, MAX_BREITE - 4);
tft.fillRect(x + 2, y + 2, breite, HOEHE - 4, rssiFarbe(rssi));
}
Interpretation der Werte
Die folgende Tabelle hilft bei der Einordnung der gemessenen RSSI-Werte:
| RSSI (dBm) | Qualität | Typische Situation |
|---|---|---|
| -30 bis -50 | Ausgezeichnet | Sichtverbindung, wenige Meter |
| -50 bis -65 | Gut | Normale Nutzung im selben Raum |
| -65 bis -75 | Brauchbar | Ein bis zwei Wände dazwischen |
| -75 bis -85 | Schwach | Mehrere Wände, Streaming kritisch |
| -85 bis -100 | Sehr schwach | Verbindungsabbrüche wahrscheinlich |
Ein wichtiger Hinweis: Der ESP32 kann im Station-Modus nicht gleichzeitig scannen und mit einem Netzwerk verbunden sein, ohne die Verbindung kurz zu unterbrechen. Für ein reines Monitoring-Display ist das unproblematisch. Wer parallel eine WLAN-Verbindung für andere Zwecke benötigt, sollte den Scan-Intervall großzügig wählen.
Display 3: Google-Kalender-Daten
Das dritte Display ist das anspruchsvollste. Die offizielle Google Calendar API ist für den ESP32 ungeeignet. Der pragmatische Weg führt über einen ICS-Feed oder einen selbst gehosteten JSON-Dienst.
Variante A: ICS-Feed direkt parsen
Jeder Google Kalender bietet in den Einstellungen unter „Kalender integrieren“ eine geheime ICS-Adresse. Diese kann der ESP32 per HTTPS abrufen und grob parsen.
cpp
/*
* CYD Display 3 – Kalender (ICS-Variante)
* Liest einen öffentlichen ICS-Feed und zeigt die nächsten Termine
*/
#include <TFT_eSPI.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>
TFT_eSPI tft = TFT_eSPI();
const char* WLAN_SSID = "DEIN_WLAN";
const char* WLAN_PASSWORT = "DEIN_PASSWORT";
// Geheime ICS-Adresse aus Google Kalender-Einstellungen
const char* ICS_URL = "https://calendar.google.com/calendar/ical/DEINE_KALENDER_ID/basic.ics";
#define TFT_BL 21
const unsigned long KALENDER_INTERVALL_MS = 900000; // 15 Minuten
unsigned long letzteAbfrage = 0;
struct Termin {
String start;
String titel;
};
const int MAX_TERMINE = 5;
Termin termine[MAX_TERMINE];
int anzahlTermine = 0;
void setup() {
Serial.begin(115200);
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH);
tft.init();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextSize(2);
tft.setCursor(10, 10);
tft.println("Kalender");
WiFi.begin(WLAN_SSID, WLAN_PASSWORT);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWLAN verbunden.");
}
void loop() {
if (millis() - letzteAbfrage > KALENDER_INTERVALL_MS || letzteAbfrage == 0) {
if (WiFi.status() == WL_CONNECTED) {
kalenderAbrufen();
anzeigeZeichnen();
}
letzteAbfrage = millis();
}
delay(1000);
}
void kalenderAbrufen() {
WiFiClientSecure client;
client.setInsecure(); // Vereinfachung – für Produktion Zertifikat setzen!
HTTPClient http;
http.begin(client, ICS_URL);
int httpCode = http.GET();
if (httpCode != 200) {
Serial.print("HTTP-Fehler: ");
Serial.println(httpCode);
http.end();
return;
}
String ics = http.getString();
http.end();
// Grobes Parsen: nach VEVENT-Blöcken suchen
anzahlTermine = 0;
int pos = 0;
while (anzahlTermine < MAX_TERMINE) {
int startIdx = ics.indexOf("BEGIN:VEVENT", pos);
if (startIdx < 0) break;
int endIdx = ics.indexOf("END:VEVENT", startIdx);
if (endIdx < 0) break;
String block = ics.substring(startIdx, endIdx);
// DTSTART parsen
int dtStartIdx = block.indexOf("DTSTART");
String startWert = "";
if (dtStartIdx >= 0) {
int doppelpunkt = block.indexOf(':', dtStartIdx);
int zeilenende = block.indexOf('\n', doppelpunkt);
startWert = block.substring(doppelpunkt + 1, zeilenende);
startWert.trim();
}
// SUMMARY parsen
int sumIdx = block.indexOf("SUMMARY:");
String titel = "";
if (sumIdx >= 0) {
int zeilenende = block.indexOf('\n', sumIdx);
titel = block.substring(sumIdx + 8, zeilenende);
titel.trim();
}
if (startWert.length() > 0) {
termine[anzahlTermine].start = formatIcsDatum(startWert);
termine[anzahlTermine].titel = titel;
anzahlTermine++;
}
pos = endIdx + 10;
}
Serial.print("Termine gefunden: ");
Serial.println(anzahlTermine);
}
String formatIcsDatum(String icsDatum) {
// ICS-Format: 20260315T143000Z oder 20260315
// Ausgabe: 15.03. 14:30
icsDatum.trim();
if (icsDatum.length() < 8) return icsDatum;
String tag = icsDatum.substring(6, 8);
String monat = icsDatum.substring(4, 6);
String jahr = icsDatum.substring(0, 4);
String zeit = "";
if (icsDatum.length() >= 13) {
zeit = icsDatum.substring(9, 11) + ":" + icsDatum.substring(11, 13);
}
String ergebnis = tag + "." + monat + ".";
if (zeit.length() > 0) ergebnis += " " + zeit;
return ergebnis;
}
void anzeigeZeichnen() {
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextSize(2);
tft.setCursor(10, 10);
tft.println("Naechste Termine:");
tft.setTextSize(1);
int y = 45;
if (anzahlTermine == 0) {
tft.setCursor(10, y);
tft.println("Keine Termine gefunden.");
return;
}
for (int i = 0; i < anzahlTermine; i++) {
tft.setTextColor(TFT_CYAN, TFT_BLACK);
tft.setCursor(10, y);
tft.print(termine[i].start);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setCursor(10, y + 12);
String titel = termine[i].titel;
if (titel.length() > 40) titel = titel.substring(0, 40) + "...";
tft.print(titel);
y += 35;
if (y > 220) break;
}
}
Variante B: JSON-Zwischendienst
Die ICS-Variante ist einfach, hat aber Grenzen: Sie sortiert Termine nicht nach Aktualität und stürzt sich auf den ersten Block im Feed, der nicht unbedingt der nächste Termin ist. Wer mehr Kontrolle möchte, betreibt auf einem Raspberry Pi oder einem kleinen Heimserver ein Python-Skript, das die Google Calendar API nutzt und die nächsten Termine als schlankes JSON bereitstellt. Der ESP32 ruft dann nur noch diese eine URL ab.
Ein solches Zwischenskript könnte mit der google-api-python-client-Bibliothek und einem Service-Account-Konto arbeiten. Der ESP32-Seitencode reduziert sich dann auf einen HTTP-Abruf und eine JSON-Verarbeitung, wie sie bereits im Wetter-Display gezeigt wurde.
Stromversorgung der drei Displays
Die gemeinsame 5-Volt-Versorgung ist elektrisch unkompliziert, verlangt aber Aufmerksamkeit bei der Dimensionierung. Die folgende Tabelle fasst den Bedarf zusammen:
| Zustand | Strom pro CYD | Strom für 3 CYDs |
|---|---|---|
| Nur Display, Backlight an | ca. 150 mA | ca. 450 mA |
| WLAN aktiv, Empfang | ca. 220 mA | ca. 660 mA |
| WLAN-Scan (Spitzen) | ca. 300 mA | ca. 900 mA |
Ein Netzteil mit 5 V und mindestens 2 A bietet ausreichend Reserve. Wichtig ist, dass alle drei Boards parallel an dieselbe 5-V-Schiene angeschlossen werden – nicht in Reihe. Die VIN- beziehungsweise 5V-Pins der Boards werden miteinander verbunden, ebenso alle GND-Pins. Die 3,3-V-Pins dürfen nicht für die Versorgung genutzt werden.
Ein Hinweis zur Wärmeentwicklung: Der AMS1117-Spannungsregler auf dem CYD erwärmt sich bei 5 V Eingangsspannung spürbar. Bei drei Boards in einem geschlossenen Gehäuse sollte für Luftzirkulation gesorgt werden.
Fazit und Ausblick
Die drei vorgestellten Beispielsketches zeigen, dass jedes der geplanten Displays mit überschaubarem Codeaufwand realisierbar ist – vorausgesetzt, man akzeptiert die Grenzen des ESP32 und sucht keine Lösungen, die für leistungsfähigere Hardware gedacht sind. Das Wetter-Display ist mit NTP und OpenWeatherMap in einer halben Stunde zum Laufen zu bringen. Der WLAN-Monitor ist noch simpler. Der Kalender ist der anspruchsvollste Teil, und hier entscheidet die Wahl des Datenwegs über den Aufwand: Der ICS-Feed ist schnell eingerichtet, aber in der Auswertung grob. Der JSON-Umweg über einen eigenen Server kostet mehr Einrichtungszeit, liefert aber saubere Daten.
Für die Zukunft lohnt sich ein Blick auf die LVGL-Bibliothek. Sie bietet fertige Widgets und eine deutlich komfortablere Oberflächengestaltung als die direkte Pixelarbeit mit TFT_eSPI. Der Preis ist ein höherer Speicherbedarf – hier stößt der ESP32-WROOM-32 ohne PSRAM schnell an seine Grenzen. Wer mehr möchte, sollte beim nächsten CYD-Kauf auf eine Variante mit ESP32-S3 achten.
Die Dokumentation der Hardware-Konfiguration, die diesem Projekt zugrunde liegt, bleibt gültig. Der hier ergänzte Code ist als Ausgangspunkt gedacht: Er läuft, wenn die WLAN-Zugangsdaten und API-Schlüssel eingetragen sind, und er lässt sich modular erweitern. Wer alle drei Displays nebeneinander betreibt, hat am Ende ein kleines Informationssystem, das ohne Cloud-Zwang und ohne Abonnement funktioniert – und das ist bei aller Bastelarbeit ein durchaus befriedigendes Ergebnis.
Quellen:
- witnessmenow: ESP32-Cheap-Yellow-Display – GitHub-Repository mit Hardware-Dokumentation und Beispielprojekten
- Bodmer: TFT_eSPI – Bibliotheksdokumentation und User_Setup-Konfiguration
- OpenWeatherMap: Aktuelle Wetterdaten-API, Dokumentation und Preisgestaltung
- NTPClient-Bibliothek von Fabrice Weinberg – Zeitsynchronisation für Arduino/ESP32
- ArduinoJson von Benoît Blanchon – JSON-Verarbeitung auf Mikrocontrollern
- Google Calendar Hilfe: „Kalender integrieren“ – ICS-Feed-Adressen und deren Verwendung
- Random Nerd Tutorials: ESP32 NTP-Client und Wetterstation-Projekte
Post Comment