ESP32: TFT-Display mit Touch-Funktion (CYD) Teil 1: verstehen & nutzen

In der Bastler- und Maker-Szene werden oft nur einfache Display- und Touch-Lösungen benötigt. Doch dieses günstigste Entwicklungsboard ESP32-2432S028 mit 2,8 Zoll Display das in der Maker-Szene auch als Cheap Yellow Display (CYD) bekannt ist und es gibt weitere mit unterschiedlichen ESP32 Varianten und Displaygrößen. Weiter unten Lösung für 3,5Zoll Display

Es basiert auf einem ESP32-WROOM-Modul mit 4 MB Flash, das auf einer auffällig gelben Platine sitzt. Verbaut ist ein 2,8-Zoll Touchdisplay mit 320 × 240 Pixeln, das sich ideal für einfache grafische Benutzeroberflächen eignet.

Das Cheap Yellow Display Board ist wie folgt ausgestattet:

  • Resistiver Touchscreen mit ILI9341-TFT & XPT2046-Touch
  • microSD-Kartenslot
  • LDR (Helligkeitssensor)
  • RGB-LED (GPIO-Pins: 4, 16, 17)
  • Lautsprecheranschluss
  • Boot-Taster
  • JST-Anschlüsse (1,25 mm, für Sensoren/Peripherie)
  • USB (Micro-USB und/oder USB-C) zur Stromversorgung & Programmierung
  • WLAN/Bluetooth

Detaillierte Hersteller Informationen habe ich nicht gefunden, dennoch gibt es eine Vielzahl von deckungsgleichen Dokumenten die bei der Umsetzung hilfreich sind.

Weitere Details: https://randomnerdtutorials.com/



Projektübersicht: LED Steuerung & Temperaturanzeige

Für den einfachen Einstieg wird in diesem Projekt ein DHT22-Temperatursensor angeschlossen, dessen Messwerte direkt auf dem Display angezeigt werden.
Zusätzlich soll die integrierte RGB-LED über das Touchdisplay ein- und ausgeschaltet werden können.

Hardware:

ESP32-2432S028R oder ESP32 2,8 Zoll Smart Display V3
DHT11 oder DHT11 Sensor (Für Temperatur-/Feuchtigkeits-Messung)

ACHTUNG: Ich habe hier die Resistive Version des Boards vor mir liegen.
Zu erkennen an dem letzten R in der Typenbezeichnung: ESP32-2432S028R
Diese Version hat ein Touchscreen das den XPT2046-Chip nutzt, über SPI-Kommunikation (Serial Peripheral Interface).
Es gibt noch eine C-Version (Kapazitiv) die den GT911-Chip für die Touch nutzt über I2C-Bus (TAMC_GT911-Bibliothek)

Also nicht verwechseln


Entwicklungsumgebung: Für dieses Beispiel nutzen wir die klassische Arduino‑IDE.

Vorgehensweise: Direkte Programmierung mit TFT_eSPI & TouchScreen.h

Die ersten Schritte sind bewusst minimal gehalten, um das Board sicher zu testen:

  1. Display-Test
    Prüfen, ob das TFT korrekt initialisiert und Text ausgeben kann.
  2. Sensor-Test (DHT11)
    Den DHT11 anschließen und sicherstellen, dass Temperaturwerte auf dem Display erscheinen.
  3. Touch-Button + RGB‑LED
    Eine einfache Schaltfläche zeichnen und testen, ob die RGB‑LED per Touch ein‑ und ausgeschaltet werden kann.

Vorbereitung: Benötigte Bibliothek installieren und anpassen.

Eine Bibliothek zur Ansteuerung von TFT-Displays (Thin-Film-Transistor).
Kann über den Bibliotheksmanager installiert werden (Suche nach „TFT_eSPI“)

Nun muss die Datei User_Setup.h der gerade installierten Bibliothek TFT_eSPI für das CYD angepasst werden:

1. Richtige Pin-Zuweisungen für SPI und Touchscreen:

TFT_MISO 12
TFT_MOSI 13
TFT_SCLK 14
TFT_CS 15
TFT_DC 2
TFT_RST -1
TOUCH_CS 33

2. Aktiviere den passenden Display-Treiber: #define ILI9341_2_DRIVER und deaktiviere andere Display-Treiber

3. Richtige Displaygröße: TFT_WIDTH 240, TFT_HEIGHT 320

Also das sollte dann in der User_Setup.h drin stehen, die im Ordner C:\ordner deiner sketche\libraries\TFT_eSPI\ zufinden ist:

klicken zum Aufklappen
// Konfiguration für das ESP32-2432S028R (Cheap Yellow Display - CYD)
#define USER_SETUP_INFO "User_Setup_CYD"

// 1. Treiber und Display-Auflösung
#define ILI9341_2_DRIVER
#define TFT_WIDTH  240
#define TFT_HEIGHT 320

