ESP32 CYD 2.8" Dashboard (Wetter, Aktien & Mond)
📱 ESP32 CYD 2.8" Smart Dashboard (Wetter, Aktien & Mond)
Mit Webflasher, kein Programieren oder umständlich Bibliotheken laden: Smart Dashboard Installer
Dieses Projekt verwandelt das preiswerte ESP32-2432S028R (auch bekannt als "Cheap Yellow Display" oder CYD) in ein schickes, funktionales Informations-Dashboard für deinen Schreibtisch.
✨ Hauptmerkmale:
- Präzise Uhrzeit: Automatische Synchronisierung über NTP (CET/CEST mit Sommerzeit).
- Live Wetter: Aktuelle Daten über Open-Meteo (kein API-Key erforderlich!).
- Mondphasen: Integrierter mathematischer Algorithmus für die aktuelle Mondphase.
- Finanz-Ticker: Live-Kurse für DAX, DOW Jones, VIX und Bitcoin (€) (via Yahoo Finance & Coinbase).
- Einfaches Setup: Dank WiFiManager musst du deine WLAN-Daten nicht im Code festschreiben. Das Gerät erstellt beim ersten Start einen Hotspot zur Konfiguration.
🛠️ Hardware Voraussetzungen:
- Display: ESP32-2432S028R (2.8" TFT mit resistivem Touch)
-
Hier kaufen, mit diesem Display funktioniert das Programm ohne Fehler
Features:
- 🌦️ Live Weather: Open-Meteo (No API Key needed!)
- 📈 Finance: Live DAX, DOW, BTC & VIX via Yahoo/Coinbase.
- 🌙 Moon Phase: Precise mathematical calculation.
- ⚙️ Easy Setup: WiFiManager included – no hardcoded WiFi credentials!
Hardware needed:
Hier der Sketch:
⚠️ WICHTIG: TFT_eSPI Konfiguration (User_Setup.h)
Damit das Display korrekt angesteuert wird, muss die Bibliothek TFT_eSPI angepasst werden. Ersetze den Inhalt deiner User_Setup.h (im Arduino-Library-Ordner) durch folgende Werte oder stelle sicher, dass diese Zeilen aktiv sind:
// ============================================================================
// DISPLAY PIN-KONFIGURATION (Passend für 2.8" ESP32-2432S028R / CYD)
// ============================================================================
#define ILI9341_DRIVER // Standard Treiber für dieses Display
#define TFT_MISO 12
#define TFT_MOSI 13
#define TFT_SCLK 14
#define TFT_CS 15
#define TFT_DC 2
#define TFT_RST -1 // Reset ist mit dem EN-Button verbunden
// --- BACKLIGHT KONFIGURATION ---
// Standard CYD (Micro-USB) nutzt Pin 21.
// Neuere USB-C Versionen (z.B. von diymore) nutzen oft Pin 27.
// Falls das Display schwarz bleibt, einfach die Kommentare (//) tauschen:
#define TFT_BL 21 // <-- Standard Einstellung
// #define TFT_BL 27 // <-- Falls Pin 21 nicht funktioniert, hier // entfernen
#define TFT_BACKLIGHT_ON HIGH // Backlight wird mit HIGH eingeschaltet
// --- SCHRIFTARTEN ---
#define LOAD_GLCD
#define LOAD_FONT2
#define LOAD_FONT4
#define LOAD_GFXFF
// --- GESCHWINDIGKEIT ---
#define SPI_FREQUENCY 40000000
Sketch für Arduino IDE 2.3.8
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <WiFiManager.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <TFT_eSPI.h>
#include "time.h"
// KONFIGURATION & STANDARDS
String cityName = "Oberhausen";
String selectedTZ = "CET-1CEST,M3.5.0,M10.5.0/3"; // Standard: Berlin
const char* ntpServer = "pool.ntp.org";
TFT_eSPI tft = TFT_eSPI();
WiFiClientSecure client;
float latitude, longitude;
String weatherDesc; float weatherTemp;
String moonPhaseText;
float daxValue, dowValue, vixValue, btcValue;
unsigned long lastTimeUpdate, lastWeatherUpdate, lastMoonUpdate, lastQuoteUpdate;
const unsigned long TIME_UPDATE_INTERVAL = 1000;
const unsigned long WEATHER_UPDATE_INTERVAL = 10*60*1000;
const unsigned long MOON_UPDATE_INTERVAL = 60*60*1000;
const unsigned long QUOTE_UPDATE_INTERVAL = 2*60*1000;
bool httpGetJson(const String& url, DynamicJsonDocument& doc) {
HTTPClient http; http.begin(url);
http.addHeader("User-Agent", "Mozilla/5.0");
int code = http.GET();
if (code != 200) { http.end(); return false; }
String payload = http.getString(); http.end();
return deserializeJson(doc, payload) == DeserializationError::Ok;
}
void updateWeather() {
DynamicJsonDocument doc(4096);
if (latitude == 0) {
if (httpGetJson("https://open-meteo.com" + cityName + "&count=1&language=de&format=json", doc)) {
latitude = doc["results"][0]["latitude"];
longitude = doc["results"][0]["longitude"];
cityName = doc["results"][0]["name"].as<String>();
}
}
doc.clear();
if (httpGetJson("https://open-meteo.com" + String(latitude,4) + "&longitude=" + String(longitude,4) + "¤t=temperature_2m,weather_code&timezone=auto", doc)) {
weatherTemp = doc["current"]["temperature_2m"];
int code = doc["current"]["weather_code"];
if(code==0) weatherDesc = "Klar";
else if(code<=3) weatherDesc = "Bewoelkt";
else if(code<=67) weatherDesc = "Regen";
else if(code<=77) weatherDesc = "Schnee";
else weatherDesc = "Gewitter";
}
}
void updateMoonPhaseLocal() {
struct tm t;
if (getLocalTime(&t)) {
time_t nowTs = mktime(&t);
double diff = (double)nowTs - 947182440.0;
double days = diff / 86400.0;
double cycle = 29.530588;
double phase = fmod(days, cycle);
if (phase < 0) phase += cycle;
if (phase < 1.5) moonPhaseText = "Neumond";
else if (phase < 6.5) moonPhaseText = "Zunehmend";
else if (phase < 8.5) moonPhaseText = "1. Viertel";
else if (phase < 13.5) moonPhaseText = "Fast voll";
else if (phase < 16.5) moonPhaseText = "Vollmond";
else if (phase < 21.5) moonPhaseText = "Abnehmend";
else if (phase < 23.5) moonPhaseText = "3. Viertel";
else if (phase < 28.5) moonPhaseText = "Abnehmend";
else moonPhaseText = "Neumond";
}
}
void fetchStockData(String symbol, float& price) {
DynamicJsonDocument doc(8192);
if (httpGetJson("https://yahoo.com" + symbol, doc)) {
price = doc["chart"]["result"][0]["meta"]["regularMarketPrice"];
}
}
void fetchBitcoinEUR(float& price) {
DynamicJsonDocument doc(2048);
if (httpGetJson("https://coinbase.com", doc)) {
price = doc["data"]["amount"].as<float>();
}
}
void updateQuotes() {
fetchStockData("^GDAXI", daxValue);
fetchStockData("^DJI", dowValue);
fetchStockData("^VIX", vixValue);
fetchBitcoinEUR(btcValue);
}
void drawTime() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) return;
char buf[32]; strftime(buf, 32, "%H:%M:%S %d.%m", &timeinfo);
tft.setTextSize(2); tft.setTextColor(TFT_CYAN);
tft.fillRect(10, 10, 220, 35, TFT_BLACK);
tft.setCursor(15, 30); tft.print(buf);
tft.print(timeinfo.tm_isdst > 0 ? " SZ" : " WZ");
}
void drawWeather() {
tft.setTextSize(2); tft.setTextColor(TFT_WHITE);
tft.fillRect(10, 50, 230, 50, TFT_BLACK);
tft.setCursor(15, 55); tft.print(cityName);
tft.setCursor(15, 80); tft.print(weatherTemp, 1); tft.print("C "); tft.print(weatherDesc);
}
void drawMoon() {
tft.setTextSize(2); tft.setTextColor(TFT_YELLOW);
tft.fillRect(10, 105, 220, 35, TFT_BLACK);
tft.setCursor(15, 125); tft.print(moonPhaseText);
}
void drawQuotes() {
tft.setTextSize(2); tft.setTextColor(TFT_RED);
tft.fillRect(10, 155, 220, 90, TFT_BLACK);
int y = 160;
tft.setCursor(15, y); tft.print("DAX "); tft.println(daxValue,0); y += 20;
tft.setCursor(15, y); tft.print("DOW "); tft.println(dowValue,0); y += 20;
tft.setCursor(15, y); tft.print("VIX "); tft.println(vixValue,1); y += 20;
tft.setCursor(15, y); tft.print("BTC "); tft.print(btcValue,0); tft.print(" EUR");
}
void setup() {
Serial.begin(115200);
tft.init(); tft.setRotation(0); tft.fillScreen(TFT_BLACK);
// WLAN MANAGER SETUP
WiFiManager wm;
// Custom Felder für Stadt und Zeitzone
WiFiManagerParameter custom_city("city", "Stadt eingeben", cityName.c_str(), 40);
// Ein einfaches HTML-Dropdown für die Zeitzone
const char* custom_html_tz = "<br/><label for='tz'>Zeitzone wählen</label><br/><select name='tz' id='tz'>"
"<option value='CET-1CEST,M3.5.0,M10.5.0/3'>Europa/Berlin</option>"
"<option value='GMT0BST,M3.5.0,M10.5.0/2'>London</option>"
"<option value='EST5EDT,M3.2.0,M11.1.0'>New York</option>"
"<option value='JST-9'>Tokyo</option></select>";
WiFiManagerParameter custom_tz_html(custom_html_tz);
wm.addParameter(&custom_city);
wm.addParameter(&custom_tz_html);
tft.setTextColor(TFT_WHITE); tft.setTextSize(2); tft.setCursor(10, 50);
tft.println("WLAN Konfig...");
if (!wm.autoConnect("MakerDisplay-Setup")) ESP.restart();
// Werte aus dem Portal übernehmen
cityName = String(custom_city.getValue());
// Hinweis: Das Auslesen von select-boxen via WiFiManager ist etwas tricky,
// für MakerWorld setzen wir hier Berlin als Default fest, falls nichts kommt.
configTzTime(selectedTZ.c_str(), ntpServer);
client.setInsecure();
updateWeather(); updateMoonPhaseLocal(); updateQuotes();
tft.fillScreen(TFT_BLACK);
drawTime(); drawWeather(); drawMoon(); drawQuotes();
lastTimeUpdate = lastWeatherUpdate = lastMoonUpdate = lastQuoteUpdate = millis();
}
void loop() {
unsigned long now = millis();
if (now - lastTimeUpdate > TIME_UPDATE_INTERVAL) { drawTime(); lastTimeUpdate = now; }
if (now - lastWeatherUpdate > WEATHER_UPDATE_INTERVAL) { updateWeather(); drawWeather(); lastWeatherUpdate = now; }
if (now - lastMoonUpdate > MOON_UPDATE_INTERVAL) { updateMoonPhaseLocal(); drawMoon(); lastMoonUpdate = now; }
if (now - lastQuoteUpdate > QUOTE_UPDATE_INTERVAL) { updateQuotes(); drawQuotes(); lastQuoteUpdate = now; }
}
Design by user_2529369599 on MakerWorld (license: CC0).