{"product_id":"esp32-cyd-2-8-dashboard-wetter-aktien-mond","title":"ESP32 CYD 2.8\" Dashboard (Wetter, Aktien \u0026 Mond)","description":"\u003cp\u003e\u003cstrong\u003e📱 ESP32 CYD 2.8\" Smart Dashboard (Wetter, Aktien \u0026amp; Mond)\u003c\/strong\u003e\u003c\/p\u003e\u003cp\u003e \u003c\/p\u003e\u003cp\u003e\u003ci\u003e\u003cstrong\u003eMit Webflasher, kein Programieren oder umständlich Bibliotheken laden\u003c\/strong\u003e\u003c\/i\u003e:  \u003ca target=\"_blank\" rel=\"noopener noreferrer ugc\" href=\"https:\/\/bo963.github.io\/ESP32-CYD-2.8-Dashboard-Wetter-Aktien-Mond-\/\"\u003eSmart Dashboard Installer\u003c\/a\u003e\u003c\/p\u003e\u003cp\u003e \u003c\/p\u003e\u003cp\u003eDieses Projekt verwandelt das preiswerte \u003cstrong\u003eESP32-2432S028R\u003c\/strong\u003e (auch bekannt als \"Cheap Yellow Display\" oder CYD) in ein schickes, funktionales Informations-Dashboard für deinen Schreibtisch.\u003c\/p\u003e\u003cp\u003e \u003c\/p\u003e\u003cp\u003e \u003c\/p\u003e\u003cp\u003e\u003cstrong\u003e✨ Hauptmerkmale:\u003c\/strong\u003e\u003c\/p\u003e\u003cul\u003e\n\u003cli\u003e\n\u003cstrong\u003ePräzise Uhrzeit:\u003c\/strong\u003e Automatische Synchronisierung über NTP (CET\/CEST mit Sommerzeit).\u003c\/li\u003e\n\u003cli\u003e\n\u003cstrong\u003eLive Wetter:\u003c\/strong\u003e Aktuelle Daten über Open-Meteo (kein API-Key erforderlich!).\u003c\/li\u003e\n\u003cli\u003e\n\u003cstrong\u003eMondphasen:\u003c\/strong\u003e Integrierter mathematischer Algorithmus für die aktuelle Mondphase.\u003c\/li\u003e\n\u003cli\u003e\n\u003cstrong\u003eFinanz-Ticker:\u003c\/strong\u003e Live-Kurse für \u003cstrong\u003eDAX, DOW Jones, VIX\u003c\/strong\u003e und \u003cstrong\u003eBitcoin (€)\u003c\/strong\u003e (via Yahoo Finance \u0026amp; Coinbase).\u003c\/li\u003e\n\u003cli\u003e\n\u003cstrong\u003eEinfaches Setup:\u003c\/strong\u003e Dank \u003cstrong\u003eWiFiManager\u003c\/strong\u003e musst du deine WLAN-Daten nicht im Code festschreiben. Das Gerät erstellt beim ersten Start einen Hotspot zur Konfiguration.\u003c\/li\u003e\n\u003c\/ul\u003e\u003cp\u003e\u003cstrong\u003e🛠️ Hardware Voraussetzungen:\u003c\/strong\u003e\u003c\/p\u003e\u003col\u003e\n\u003cli\u003e\n\u003cstrong\u003eDisplay:\u003c\/strong\u003e ESP32-2432S028R (2.8\" TFT mit resistivem Touch) \u003c\/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca target=\"_blank\" rel=\"noopener noreferrer ugc\" href=\"https:\/\/amzn.to\/4djMIuE\"\u003eHier kaufen, mit diesem Display funktioniert das Programm ohne Fehler\u003c\/a\u003e\u003c\/p\u003e\n\u003cp\u003e \u003c\/p\u003e\n\u003c\/li\u003e\n\u003c\/ol\u003e\u003cp\u003e \u003c\/p\u003e\u003cp\u003e \u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eFeatures:\u003c\/strong\u003e\u003c\/p\u003e\u003cul\u003e\n\u003cli\u003e🌦️ \u003cstrong\u003eLive Weather:\u003c\/strong\u003e Open-Meteo (No API Key needed!)\u003c\/li\u003e\n\u003cli\u003e📈 \u003cstrong\u003eFinance:\u003c\/strong\u003e Live DAX, DOW, BTC \u0026amp; VIX via Yahoo\/Coinbase.\u003c\/li\u003e\n\u003cli\u003e🌙 \u003cstrong\u003eMoon Phase:\u003c\/strong\u003e Precise mathematical calculation.\u003c\/li\u003e\n\u003cli\u003e⚙️ \u003cstrong\u003eEasy Setup:\u003c\/strong\u003e WiFiManager included – no hardcoded WiFi credentials!\u003c\/li\u003e\n\u003cli\u003e\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003eHardware needed:\u003c\/strong\u003e\u003c\/p\u003e\n\u003cul\u003e\u003cli\u003e\u003ca target=\"_blank\" rel=\"noopener noreferrer ugc\" href=\"https:\/\/amzn.to\/4djMIuE\"\u003eESP32-2432S028R (Cheap Yellow Display) \u003c\/a\u003e\u003c\/li\u003e\u003c\/ul\u003e\n\u003c\/blockquote\u003e\u003c\/li\u003e\n\u003c\/ul\u003e\u003cp\u003e \u003c\/p\u003e\u003cp\u003eHier der Sketch:\u003c\/p\u003e\u003cp\u003e \u003c\/p\u003e\u003cp\u003e\u003cstrong\u003e⚠️ WICHTIG: TFT_eSPI Konfiguration (User_Setup.h)\u003c\/strong\u003e\u003c\/p\u003e\u003cp\u003eDamit das Display korrekt angesteuert wird, muss die Bibliothek \u003cstrong\u003eTFT_eSPI\u003c\/strong\u003e angepasst werden. Ersetze den Inhalt deiner User_Setup.h (im Arduino-Library-Ordner) durch folgende Werte oder stelle sicher, dass diese Zeilen aktiv sind:\u003c\/p\u003e\u003cp\u003e \u003c\/p\u003e\u003cp\u003e\/\/ ============================================================================\u003cbr\u003e\/\/ DISPLAY PIN-KONFIGURATION (Passend für 2.8\" ESP32-2432S028R \/ CYD)\u003cbr\u003e\/\/ ============================================================================\u003c\/p\u003e\u003cp\u003e#define ILI9341_DRIVER       \/\/ Standard Treiber für dieses Display\u003c\/p\u003e\u003cp\u003e#define TFT_MISO 12\u003cbr\u003e#define TFT_MOSI 13\u003cbr\u003e#define TFT_SCLK 14\u003cbr\u003e#define TFT_CS   15\u003cbr\u003e#define TFT_DC    2\u003cbr\u003e#define TFT_RST  -1          \/\/ Reset ist mit dem EN-Button verbunden\u003c\/p\u003e\u003cp\u003e\/\/ --- BACKLIGHT KONFIGURATION ---\u003cbr\u003e\/\/ Standard CYD (Micro-USB) nutzt Pin 21. \u003cbr\u003e\/\/ Neuere USB-C Versionen (z.B. von diymore) nutzen oft Pin 27.\u003cbr\u003e\/\/ Falls das Display schwarz bleibt, einfach die Kommentare (\/\/) tauschen:\u003c\/p\u003e\u003cp\u003e#define TFT_BL   21          \/\/ \u0026lt;-- Standard Einstellung\u003cbr\u003e\/\/ #define TFT_BL   27       \/\/ \u0026lt;-- Falls Pin 21 nicht funktioniert, hier \/\/ entfernen\u003c\/p\u003e\u003cp\u003e#define TFT_BACKLIGHT_ON HIGH \/\/ Backlight wird mit HIGH eingeschaltet\u003c\/p\u003e\u003cp\u003e\/\/ --- SCHRIFTARTEN ---\u003cbr\u003e#define LOAD_GLCD  \u003cbr\u003e#define LOAD_FONT2 \u003cbr\u003e#define LOAD_FONT4 \u003cbr\u003e#define LOAD_GFXFF\u003c\/p\u003e\u003cp\u003e\/\/ --- GESCHWINDIGKEIT ---\u003cbr\u003e#define SPI_FREQUENCY  40000000\u003cbr\u003e \u003c\/p\u003e\u003cp\u003eSketch für \u003cspan style=\"color: rgb(55,65,70); font-size: 20px\"\u003e\u003cstrong\u003eArduino IDE 2.3.8 \u003c\/strong\u003e\u003c\/span\u003e\u003c\/p\u003e\u003cp\u003e \u003c\/p\u003e\u003cp\u003e#include \u0026lt;WiFi.h\u0026gt;\u003cbr\u003e#include \u0026lt;WiFiClientSecure.h\u0026gt;\u003cbr\u003e#include \u0026lt;WiFiManager.h\u0026gt;\u003cbr\u003e#include \u0026lt;HTTPClient.h\u0026gt;\u003cbr\u003e#include \u0026lt;ArduinoJson.h\u0026gt;\u003cbr\u003e#include \u0026lt;TFT_eSPI.h\u0026gt;\u003cbr\u003e#include \"time.h\"\u003c\/p\u003e\u003cp\u003e\/\/ KONFIGURATION \u0026amp; STANDARDS\u003cbr\u003eString cityName = \"Oberhausen\";\u003cbr\u003eString selectedTZ = \"CET-1CEST,M3.5.0,M10.5.0\/3\"; \/\/ Standard: Berlin\u003cbr\u003econst char* ntpServer = \"pool.ntp.org\";\u003c\/p\u003e\u003cp\u003eTFT_eSPI tft = TFT_eSPI();\u003cbr\u003eWiFiClientSecure client;\u003c\/p\u003e\u003cp\u003efloat latitude, longitude;\u003cbr\u003eString weatherDesc; float weatherTemp;\u003cbr\u003eString moonPhaseText;\u003cbr\u003efloat daxValue, dowValue, vixValue, btcValue;\u003c\/p\u003e\u003cp\u003eunsigned long lastTimeUpdate, lastWeatherUpdate, lastMoonUpdate, lastQuoteUpdate;\u003cbr\u003econst unsigned long TIME_UPDATE_INTERVAL = 1000;\u003cbr\u003econst unsigned long WEATHER_UPDATE_INTERVAL = 10*60*1000;\u003cbr\u003econst unsigned long MOON_UPDATE_INTERVAL = 60*60*1000;\u003cbr\u003econst unsigned long QUOTE_UPDATE_INTERVAL = 2*60*1000;\u003c\/p\u003e\u003cp\u003ebool httpGetJson(const String\u0026amp; url, DynamicJsonDocument\u0026amp; doc) {\u003cbr\u003e HTTPClient http; http.begin(url);\u003cbr\u003e http.addHeader(\"User-Agent\", \"Mozilla\/5.0\");\u003cbr\u003e int code = http.GET();\u003cbr\u003e if (code != 200) { http.end(); return false; }\u003cbr\u003e String payload = http.getString(); http.end();\u003cbr\u003e return deserializeJson(doc, payload) == DeserializationError::Ok;\u003cbr\u003e}\u003c\/p\u003e\u003cp\u003evoid updateWeather() {\u003cbr\u003e DynamicJsonDocument doc(4096);\u003cbr\u003e if (latitude == 0) {\u003cbr\u003e   if (httpGetJson(\"https:\/\/open-meteo.com\" + cityName + \"\u0026amp;count=1\u0026amp;language=de\u0026amp;format=json\", doc)) {\u003cbr\u003e     latitude = doc[\"results\"][0][\"latitude\"];\u003cbr\u003e     longitude = doc[\"results\"][0][\"longitude\"];\u003cbr\u003e     cityName = doc[\"results\"][0][\"name\"].as\u0026lt;String\u0026gt;();\u003cbr\u003e   }\u003cbr\u003e }\u003cbr\u003e doc.clear();\u003cbr\u003e if (httpGetJson(\"https:\/\/open-meteo.com\" + String(latitude,4) + \"\u0026amp;longitude=\" + String(longitude,4) + \"\u0026amp;current=temperature_2m,weather_code\u0026amp;timezone=auto\", doc)) {\u003cbr\u003e   weatherTemp = doc[\"current\"][\"temperature_2m\"];\u003cbr\u003e   int code = doc[\"current\"][\"weather_code\"];\u003cbr\u003e   if(code==0) weatherDesc = \"Klar\";\u003cbr\u003e   else if(code\u0026lt;=3) weatherDesc = \"Bewoelkt\";\u003cbr\u003e   else if(code\u0026lt;=67) weatherDesc = \"Regen\"; \u003cbr\u003e   else if(code\u0026lt;=77) weatherDesc = \"Schnee\";\u003cbr\u003e   else weatherDesc = \"Gewitter\";\u003cbr\u003e }\u003cbr\u003e}\u003c\/p\u003e\u003cp\u003evoid updateMoonPhaseLocal() {\u003cbr\u003e struct tm t;\u003cbr\u003e if (getLocalTime(\u0026amp;t)) {\u003cbr\u003e   time_t nowTs = mktime(\u0026amp;t);\u003cbr\u003e   double diff = (double)nowTs - 947182440.0;\u003cbr\u003e   double days = diff \/ 86400.0;\u003cbr\u003e   double cycle = 29.530588;\u003cbr\u003e   double phase = fmod(days, cycle);\u003cbr\u003e   if (phase \u0026lt; 0) phase += cycle;\u003c\/p\u003e\u003cp\u003e   if (phase \u0026lt; 1.5) moonPhaseText = \"Neumond\";\u003cbr\u003e   else if (phase \u0026lt; 6.5) moonPhaseText = \"Zunehmend\";\u003cbr\u003e   else if (phase \u0026lt; 8.5) moonPhaseText = \"1. Viertel\";\u003cbr\u003e   else if (phase \u0026lt; 13.5) moonPhaseText = \"Fast voll\";\u003cbr\u003e   else if (phase \u0026lt; 16.5) moonPhaseText = \"Vollmond\"; \u003cbr\u003e   else if (phase \u0026lt; 21.5) moonPhaseText = \"Abnehmend\";\u003cbr\u003e   else if (phase \u0026lt; 23.5) moonPhaseText = \"3. Viertel\";\u003cbr\u003e   else if (phase \u0026lt; 28.5) moonPhaseText = \"Abnehmend\";\u003cbr\u003e   else moonPhaseText = \"Neumond\";\u003cbr\u003e }\u003cbr\u003e}\u003c\/p\u003e\u003cp\u003evoid fetchStockData(String symbol, float\u0026amp; price) {\u003cbr\u003e DynamicJsonDocument doc(8192);\u003cbr\u003e if (httpGetJson(\"https:\/\/yahoo.com\" + symbol, doc)) {\u003cbr\u003e   price = doc[\"chart\"][\"result\"][0][\"meta\"][\"regularMarketPrice\"];\u003cbr\u003e }\u003cbr\u003e}\u003c\/p\u003e\u003cp\u003evoid fetchBitcoinEUR(float\u0026amp; price) {\u003cbr\u003e DynamicJsonDocument doc(2048);\u003cbr\u003e if (httpGetJson(\"https:\/\/coinbase.com\", doc)) {\u003cbr\u003e   price = doc[\"data\"][\"amount\"].as\u0026lt;float\u0026gt;();\u003cbr\u003e }\u003cbr\u003e}\u003c\/p\u003e\u003cp\u003evoid updateQuotes() {\u003cbr\u003e fetchStockData(\"^GDAXI\", daxValue);\u003cbr\u003e fetchStockData(\"^DJI\", dowValue);\u003cbr\u003e fetchStockData(\"^VIX\", vixValue);\u003cbr\u003e fetchBitcoinEUR(btcValue);\u003cbr\u003e}\u003c\/p\u003e\u003cp\u003evoid drawTime() {\u003cbr\u003e struct tm timeinfo;\u003cbr\u003e if (!getLocalTime(\u0026amp;timeinfo)) return;\u003cbr\u003e char buf[32]; strftime(buf, 32, \"%H:%M:%S %d.%m\", \u0026amp;timeinfo);\u003cbr\u003e tft.setTextSize(2); tft.setTextColor(TFT_CYAN);\u003cbr\u003e tft.fillRect(10, 10, 220, 35, TFT_BLACK);\u003cbr\u003e tft.setCursor(15, 30); tft.print(buf);\u003cbr\u003e tft.print(timeinfo.tm_isdst \u0026gt; 0 ? \" SZ\" : \" WZ\");\u003cbr\u003e}\u003c\/p\u003e\u003cp\u003evoid drawWeather() {\u003cbr\u003e tft.setTextSize(2); tft.setTextColor(TFT_WHITE);\u003cbr\u003e tft.fillRect(10, 50, 230, 50, TFT_BLACK);\u003cbr\u003e tft.setCursor(15, 55); tft.print(cityName);\u003cbr\u003e tft.setCursor(15, 80); tft.print(weatherTemp, 1); tft.print(\"C \"); tft.print(weatherDesc);\u003cbr\u003e}\u003c\/p\u003e\u003cp\u003evoid drawMoon() {\u003cbr\u003e tft.setTextSize(2); tft.setTextColor(TFT_YELLOW);\u003cbr\u003e tft.fillRect(10, 105, 220, 35, TFT_BLACK);\u003cbr\u003e tft.setCursor(15, 125); tft.print(moonPhaseText);\u003cbr\u003e}\u003c\/p\u003e\u003cp\u003evoid drawQuotes() {\u003cbr\u003e tft.setTextSize(2); tft.setTextColor(TFT_RED);\u003cbr\u003e tft.fillRect(10, 155, 220, 90, TFT_BLACK);\u003cbr\u003e int y = 160;\u003cbr\u003e tft.setCursor(15, y); tft.print(\"DAX \"); tft.println(daxValue,0); y += 20;\u003cbr\u003e tft.setCursor(15, y); tft.print(\"DOW \"); tft.println(dowValue,0); y += 20;\u003cbr\u003e tft.setCursor(15, y); tft.print(\"VIX \"); tft.println(vixValue,1); y += 20;\u003cbr\u003e tft.setCursor(15, y); tft.print(\"BTC \"); tft.print(btcValue,0); tft.print(\" EUR\");\u003cbr\u003e}\u003c\/p\u003e\u003cp\u003evoid setup() {\u003cbr\u003e Serial.begin(115200);\u003cbr\u003e tft.init(); tft.setRotation(0); tft.fillScreen(TFT_BLACK);\u003c\/p\u003e\u003cp\u003e \/\/ WLAN MANAGER SETUP\u003cbr\u003e WiFiManager wm;\u003cbr\u003e \u003cbr\u003e \/\/ Custom Felder für Stadt und Zeitzone\u003cbr\u003e WiFiManagerParameter custom_city(\"city\", \"Stadt eingeben\", cityName.c_str(), 40);\u003cbr\u003e \/\/ Ein einfaches HTML-Dropdown für die Zeitzone\u003cbr\u003e const char* custom_html_tz = \"\u0026lt;br\/\u0026gt;\u0026lt;label for='tz'\u0026gt;Zeitzone wählen\u0026lt;\/label\u0026gt;\u0026lt;br\/\u0026gt;\u0026lt;select name='tz' id='tz'\u0026gt;\"\u003cbr\u003e                              \"\u0026lt;option value='CET-1CEST,M3.5.0,M10.5.0\/3'\u0026gt;Europa\/Berlin\u0026lt;\/option\u0026gt;\"\u003cbr\u003e                              \"\u0026lt;option value='GMT0BST,M3.5.0,M10.5.0\/2'\u0026gt;London\u0026lt;\/option\u0026gt;\"\u003cbr\u003e                              \"\u0026lt;option value='EST5EDT,M3.2.0,M11.1.0'\u0026gt;New York\u0026lt;\/option\u0026gt;\"\u003cbr\u003e                              \"\u0026lt;option value='JST-9'\u0026gt;Tokyo\u0026lt;\/option\u0026gt;\u0026lt;\/select\u0026gt;\";\u003cbr\u003e WiFiManagerParameter custom_tz_html(custom_html_tz);\u003c\/p\u003e\u003cp\u003e wm.addParameter(\u0026amp;custom_city);\u003cbr\u003e wm.addParameter(\u0026amp;custom_tz_html);\u003c\/p\u003e\u003cp\u003e tft.setTextColor(TFT_WHITE); tft.setTextSize(2); tft.setCursor(10, 50);\u003cbr\u003e tft.println(\"WLAN Konfig...\");\u003c\/p\u003e\u003cp\u003e if (!wm.autoConnect(\"MakerDisplay-Setup\")) ESP.restart();\u003c\/p\u003e\u003cp\u003e \/\/ Werte aus dem Portal übernehmen\u003cbr\u003e cityName = String(custom_city.getValue());\u003cbr\u003e \/\/ Hinweis: Das Auslesen von select-boxen via WiFiManager ist etwas tricky, \u003cbr\u003e \/\/ für MakerWorld setzen wir hier Berlin als Default fest, falls nichts kommt.\u003cbr\u003e configTzTime(selectedTZ.c_str(), ntpServer);\u003c\/p\u003e\u003cp\u003e client.setInsecure();\u003cbr\u003e updateWeather(); updateMoonPhaseLocal(); updateQuotes();\u003cbr\u003e tft.fillScreen(TFT_BLACK);\u003cbr\u003e drawTime(); drawWeather(); drawMoon(); drawQuotes();\u003cbr\u003e lastTimeUpdate = lastWeatherUpdate = lastMoonUpdate = lastQuoteUpdate = millis();\u003cbr\u003e}\u003c\/p\u003e\u003cp\u003evoid loop() {\u003cbr\u003e unsigned long now = millis();\u003cbr\u003e if (now - lastTimeUpdate \u0026gt; TIME_UPDATE_INTERVAL) { drawTime(); lastTimeUpdate = now; }\u003cbr\u003e if (now - lastWeatherUpdate \u0026gt; WEATHER_UPDATE_INTERVAL) { updateWeather(); drawWeather(); lastWeatherUpdate = now; }\u003cbr\u003e if (now - lastMoonUpdate \u0026gt; MOON_UPDATE_INTERVAL) { updateMoonPhaseLocal(); drawMoon(); lastMoonUpdate = now; }\u003cbr\u003e if (now - lastQuoteUpdate \u0026gt; QUOTE_UPDATE_INTERVAL) { updateQuotes(); drawQuotes(); lastQuoteUpdate = now; }\u003cbr\u003e}\u003cbr\u003e \u003c\/p\u003e\u003cp\u003e \u003c\/p\u003e\u003cp\u003e\u003cbr\u003e \u003c\/p\u003e\u003cp\u003eDesign by user_2529369599 on MakerWorld (license: CC0).\u003c\/p\u003e","brand":"Mymadmanlab","offers":[{"title":"Default Title","offer_id":67370741825840,"sku":null,"price":5.99,"currency_code":"USD","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0987\/0809\/5280\/files\/0_323bdb58-be89-4644-a5f4-e0d3ed41c9cf.png?v=1784095770","url":"https:\/\/mymadmanlab.com\/products\/esp32-cyd-2-8-dashboard-wetter-aktien-mond","provider":"Mymadmanlab.com","version":"1.0","type":"link"}