// 2. Pin-Belegung
#define TFT_MISO 12
#define TFT_MOSI 13
#define TFT_SCLK 14
#define TFT_CS   15  // Chip Select
#define TFT_DC   2   // Data Command
#define TFT_RST  -1  // Reset (wird direkt vom ESP32 gesteuert)

#define TFT_BL   21  // Backlight (Hintergrundbeleuchtung)
#define TFT_BACKLIGHT_ON HIGH

#define TOUCH_CS 33  // Chip Select für den Touchscreen

// 3. Schriftarten (Fonts) laden
#define LOAD_GLCD
#define LOAD_FONT2
#define LOAD_FONT4
#define LOAD_FONT6
#define LOAD_FONT7
#define LOAD_FONT8
#define LOAD_GFXFF
#define SMOOTH_FONT

// 4. Geschwindigkeiten für die serielle Peripherieschnittstelle (SPI - Serial Peripheral Interface)
#define SPI_FREQUENCY  55000000
#define SPI_READ_FREQUENCY  20000000
#define SPI_TOUCH_FREQUENCY  2500000

// 5. Weitere Optionen
#define USE_HSPI_PORT

Einfache Display Text Ausgabe

Sketch (Zum aufklappen anklicken)
#include <TFT_eSPI.h>

TFT_eSPI tft = TFT_eSPI(); // Initialisiert das TFT-Display-Objekt

void setup() {
  // WICHTIG für das CYD: Hintergrundbeleuchtung aktivieren
  pinMode(21, OUTPUT);
  digitalWrite(21, HIGH);

  // Startet das TFT-Display
  tft.init();
  tft.setRotation(1); // Setzt das Display ins Querformat (320 x 240 Bildpunkte)
  
  // Löscht den Bildschirm (setzt alles auf Schwarz)
  tft.fillScreen(TFT_BLACK);

  // 1. Einen zweifarbigen Rahmen um den gesamten Bildschirm zeichnen
  // drawRect(x, y, breite, höhe, farbe)
  tft.drawRect(0, 0, 320, 240, TFT_CYAN);
  tft.drawRect(2, 2, 316, 236, TFT_BLUE);

  // 2. Begrüßung in mittlerer Schrift (Font 4) und Gelb
  tft.setTextColor(TFT_YELLOW, TFT_BLACK); 
  // drawCentreString platziert den Text exakt in der Mitte der X-Achse (bei 320 Pixeln Breite ist das 160)
  tft.drawCentreString("Hallo Bastler & Tueftler!", 160, 20, 4); 

  // 3. Eine dekorative rote Trennlinie zeichnen
  // drawLine(start_x, start_y, ende_x, ende_y, farbe)
  tft.drawLine(20, 60, 300, 60, TFT_RED);

  // 4. Normaler Text (Font 2) in Weiß
  tft.setTextColor(TFT_WHITE, TFT_BLACK); 
  tft.drawCentreString("Willkommen auf", 160, 80, 2);

  // 5. Der Blog-Name hervorgehoben in leuchtendem Grün
  tft.setTextColor(TFT_GREEN, TFT_BLACK);
  // Mit Sonderzeichen wie Klammern oder Pfeilen kann man den Text optisch einrahmen
  tft.drawCentreString(">>> prilchen.de <<<", 160, 110, 4);

  // 6. Ein kleiner Status-Text (Font 2) in Orange
  tft.setTextColor(TFT_ORANGE, TFT_BLACK);
  tft.drawCentreString("Hardware-Test bestanden! [OK]", 160, 160, 2);
  
  // 7. Ein paar grafische Spielereien: Gefüllte Kreise
  // fillCircle(x, y, radius, farbe)
  tft.fillCircle(60, 210, 10, TFT_MAGENTA);
  tft.fillCircle(160, 210, 10, TFT_CYAN);
  tft.fillCircle(260, 210, 10, TFT_YELLOW);
}

void loop() {
  // Der Hauptcode, der wiederholt ausgeführt wird.
  // Hier bleibt es erstmal leer, da sich unser Bildausschnitt nicht verändert.
}


Als Board wählst du ESP32 Dev Module und beim Upload ggf. die Boot Taste drücken.

Der Text ist zu sehen, aber erst mal nicht spannend.




Nun mal was auslesen und steuern:

DHT11 anschießen, Temperaturwert auf dem Display erscheint und die LED schalten

Benötigte Bibliothek:
TFT_eSPI
XPT2046_Touchscreen
DHT11

Verdrahtung:

Beiliegendes 4 adriges Adapterkabel nehmen und an Anschluss IO2:4P1.25mm anklemmen.
Ein DHT11 an Plus an Rot, Minus an Schwarz und Daten an Gelb anschließen. Blaues Kabel bleibt frei.

Arduino Sketch:

