Search models, users, collections, and posts

ESP32 CYD 2.8" Dashboard (Weather, Stocks & Moon)

Print Profile(1)

All
X1 Carbon
A1
X1E
P1P
H2C
X1
P1S
A1 mini
H2S
P2S
H2D
H2D Pro
X2D
A2L

Smart Dashboard 0.2mm layer, 2 walls, 15% infill
Smart Dashboard 0.2mm layer, 2 walls, 15% infill
Designer
1.5 h
1 plate
5.0(2)

Open in Bambu Studio
Boost
8
35
7
2
25
23
Released 

Description

Content has been automatically translated.
Show original

📱 ESP32 CYD 2.8" Smart Dashboard (Weather, Stocks & Moon)

 

With Webflasher, no programming or cumbersome library loading:  Smart Dashboard Installer

 

This project transforms the affordable ESP32-2432S028R (also known as "Cheap Yellow Display" or CYD) into a sleek, functional information dashboard for your desk

 

 

✨ Main Features:

  • Precise Time: Automatic synchronization via NTP (CET/CEST with daylight saving time)
  • Live Weather: Current data via Open-Meteo (no API key required!)
  • Moon Phases: Integrated mathematical algorithm for the current moon phase
  • Financial Ticker: Live rates for DAX, DOW Jones, VIX and Bitcoin (€) (via Yahoo Finance & Coinbase)
  • Easy Setup: Thanks to WiFiManager, you don't have to hardcode your WiFi data. The device creates a hotspot for configuration on first boot

🛠️ Hardware Requirements:

  1. Display: ESP32-2432S028R (2.8" TFT with resistive touch) 
  2. Buy here, with this display the program works without errors

     

 

 

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:

 

Here is the Sketch:

 

⚠️ IMPORTANT: TFT_eSPI Configuration (User_Setup.h)

For the display to be controlled correctly, the TFT_eSPI library must be adapted. Replace the content of your User_Setup.h (in the Arduino library folder) with the following values or ensure that these lines are active:

 

// ============================================================================
// DISPLAY PIN CONFIGURATION (Suitable for 2.8" ESP32-2432S028R / CYD)
// ============================================================================

#define ILI9341_DRIVER       // Standard driver for this 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 is connected to the EN button

// --- BACKLIGHT CONFIGURATION ---
// Standard CYD (Micro-USB) uses Pin 21. 
// Newer USB-C versions (e.g. from diymore) often use Pin 27.
// If the display remains black, simply swap the comments (//):

#define TFT_BL   21          // <-- Standard setting
// #define TFT_BL   27       // <-- If Pin 21 does not work, remove // here

#define TFT_BACKLIGHT_ON HIGH // Backlight is switched on with HIGH

// --- FONTS ---
#define LOAD_GLCD  
#define LOAD_FONT2 
#define LOAD_FONT4 
#define LOAD_GFXFF

// --- SPEED ---
#define SPI_FREQUENCY  40000000
 

Sketch for 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"

// CONFIGURATION & STANDARDS
String cityName = "Oberhausen";
String selectedTZ = "CET-1CEST,M3.5.0,M10.5.0/3"; // Default: 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) + "&current=temperature_2m,weather_code&timezone=auto", doc)) {
   weatherTemp = doc["current"]["temperature_2m"];
   int code = doc["current"]["weather_code"];
   if(code==0) weatherDesc = "Clear";
   else if(code<=3) weatherDesc = "Cloudy";
   else if(code<=67) weatherDesc = "Rain"; 
   else if(code<=77) weatherDesc = "Snow";
   else weatherDesc = "Thunderstorm";
 }
}

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 = "New Moon";
   else if (phase < 6.5) moonPhaseText = "Waxing Crescent";
   else if (phase < 8.5) moonPhaseText = "First Quarter";
   else if (phase < 13.5) moonPhaseText = "Gibbous"; 
   else if (phase < 16.5) moonPhaseText = "Full Moon"; 
   else if (phase < 21.5) moonPhaseText = "Waning Gibbous";
   else if (phase < 23.5) moonPhaseText = "Last Quarter";
   else if (phase < 28.5) moonPhaseText = "Waning Crescent";
   else moonPhaseText = "New Moon";
 }
}

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 ? " DST" : " ST");
}

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);

 // WIFI MANAGER SETUP
 WiFiManager wm;
 
 // Custom fields for city and time zone
 WiFiManagerParameter custom_city("city", "Enter city", cityName.c_str(), 40);
 // A simple HTML dropdown for the time zone
 const char* custom_html_tz = "<br/><label for='tz'>Select time zone</label><br/><select name='tz' id='tz'>"
                              "<option value='CET-1CEST,M3.5.0,M10.5.0/3'>Europe/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("WiFi Config...");

 if (!wm.autoConnect("MakerDisplay-Setup")) ESP.restart();

 // Adopt values from the portal
 cityName = String(custom_city.getValue());
 // Note: Reading select boxes via WiFiManager is a bit tricky, 
 // for MakerWorld we set Berlin as default here if nothing comes.
 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; }
}
 

 


 

Comment & Rating (7)

(0/1000)