Sketch (Zum aufklappen anklicken)
#include <TFT_eSPI.h>                  // Bibliothek für das TFT-Display (Thin-Film-Transistor)
#include <XPT2046_Touchscreen.h>       // Bibliothek für den Touchscreen
#include <DHT.h>                       // Bibliothek für den DHT11-Sensor (Digital Humidity and Temperature)

// DHT11 Sensor Setup
#define DHTPIN 27
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);

// Touchscreen Setup
#define TOUCH_CS 33
#define TOUCH_IRQ 36
#define TOUCH_CLK 25
#define TOUCH_MISO 39
#define TOUCH_MOSI 32

SPIClass touchscreenSPI = SPIClass(VSPI);
XPT2046_Touchscreen ts(TOUCH_CS, TOUCH_IRQ);

// TFT-Display Setup
TFT_eSPI tft = TFT_eSPI();

// GPIO-Pins (General Purpose Input/Output) der verbauten RGB-LED (Red, Green, Blue)
// Hinweis: Diese LEDs leuchten, wenn der Pin auf LOW (0 Volt) geschaltet wird (aktiv-low)
#define LED_R 4
#define LED_G 16
#define LED_B 17

bool ledState = false; // Speichert, ob die LED gerade an oder aus ist
unsigned long lastSensorUpdate = 0; // Merker für die Zeitsteuerung

void setup() {
  Serial.begin(115200);
  dht.begin();

  // WICHTIG: Hintergrundbeleuchtung des CYD einschalten
  pinMode(21, OUTPUT);
  digitalWrite(21, HIGH);

  // Touchscreen initialisieren
  touchscreenSPI.begin(TOUCH_CLK, TOUCH_MISO, TOUCH_MOSI, TOUCH_CS);
  ts.begin(touchscreenSPI);
  ts.setRotation(1); // Querformat

  // LED-Pins (Light Emitting Diode) als Ausgang setzen
  pinMode(LED_R, OUTPUT);
  pinMode(LED_G, OUTPUT);
  pinMode(LED_B, OUTPUT);
  updateLED(); // Schaltet die LED beim Start sicher aus

  // Display initialisieren
  tft.init();
  tft.setRotation(1); // Querformat (320 x 240 Bildpunkte)
  tft.fillScreen(TFT_BLACK);

  // --- Grafisches Grundgerüst zeichnen (wird nur 1x im Setup gemacht) ---
  
  // 1. Kopfzeile mit abgerundeten Ecken
  // fillRoundRect(x, y, breite, höhe, radius, farbe)
  tft.fillRoundRect(10, 10, 300, 40, 8, TFT_DARKCYAN);
  tft.setTextColor(TFT_WHITE, TFT_DARKCYAN); 
  tft.drawCentreString("Prilchen.de - Sensor-Station", 160, 20, 2); // Zentriert mit Font 2

  // 2. Optische Rahmen für die Messwerte
  tft.drawRoundRect(10, 60, 300, 100, 8, TFT_DARKGREY);
  
  // 3. Feste Beschriftungen (werden nicht mehr überschrieben)
  tft.setTextColor(TFT_LIGHTGREY, TFT_BLACK);
  tft.drawString("Temperatur:", 25, 75, 2);
  tft.drawString("Luftfeuchte:", 25, 115, 2);

  // 4. Den Button (Schaltfläche) zum ersten Mal zeichnen
  drawButton();
}

void loop() {
  unsigned long currentMillis = millis();

  // Temperaturanzeige alle 2000 ms (2 Sekunden) aktualisieren
  // Der DHT11 ist recht langsam, 2 Sekunden Pause sind ideal
  if (currentMillis - lastSensorUpdate >= 2000) {
    lastSensorUpdate = currentMillis;

    float temp = dht.readTemperature();
    float hum = dht.readHumidity();

    // Wir setzen TextPadding (Hintergrund-Füllung), um flimmerfrei zu überschreiben.
    // Die Bibliothek füllt automatisch 80 Bildpunkte Breite in der Hintergrundfarbe.
    tft.setTextPadding(80); 

    // Temperatur in Gelb anzeigen
    tft.setTextColor(TFT_YELLOW, TFT_BLACK);
    String tempStr = String(temp, 1) + " C"; // Kommazahl auf 1 Stelle runden
    tft.drawString(tempStr, 180, 70, 4); 

    // Feuchtigkeit in Hellblau anzeigen
    tft.setTextColor(TFT_CYAN, TFT_BLACK);
    String humStr = String(hum, 1) + " %";
    tft.drawString(humStr, 180, 110, 4); 
    
    tft.setTextPadding(0); // Padding wieder ausschalten, damit andere Texte nicht gestört werden
  }

  // Touchscreen auswerten
  if (ts.tirqTouched() && ts.touched()) {
    TS_Point p = ts.getPoint();

    // Koordinaten an das Display-Raster (320x240) anpassen
    int x = map(p.x, 200, 3700, 0, 320);
    int y = map(p.y, 240, 3800, 0, 240);

    // Entprellung: Nur alle 300 Millisekunden einen Touch-Druck zulassen
    static unsigned long lastTouch = 0;
    
    // Prüfen, ob der Touch innerhalb unseres gezeichneten Buttons lag
    if (x >= 80 && x <= 240 && y >= 170 && y <= 230 && millis() - lastTouch > 300) {
      lastTouch = millis();
      ledState = !ledState; // Zustand umkehren (aus AN wird AUS und umgekehrt)
      updateLED();          // LED hardwareseitig schalten
      drawButton();         // Button auf dem Display neu zeichnen
    }
  }
}

// Funktion, um die Schaltfläche optisch ansprechend zu zeichnen
void drawButton() {
  // Wenn LED an ist -> Roter Button ("Ausschalten"). Wenn LED aus ist -> Grüner Button ("Einschalten").
  uint16_t buttonColor = ledState ? TFT_RED : TFT_DARKGREEN;
  
  // Einen großen, abgerundeten Button zeichnen
  tft.fillRoundRect(80, 175, 160, 50, 10, buttonColor);
  
  // Einen weißen Rand als "Highlight" hinzufügen
  tft.drawRoundRect(80, 175, 160, 50, 10, TFT_WHITE);

  // Text zentriert auf dem Button platzieren
  tft.setTextColor(TFT_WHITE, buttonColor);
  if (ledState) {
    tft.drawCentreString("LED AUS", 160, 190, 4);
  } else {
    tft.drawCentreString("LED AN", 160, 190, 4);
  }
}

// Funktion, um die Hardware-LED zu steuern
void updateLED() {
  if (ledState) {
    // LED einschalten (Wir nutzen hier als Beispiel nur die rote LED)
    digitalWrite(LED_R, LOW);   // LOW bedeutet bei dieser LED: AN
    digitalWrite(LED_G, HIGH);  // HIGH bedeutet: AUS
    digitalWrite(LED_B, HIGH);
  } else {
    // Alle Farbkanäle ausschalten
    digitalWrite(LED_R, HIGH);  
    digitalWrite(LED_G, HIGH);
    digitalWrite(LED_B, HIGH);
  }
}

Ergebnis:

Temperaturwerte werden aktuell angezeigt und LED auf der Rückseite lässt sich per Touch auf dem Display schalten.


3.5 Zoll CYD Varianten mit optionalen Akku Anschluss:

Wer einen der größeren Varianten der CYD vorliegen hat, wie zum Beispiel diesen 3,5 Zoll ESP32-3248S035R, muss einige Dinge ändern:

Unterschiede zur 2,8 Zoll Version:

CYD 2,8 ZollCYD 3,5 Zoll
Backlight Pin 21Pin 27
TFT_WIDTH240320
TFT_HEIGHT 320480
Display TreiberILI9341ST7796
Touch TreiberXPT2046 über SPIGT911 über I²C
RGB‑LEDGPIO 4, 16, 17GPIO 22, 16, 17
SPI_FREQUENCY4000000065000000
DHT Anschluss2732

Anpassung 1: Somit hier die angepasste User_Setup.h in der TFT_eSPI Library

User_Setup.h für CYD 3,5 Zoll: (Zum aufklappen anklicken)
// Konfiguration für das ESP32-3248S035R (Cheap Yellow Display - CYD)
#define USER_SETUP_INFO "User_Setup_CYD"

// 1. Treiber und Display-Auflösung
#define ST7796_DRIVER
#define TFT_WIDTH  320
#define TFT_HEIGHT 480

// 2. Pin-Belegung
#define TFT_MISO 12
#define TFT_MOSI 13
#define TFT_SCLK 14
#define TFT_CS   15  // Chip Select
#define TFT_DC   2   // Data Command
#define TFT_RST  -1  // Reset (wird direkt vom ESP32 gesteuert)

#define TFT_BL   27  // Backlight (Hintergrundbeleuchtung)
#define TFT_BACKLIGHT_ON HIGH

#define TOUCH_CS 33  // Chip Select für den Touchscreen

// 3. Schriftarten (Fonts) laden
#define LOAD_GLCD
#define LOAD_FONT2
#define LOAD_FONT4
#define LOAD_FONT6
#define LOAD_FONT7
#define LOAD_FONT8
#define LOAD_GFXFF
#define SMOOTH_FONT

// 4. Geschwindigkeiten für die serielle Peripherieschnittstelle (SPI - Serial Peripheral Interface)
#define SPI_FREQUENCY  65000000
#define SPI_READ_FREQUENCY  20000000
#define SPI_TOUCH_FREQUENCY  2500000

// 5. Weitere Optionen
#define USE_HSPI_PORT


Anpassung 2: Anpassung im Arduino Sketch für CYD 3,5 Zoll. TFT_eSPI hat eine eingebaute GT911‑Touch‑Integration und somit wird keine weitere Library benötigt:

Sketch (Zum aufklappen anklicken)
#include <TFT_eSPI.h>                  // Bibliothek für das TFT-Display (Thin-Film-Transistor)
#include <DHT.h>                       // Bibliothek für den DHT11-Sensor

// DHT11 Sensor Setup
#define DHTPIN 32 
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);

// TFT-Display Setup (steuert jetzt auch den Touchscreen!)
TFT_eSPI tft = TFT_eSPI();

// GPIO-Pins der verbauten RGB-LED
#define LED_R 22 
#define LED_G 16
#define LED_B 17

bool ledState = false; 
unsigned long lastSensorUpdate = 0; 

void setup() {
  Serial.begin(115200);
  dht.begin();

  // Hintergrundbeleuchtung (Pin 27)
  pinMode(27, OUTPUT);
  digitalWrite(27, HIGH);

  // LED-Pins als Ausgang setzen
  pinMode(LED_R, OUTPUT);
  pinMode(LED_G, OUTPUT);
  pinMode(LED_B, OUTPUT);
  updateLED();

  // Display und Touch initialisieren (alles in einem Abwasch!)
  tft.init();
  tft.setRotation(1); // Querformat (480 x 320)
  tft.fillScreen(TFT_BLACK);

  // Kalibrierung des Touchscreens (WICHTIG!)
  // Diese Werte passen die rohen Touch-Daten an das 480x320 Display an
  uint16_t calData[5] = { 250, 3600, 250, 3600, 1 }; 
  tft.setTouch(calData);

  // --- Grafisches Grundgerüst zeichnen ---
  tft.fillRoundRect(10, 10, 460, 40, 8, TFT_DARKCYAN);
  tft.setTextColor(TFT_WHITE, TFT_DARKCYAN); 
  tft.drawCentreString("Prilchen.de - Sensor-Station", 240, 20, 2);

  tft.drawRoundRect(10, 60, 460, 120, 8, TFT_DARKGREY);
  
  tft.setTextColor(TFT_LIGHTGREY, TFT_BLACK);
  tft.drawString("Temperatur:", 50, 85, 4);  
  tft.drawString("Luftfeuchte:", 50, 135, 4);

  drawButton();
}

void loop() {
  unsigned long currentMillis = millis();

  // Temperaturanzeige alle 2000 ms
  if (currentMillis - lastSensorUpdate >= 2000) {
    lastSensorUpdate = currentMillis;
    float temp = dht.readTemperature();
    float hum = dht.readHumidity();
    
    tft.setTextPadding(120); 
    tft.setTextColor(TFT_YELLOW, TFT_BLACK);
    String tempStr = String(temp, 1) + " C";
    tft.drawString(tempStr, 280, 85, 4); 

    tft.setTextColor(TFT_CYAN, TFT_BLACK);
    String humStr = String(hum, 1) + " %";
    tft.drawString(humStr, 280, 135, 4); 
    tft.setTextPadding(0);
  }

  // --- Touchscreen auswerten mit TFT_eSPI ---
  uint16_t x, y;
  
  if (tft.getTouch(&x, &y)) {
    
    y = 320 - y;
    
    Serial.print("Touch X: "); Serial.print(x);
    Serial.print(" | Y (korrigiert): "); Serial.println(y);

    static unsigned long lastTouch = 0;
    
    if (x >= 140 && x <= 340 && y >= 220 && y <= 290 && millis() - lastTouch > 300) {
      lastTouch = millis();
      ledState = !ledState; 
      updateLED();          
      drawButton();         
    }
  }
}

void drawButton() {
  uint16_t buttonColor = ledState ? TFT_RED : TFT_DARKGREEN;
  tft.fillRoundRect(140, 220, 200, 70, 10, buttonColor);
  tft.drawRoundRect(140, 220, 200, 70, 10, TFT_WHITE);
  tft.setTextColor(TFT_WHITE, buttonColor);
  if (ledState) {
    tft.drawCentreString("LED AUS", 240, 240, 4);
  } else {
    tft.drawCentreString("LED AN", 240, 240, 4);
  }
}

void updateLED() {
  if (ledState) {
    digitalWrite(LED_R, LOW);   
    digitalWrite(LED_G, HIGH);  
    digitalWrite(LED_B, HIGH);
  } else {
    digitalWrite(LED_R, HIGH);  
    digitalWrite(LED_G, HIGH);
    digitalWrite(LED_B, HIGH);
  }
}


Verkabelung: Anschluss des DHT11:

Pinbelegung und weitere Details: 3.5inch ESP32-32E Display – LCD wiki

Installation: Adafruit-ST7735-Library

Ergebnis:

Beim Antippen des Button geht auf der Rückseite tatsächlich die LED in Rot an und auf dem Display ist dann der Button mit „LED aus“ mit roten Hintergrund anklickbar, um beim weiteren anklicken die LED auch wieder auszuschalten.


CYD mit 3,5 Zoll mit externen Wetterdaten als Wetterstation

Als abschließendes Beispiel für die direkte Programmierung mit der Library TFT_eSPI zeige ich, wie ich die Netzwerkfunktionen des CYD nutze: Wie im Projekt smart-weather-e-paper-display gezeigt, werden über Open‑Meteo lokale Wetterdaten abgerufen und grafisch auf dem Display dargestellt.

Um die Funktion der einzelnen Abschnitte vorab besser zu verstehen, hier nur erstmal der Code nur einem Wert, mit Temperatur:

Quellcode hier aufklappen
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <TFT_eSPI.h>

// WLAN-Zugangsdaten (SSID = Service Set Identifier)
const char* ssid = "DEIN_WLAN_NAME";
const char* password = "DEIN_WLAN_PASSWORT";

// Open-Meteo API URL (Koordinaten: 50.91, 6.81)
String apiUrl = "http://api.open-meteo.com/v1/forecast?latitude=50.91&longitude=6.81&current_weather=true";

// TFT-Display Setup
TFT_eSPI tft = TFT_eSPI();

// Timer-Variablen: Wir wollen die API nicht blockieren oder überlasten
unsigned long lastTime = 0;
// Intervall auf 15 Minuten setzen (15 Minuten * 60 Sekunden * 1000 Millisekunden)
unsigned long timerDelay = 900000; 

void setup() {
  Serial.begin(115200);

  // Hintergrundbeleuchtung des Displays einschalten (Pin 27)
  pinMode(27, OUTPUT);
  digitalWrite(27, HIGH);

  // Display initialisieren
  tft.init();
  tft.setRotation(1); // Querformat (480 x 320)
  tft.fillScreen(TFT_BLACK);

  // 1. WLAN-Verbindung aufbauen
  WiFi.begin(ssid, password);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.drawCentreString("Verbinde mit WLAN...", 240, 150, 4);
  
  Serial.print("Verbinde mit WLAN");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  
  // Erfolgsmeldung auf dem Display
  tft.fillScreen(TFT_BLACK);
  tft.setTextColor(TFT_GREEN, TFT_BLACK);
  tft.drawCentreString("WLAN verbunden!", 240, 150, 4);
  delay(2000);
  tft.fillScreen(TFT_BLACK);

  // 2. Ersten API-Aufruf direkt beim Start ausführen
  fetchAndDisplayWeather();
}

void loop() {
  // API nur alle 15 Minuten abfragen
  if ((millis() - lastTime) > timerDelay) {
    if (WiFi.status() == WL_CONNECTED) {
      fetchAndDisplayWeather();
    }
    lastTime = millis();
  }
}

// Funktion für den HTTP-Request (Hypertext Transfer Protocol) und die Anzeige
void fetchAndDisplayWeather() {
  HTTPClient http; 
  
  http.begin(apiUrl);
  int httpResponseCode = http.GET(); // GET-Anfrage an den Server senden
  
  if (httpResponseCode > 0) {
    String payload = http.getString();
    
    // Neues JSON-Dokument vorbereiten (ArduinoJson Version 7)
    JsonDocument doc;
    DeserializationError error = deserializeJson(doc, payload);
    
    if (!error) {
      // Temperatur aus dem JSON extrahieren
      float temp = doc["current_weather"]["temperature"];
      
      // Anzeige auf dem TFT aktualisieren
      // TextPadding füllt den Hintergrund automatisch schwarz, um altes Flackern zu verhindern
      tft.setTextPadding(240); 
      
      tft.setTextColor(TFT_LIGHTGREY, TFT_BLACK);
      tft.drawCentreString("Aktuelle Temperatur in Koeln:", 230, 80, 4);
      
      // Temperatur groß in Gelb darstellen
      tft.setTextColor(TFT_YELLOW, TFT_BLACK);
      String tempNumber = String(temp, 1);
      tft.setTextPadding(150); 
      tft.drawRightString(tempNumber, 280, 150, 7); 
      tft.setTextPadding(0);
      tft.drawString("C", 290, 180, 4); 
      
      tft.setTextPadding(0);
    } else {
      Serial.print("JSON parsen fehlgeschlagen: ");
      Serial.println(error.c_str());
    }
  } else {
    Serial.print("Fehler beim HTTP-Request. Code: ");
    Serial.println(httpResponseCode);
  }
  
  http.end(); // Verbindung sauber schließen, um Speicher freizugeben
}


Ergebnis:

In einem zweiten Teil werde ich, mit dieser einfachen Version – um die Entwicklung nachvollziehbar zu halten – zeigen, wie sich mit LVGL und dem EEZ‑Studio Tool auch animierte Darstellungen sowie eigene Logos realisieren lassen, selbst trotz des begrenzten Speichers der Microcontroller.

Vorab aber noch diese TFT_eSPI Version mit deutlich mehr Information die uns ja Open-Meteo anbietet:

Sketch mit informativeren Display ohne LVGL:

Quellcode hier aufklappen
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <TFT_eSPI.h>

// WLAN-Zugangsdaten (SSID = Service Set Identifier)
const char* ssid = "DEIN_WLAN_NAME";
const char* password = "DEIN_WLAN_PASSWORT";

String apiUrl = "http://api.open-meteo.com/v1/forecast?latitude=50.91&longitude=6.81&current=temperature_2m,relative_humidity_2m,apparent_temperature,weather_code,wind_speed_10m,wind_direction_10m&daily=precipitation_probability_max&timezone=Europe/Berlin";

// TFT-Display Setup (Thin-Film Transistor)
TFT_eSPI tft = TFT_eSPI();

// Timer-Variablen
unsigned long lastTime = 0;
// Intervall auf 15 Minuten setzen (15 * 60 * 1000)
unsigned long timerDelay = 900000; 

// Hilfsfunktion: WMO-Code (World Meteorological Organization) in Text übersetzen
String getWeatherDescription(int code) {
  switch (code) {
    case 0: return "Klarer Himmel";
    case 1: case 2: case 3: return "Heiter bis wolkig";
    case 45: case 48: return "Nebel";
    case 51: case 53: case 55: return "Nieselregen";
    case 61: case 63: case 65: return "Regen";
    case 71: case 73: case 75: return "Schnee";
    case 77: return "Schneegriesel";
    case 80: case 81: case 82: return "Regenschauer";
    case 85: case 86: return "Schneeschauer";
    case 95: case 96: case 99: return "Gewitter";
    default: return "Unbekanntes Wetter";
  }
}

void setup() {
  Serial.begin(115200);

  // Hintergrundbeleuchtung des Displays einschalten (Pin 27)
  pinMode(27, OUTPUT);
  digitalWrite(27, HIGH);

  // Display initialisieren
  tft.init();
  tft.setRotation(1); // Querformat (480 x 320)
  tft.fillScreen(TFT_BLACK);

  // 1. WLAN-Verbindung aufbauen
  WiFi.begin(ssid, password);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.drawCentreString("Verbinde mit WLAN...", 240, 150, 4);
  
  Serial.print("Verbinde mit WLAN");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  
  // Erfolgsmeldung
  tft.fillScreen(TFT_BLACK);
  tft.setTextColor(TFT_GREEN, TFT_BLACK);
  tft.drawCentreString("WLAN verbunden!", 240, 150, 4);
  delay(2000);
  tft.fillScreen(TFT_BLACK);

  // 2. Ersten API-Aufruf direkt beim Start ausführen
  fetchAndDisplayWeather();
}

void loop() {
  // API nur alle 15 Minuten abfragen
  if ((millis() - lastTime) > timerDelay) {
    if (WiFi.status() == WL_CONNECTED) {
      fetchAndDisplayWeather();
    }
    lastTime = millis();
  }
}

// Funktion für den HTTP-Request (Hypertext Transfer Protocol)
void fetchAndDisplayWeather() {
  HTTPClient http; 
  
  http.begin(apiUrl);
  int httpResponseCode = http.GET(); 
  
  if (httpResponseCode > 0) {
    String payload = http.getString();
    
    // JSON (JavaScript Object Notation) Dokument vorbereiten (ArduinoJson Version 7)
    JsonDocument doc;
    DeserializationError error = deserializeJson(doc, payload);
    
    if (!error) {
      // Daten aus dem JSON extrahieren
      float temp       = doc["current"]["temperature_2m"];
      float feelsLike  = doc["current"]["apparent_temperature"];
      int humidity     = doc["current"]["relative_humidity_2m"];
      float windSpeed  = doc["current"]["wind_speed_10m"];
      int windDir      = doc["current"]["wind_direction_10m"];
      int weatherCode  = doc["current"]["weather_code"];
      
      // Regenwahrscheinlichkeit für den heutigen Tag (Index 0)
      int rainProb     = doc["daily"]["precipitation_probability_max"][0];

      String weatherDesc = getWeatherDescription(weatherCode);
      
      // Bildschirm leeren
      tft.fillScreen(TFT_BLACK);

      // --- ÜBERSCHRIFT ---
      tft.setTextColor(TFT_LIGHTGREY, TFT_BLACK);
      tft.drawCentreString("Wetter in Koeln", 240, 10, 4);
      tft.drawLine(20, 40, 460, 40, TFT_DARKGREY);

      // --- LINKE SEITE (Hauptinformationen) ---
      
      // 1. Nur die Temperatur-Zahl mit der großen Schriftart (7) schreiben
      tft.setTextColor(TFT_YELLOW, TFT_BLACK);
      String tempString = String(temp, 1);
      tft.drawString(tempString, 30, 80, 7); 
      
      // 2. Die Breite der Zahl berechnen, damit wir wissen, wo das 'C' hin muss
      int numWidth = tft.textWidth(tempString, 7);
      
      // 3. Grad-Zeichen als zwei ineinanderliegende Kreise selbst zeichnen (sieht oft besser aus)
      int degreeX = 30 + numWidth + 5;
      tft.drawCircle(degreeX, 85, 3, TFT_YELLOW); 
      tft.drawCircle(degreeX, 85, 4, TFT_YELLOW); // Etwas dicker machen
      
      // 4. Das 'C' mit der normalen Schriftart (4) schreiben
      tft.drawString("C", degreeX + 10, 80 + 15, 4); 
      
      // Wetterbeschreibung (z.B. "Heiter bis wolkig")
      tft.setTextColor(TFT_CYAN, TFT_BLACK);
      tft.drawString(weatherDesc, 30, 150, 4);

      // --- RECHTE SEITE (Details) ---
      int rightColX = 250; // Startpunkt für die rechte Spalte
      
      tft.setTextColor(TFT_WHITE, TFT_BLACK);
      tft.drawString("Details:", rightColX, 70, 4);
      
      tft.setTextColor(TFT_LIGHTGREY, TFT_BLACK);
      
      String feelsString = "Gefuehlt: " + String(feelsLike, 1) + " C";
      tft.drawString(feelsString, rightColX, 105, 2);
      
      String humString = "Feuchte:  " + String(humidity) + " %";
      tft.drawString(humString, rightColX, 130, 2);
      
      String windString = "Wind:     " + String(windSpeed, 1) + " km/h";
      tft.drawString(windString, rightColX, 155, 2);

      String dirString = "Richtung: " + String(windDir) + " Grad";
      tft.drawString(dirString, rightColX, 180, 2);
      
      String rainString = "Regenrisiko: " + String(rainProb) + " %";
      tft.drawString(rainString, rightColX, 205, 2); 

      // --- FUSSZEILE ---
      tft.drawLine(20, 280, 460, 280, TFT_DARKGREY);
      tft.setTextColor(TFT_DARKGREY, TFT_BLACK);
      tft.drawCentreString("Daten bereitgestellt von Open-Meteo", 240, 290, 2);

    } else {
      Serial.print("JSON parsen fehlgeschlagen: ");
      Serial.println(error.c_str());
    }
  } else {
    Serial.print("Fehler beim HTTP-Request. Code: ");
    Serial.println(httpResponseCode);
  }
  
  http.end(); // Verbindung sauber schließen
}

Wie ihr eure perfekte Open-Meteo Koordinate für deinen Ort findet, hatte ich hier beschrieben:
https://prilchen.de/esp32-projekt-smart-weather-e-paper-display/



Übersicht weiterer Möglichkeiten grafischer User Interfaces (GUI) zu erstellen

MethodeBeschreibungFür wen geeignet
Arduino IDE (manuell)Direkte Programmierung mit TFT_eSPI & TouchScreen.hFortgeschrittene Bastler
EEZ StudioVisuelle GUI-Erstellung mit automatischer Code-GenerierungEinsteiger & GUI-Fans
LVGL mit Arduino IDEProfessionelle GUI mit Widgets, Animationen, ThemesFortgeschrittene Entwickler
Adafruit GFXAlternative Display-Library, einfacher aber weniger performantMinimalisten & Lernende
VS Code mit PlatformIOModerne Entwicklungsumgebung mit besserem ProjektmanagementProfis & Power-User
Web-basierte Toolsz. B. ESP Web Tools für OTA-Upload & GUI-EditorSchnelle Tests & Updates

Youtube Video:


Vorschau:

In Teil 2 zeige ich, wie ich mit LVGL eine modernere und mobile Version dieser Wetterstation erstelle, mit passendem 3D Druck Gehäuse.
Der letzte Teil 3 wird im Oktober veröffentlicht. Da zeige ich wie das ganze mit dem kostenlose GUI Erstellungstool EEZ Studio zum laufen gebracht werden kann.

, ,
Datenschutz-Übersicht

Diese Website verwendet Cookies, damit wir dir die bestmögliche Benutzerkomfort bieten können. Cookie-Informationen werden in deinem Browser gespeichert und führen Funktionen aus, wie das Wiedererkennen von dir, wenn du auf unsere Website zurückkehrst und hilft uns zu verstehen, welche Abschnitte der Website für dich am interessantesten und nützlichsten sind.