From 28ca31e0e683d85f61b7b7b5ac7b444916f9bbcf Mon Sep 17 00:00:00 2001 From: Chad Date: Sun, 29 Mar 2026 16:19:31 -0500 Subject: [PATCH] Modern UI: antialiased DejaVu fonts, color-coded assets, proper RGB565 colors --- docs/README.md | 125 +++++++++ src/main.cpp | 667 ++++++++++++++++++++++--------------------------- 2 files changed, 417 insertions(+), 375 deletions(-) create mode 100644 docs/README.md diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..b722d7f --- /dev/null +++ b/docs/README.md @@ -0,0 +1,125 @@ +# Bitcoin Dashboard for Elecrow 7-inch HMI Display + +A real-time financial dashboard displaying cryptocurrency, precious metals, stocks, and market indices on the Elecrow 7.0-inch ESP32 HMI Display. + +## Hardware + +- **Display**: Elecrow 7.0-inch ESP32 HMI Display (Advance series) +- **Resolution**: 800x480 RGB parallel interface +- **Touch**: GT911 capacitive touch controller +- **MCU**: ESP32-S3 with N4R8 module (4MB Flash, 8MB PSRAM) + +## Features + +- Real-time price data from Yahoo Finance API +- Auto-refresh every 5 minutes +- Touch to manual refresh +- WiFi credentials stored in persistent memory +- Antialiased fonts (DejaVu family) + +## Displayed Assets + +| Column | Assets | +|--------|--------| +| Hard Money | Bitcoin (BTC/USD), Gold, Silver | +| Stocks | MSTR, STRC | +| Indices | NASDAQ, DOW, S&P 500 | + +## Color Scheme + +| Asset | Color | Hex | +|-------|-------|-----| +| Bitcoin | Orange | `#F7931A` | +| Gold | Gold | `#FFD700` | +| Silver | Silver | `#C0C0C0` | +| Stocks/Indices | Dollar Green | `#00A86B` | +| Status Bar | Bitcoin Orange | `#F7931A` | + +## Software Stack + +- **Framework**: Arduino (PlatformIO) +- **Display Library**: LovyanGFX v1.2.19 +- **Fonts**: DejaVu (antialiased) + +## Building + +```bash +cd /home/chad/Documents/PlatformIO/Projects/bitcoin_dashboard +pio run -t upload +``` + +## PlatformIO Configuration + +- **Platform**: espressif32@6.8.1 +- **Board**: esp32-s3-devkitc-1 +- **Partition**: huge_app.csv (3MB app, no OTA) +- **Flash Mode**: QIO @ 80MHz +- **PSRAM**: OPI (8MB) + +## Data Source + +Yahoo Finance API: +``` +https://query1.finance.yahoo.com/v8/finance/chart/{SYMBOL}?interval=1d&range=1d +``` + +### Symbol Mapping + +| Display | Yahoo Symbol | +|---------|--------------| +| Bitcoin | BTC-USD | +| Gold | GC=F | +| Silver | SI=F | +| MSTR | MSTR | +| STRC | STRC | +| NASDAQ | ^IXIC | +| DOW | ^DJI | +| S&P 500 | ^GSPC | + +## Pin Configuration + +### RGB Display Interface + +| Signal | GPIO | +|--------|------| +| D0-D15 | 15, 7, 6, 5, 4, 9, 46, 3, 8, 16, 1, 14, 21, 47, 48, 45 | +| HSYNC | GPIO 39 | +| VSYNC | GPIO 40 | +| DE | GPIO 41 | +| PCLK | GPIO 0 | +| Backlight | GPIO 2 | + +### Touch Controller (GT911) + +| Signal | GPIO | +|--------|------| +| SDA | GPIO 19 | +| SCL | GPIO 20 | +| I2C Address | 0x14 | + +## WiFi Setup + +WiFi credentials are stored in ESP32 preferences (persistent storage): + +```cpp +preferences.begin("wifi", true); +preferences.putString("ssid", "your_ssid"); +preferences.putString("password", "your_password"); +preferences.end(); +``` + +## Project Structure + +``` +bitcoin_dashboard/ +├── src/ +│ └── main.cpp # Main application +├── docs/ +│ └── README.md # This file +├── platformio.ini # PlatformIO configuration +└── .gitignore +``` + +## License + +MIT diff --git a/src/main.cpp b/src/main.cpp index 93ccb50..eb51782 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,6 @@ /** * Bitcoin Dashboard for Elecrow 7-inch HMI Display - * Simplified version with better WiFi/API debugging + * Three-column layout with antialiased fonts */ #include @@ -16,412 +16,329 @@ #define SCREEN_WIDTH 800 #define SCREEN_HEIGHT 480 -#define TFT_WHITE 0xFFFF -#define TFT_BLACK 0x0000 -#define TFT_RED 0xF800 -#define TFT_GREEN 0x07E0 -#define TFT_BLUE 0x001F -#define TFT_ORANGE 0xFD40 -#define TFT_GRAY 0x7BEF +#define TFT_WHITE 0xFFFF +#define TFT_BLACK 0x0000 +#define TFT_ORANGE 0xFD20 +#define TFT_GOLD 0xFEA0 +#define TFT_SILVER 0xC618 +#define TFT_GREEN 0x07E0 class LGFX : public lgfx::LGFX_Device { public: - lgfx::Bus_RGB _bus_instance; - lgfx::Panel_RGB _panel_instance; - lgfx::Light_PWM _light_instance; - lgfx::Touch_GT911 _touch_instance; + lgfx::Bus_RGB _bus_instance; + lgfx::Panel_RGB _panel_instance; + lgfx::Light_PWM _light_instance; + lgfx::Touch_GT911 _touch_instance; - LGFX(void) { - { - auto cfg = _panel_instance.config(); - cfg.memory_width = 800; - cfg.memory_height = 480; - cfg.panel_width = 800; - cfg.panel_height = 480; - cfg.offset_x = 0; - cfg.offset_y = 0; - _panel_instance.config(cfg); - } - - { - auto cfg = _bus_instance.config(); - cfg.panel = &_panel_instance; - cfg.pin_d0 = GPIO_NUM_15; - cfg.pin_d1 = GPIO_NUM_7; - cfg.pin_d2 = GPIO_NUM_6; - cfg.pin_d3 = GPIO_NUM_5; - cfg.pin_d4 = GPIO_NUM_4; - cfg.pin_d5 = GPIO_NUM_9; - cfg.pin_d6 = GPIO_NUM_46; - cfg.pin_d7 = GPIO_NUM_3; - cfg.pin_d8 = GPIO_NUM_8; - cfg.pin_d9 = GPIO_NUM_16; - cfg.pin_d10 = GPIO_NUM_1; - cfg.pin_d11 = GPIO_NUM_14; - cfg.pin_d12 = GPIO_NUM_21; - cfg.pin_d13 = GPIO_NUM_47; - cfg.pin_d14 = GPIO_NUM_48; - cfg.pin_d15 = GPIO_NUM_45; - cfg.pin_henable = GPIO_NUM_41; - cfg.pin_vsync = GPIO_NUM_40; - cfg.pin_hsync = GPIO_NUM_39; - cfg.pin_pclk = GPIO_NUM_0; - cfg.freq_write = 12000000; - cfg.hsync_polarity = 0; - cfg.hsync_front_porch = 40; - cfg.hsync_pulse_width = 48; - cfg.hsync_back_porch = 40; - cfg.vsync_polarity = 0; - cfg.vsync_front_porch = 1; - cfg.vsync_pulse_width = 31; - cfg.vsync_back_porch = 13; - cfg.pclk_active_neg = 1; - cfg.de_idle_high = 0; - cfg.pclk_idle_high = 0; - _bus_instance.config(cfg); - } - _panel_instance.setBus(&_bus_instance); - - { - auto cfg = _light_instance.config(); - cfg.pin_bl = GPIO_NUM_2; - _light_instance.config(cfg); - } - _panel_instance.light(&_light_instance); - - { - auto cfg = _touch_instance.config(); - cfg.x_min = 0; - cfg.x_max = 799; - cfg.y_min = 0; - cfg.y_max = 479; - cfg.pin_int = -1; - cfg.pin_rst = -1; - cfg.bus_shared = false; - cfg.offset_rotation = 0; - cfg.i2c_port = I2C_NUM_1; - cfg.pin_sda = GPIO_NUM_19; - cfg.pin_scl = GPIO_NUM_20; - cfg.freq = 400000; - cfg.i2c_addr = 0x14; - _touch_instance.config(cfg); - _panel_instance.setTouch(&_touch_instance); - } - setPanel(&_panel_instance); + LGFX(void) { + { + auto cfg = _panel_instance.config(); + cfg.memory_width = 800; + cfg.memory_height = 480; + cfg.panel_width = 800; + cfg.panel_height = 480; + cfg.offset_x = 0; + cfg.offset_y = 0; + _panel_instance.config(cfg); } + + { + auto cfg = _bus_instance.config(); + cfg.panel = &_panel_instance; + cfg.pin_d0 = GPIO_NUM_15; + cfg.pin_d1 = GPIO_NUM_7; + cfg.pin_d2 = GPIO_NUM_6; + cfg.pin_d3 = GPIO_NUM_5; + cfg.pin_d4 = GPIO_NUM_4; + cfg.pin_d5 = GPIO_NUM_9; + cfg.pin_d6 = GPIO_NUM_46; + cfg.pin_d7 = GPIO_NUM_3; + cfg.pin_d8 = GPIO_NUM_8; + cfg.pin_d9 = GPIO_NUM_16; + cfg.pin_d10 = GPIO_NUM_1; + cfg.pin_d11 = GPIO_NUM_14; + cfg.pin_d12 = GPIO_NUM_21; + cfg.pin_d13 = GPIO_NUM_47; + cfg.pin_d14 = GPIO_NUM_48; + cfg.pin_d15 = GPIO_NUM_45; + cfg.pin_henable = GPIO_NUM_41; + cfg.pin_vsync = GPIO_NUM_40; + cfg.pin_hsync = GPIO_NUM_39; + cfg.pin_pclk = GPIO_NUM_0; + cfg.freq_write = 12000000; + cfg.hsync_polarity = 0; + cfg.hsync_front_porch = 40; + cfg.hsync_pulse_width = 48; + cfg.hsync_back_porch = 40; + cfg.vsync_polarity = 0; + cfg.vsync_front_porch = 1; + cfg.vsync_pulse_width = 31; + cfg.vsync_back_porch = 13; + cfg.pclk_active_neg = 1; + cfg.de_idle_high = 0; + cfg.pclk_idle_high = 0; + _bus_instance.config(cfg); + } + _panel_instance.setBus(&_bus_instance); + + { + auto cfg = _light_instance.config(); + cfg.pin_bl = GPIO_NUM_2; + _light_instance.config(cfg); + } + _panel_instance.light(&_light_instance); + + { + auto cfg = _touch_instance.config(); + cfg.x_min = 0; + cfg.x_max = 799; + cfg.y_min = 0; + cfg.y_max = 479; + cfg.pin_int = -1; + cfg.pin_rst = -1; + cfg.bus_shared = false; + cfg.offset_rotation = 0; + cfg.i2c_port = I2C_NUM_1; + cfg.pin_sda = GPIO_NUM_19; + cfg.pin_scl = GPIO_NUM_20; + cfg.freq = 400000; + cfg.i2c_addr = 0x14; + _touch_instance.config(cfg); + _panel_instance.setTouch(&_touch_instance); + } + setPanel(&_panel_instance); + } }; static LGFX lcd; static Preferences preferences; -// Price data -double btcPrice = 0; -double goldPrice = 0; -double silverPrice = 0; -double mstrPrice = 0; -double strtPrice = 0; -double nasdaqPrice = 0; -double dowPrice = 0; -double spxPrice = 0; +double btcPrice = 0, goldPrice = 0, silverPrice = 0; +double mstrPrice = 0, strtPrice = 0; +double nasdaqPrice = 0, dowPrice = 0, spxPrice = 0; unsigned long lastRefresh = 0; const unsigned long REFRESH_INTERVAL = 300000; -// Format price with US-style commas (e.g., 66,647.29) String formatPriceUS(double price, int decimals = 2, bool includeDollar = true) { - if (price <= 0) return "..."; - - // Build the number string - String result = ""; - - // Handle the integer part with commas - long longPrice = (long)price; - String intStr = String(longPrice); - int len = intStr.length(); - int commaPos = len % 3; - - for (int i = 0; i < len; i++) { - if (i > 0 && (i - (len % 3)) % 3 == 0 && (len % 3 != 0 || i > 0)) { - result += ","; - } else if (len % 3 == 0 && i > 0 && i % 3 == 0) { - result += ","; - } - result += intStr[i]; + if (price <= 0) return "..."; + + String result = ""; + long longPrice = (long)price; + String intStr = String(longPrice); + int len = intStr.length(); + + for (int i = 0; i < len; i++) { + if (i > 0 && (len - i) % 3 == 0) { + result += ","; } - - // Add decimals if needed - if (decimals > 0) { - result += "."; - double frac = price - longPrice; - for (int d = 0; d < decimals; d++) { - frac *= 10; - } - long fracLong = (long)(frac + 0.5); // Round - String fracStr = String(fracLong); - while (fracStr.length() < decimals) { - fracStr = "0" + fracStr; - } - result += fracStr; - } - - if (includeDollar) { - return "$" + result; - } - return result; + result += intStr[i]; + } + + if (decimals > 0) { + result += "."; + double frac = price - longPrice; + for (int d = 0; d < decimals; d++) frac *= 10; + long fracLong = (long)(frac + 0.5); + String fracStr = String(fracLong); + while (fracStr.length() < decimals) fracStr = "0" + fracStr; + result += fracStr; + } + + return includeDollar ? "$" + result : result; } bool fetchPrice(const char* symbol, double* result) { - WiFiClientSecure client; - client.setInsecure(); - - HTTPClient http; - String url = "https://query1.finance.yahoo.com/v8/finance/chart/" + String(symbol) + "?interval=1d&range=1d"; - - Serial.println("Fetching: " + String(symbol)); - Serial.println("URL: " + url); - - if (!http.begin(client, url)) { - Serial.println("HTTP begin failed"); - return false; - } - - http.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"); - http.setTimeout(15000); - - int httpCode = http.GET(); - Serial.println("HTTP code: " + String(httpCode)); - - if (httpCode != HTTP_CODE_OK) { - Serial.println("HTTP error: " + http.errorToString(httpCode)); - http.end(); - return false; - } - - String payload = http.getString(); + WiFiClientSecure client; + client.setInsecure(); + + HTTPClient http; + String url = "https://query1.finance.yahoo.com/v8/finance/chart/" + String(symbol) + "?interval=1d&range=1d"; + + if (!http.begin(client, url)) return false; + + http.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"); + http.setTimeout(15000); + + int httpCode = http.GET(); + if (httpCode != HTTP_CODE_OK) { http.end(); - - Serial.println("Response length: " + String(payload.length())); - - // Parse regularMarketPrice from meta section - // Format: "regularMarketPrice":66647.29 - String searchStr = "\"regularMarketPrice\":"; - int idx = payload.indexOf(searchStr); - - if (idx == -1) { - Serial.println("regularMarketPrice not found"); - return false; - } - - int start = idx + searchStr.length(); - int end = payload.indexOf(",", start); - if (end == -1) end = payload.indexOf("}", start); - - if (end <= start) { - Serial.println("Could not find end of price"); - return false; - } - - String priceStr = payload.substring(start, end); - Serial.println("Price string: " + priceStr); - - *result = priceStr.toDouble(); - Serial.println("Parsed: " + String(*result)); - - return true; + return false; + } + + String payload = http.getString(); + http.end(); + + String searchStr = "\"regularMarketPrice\":"; + int idx = payload.indexOf(searchStr); + if (idx == -1) return false; + + int start = idx + searchStr.length(); + int end = payload.indexOf(",", start); + if (end == -1) end = payload.indexOf("}", start); + if (end <= start) return false; + + String priceStr = payload.substring(start, end); + *result = priceStr.toDouble(); + return true; } void updatePrices() { - Serial.println("\n=== Updating prices ==="); - Serial.println("WiFi status: " + String(WiFi.status())); - Serial.println("WiFi connected: " + String(WiFi.status() == WL_CONNECTED ? "YES" : "NO")); - - if (WiFi.status() != WL_CONNECTED) { - Serial.println("WiFi not connected, skipping update"); - return; - } - - Serial.println("IP: " + WiFi.localIP().toString()); - - fetchPrice("BTC-USD", &btcPrice); - fetchPrice("GC=F", &goldPrice); - fetchPrice("SI=F", &silverPrice); - fetchPrice("MSTR", &mstrPrice); - fetchPrice("STRC", &strtPrice); - fetchPrice("^IXIC", &nasdaqPrice); - fetchPrice("^DJI", &dowPrice); - fetchPrice("^GSPC", &spxPrice); - - Serial.println("=== Update complete ===\n"); + if (WiFi.status() != WL_CONNECTED) return; + fetchPrice("BTC-USD", &btcPrice); + fetchPrice("GC=F", &goldPrice); + fetchPrice("SI=F", &silverPrice); + fetchPrice("MSTR", &mstrPrice); + fetchPrice("STRC", &strtPrice); + fetchPrice("^IXIC", &nasdaqPrice); + fetchPrice("^DJI", &dowPrice); + fetchPrice("^GSPC", &spxPrice); } void drawDashboard() { - lcd.fillScreen(TFT_BLACK); - - // Title bar - lcd.fillRect(0, 0, SCREEN_WIDTH, 45, TFT_ORANGE); - lcd.setTextColor(TFT_BLACK, TFT_ORANGE); - lcd.setTextSize(3); - lcd.setTextDatum(textdatum_t::middle_center); - lcd.drawString("BITCOIN DASHBOARD", SCREEN_WIDTH / 2, 22); - - // Column positions - int col1X = 20; - int col2X = 280; - int col3X = 540; - int startY = 60; - - // Column separators - lcd.drawLine(265, startY, 265, SCREEN_HEIGHT - 35, TFT_GRAY); - lcd.drawLine(525, startY, 525, SCREEN_HEIGHT - 35, TFT_GRAY); - - // Column 1: HARD MONEY - lcd.setTextColor(TFT_ORANGE, TFT_BLACK); - lcd.setTextSize(2); - lcd.setTextDatum(textdatum_t::top_left); - lcd.drawString("HARD MONEY", col1X, startY); - -lcd.setTextColor(TFT_WHITE, TFT_BLACK); - lcd.drawString("Bitcoin:", col1X, startY + 35); - lcd.drawString(formatPriceUS(btcPrice, 0, true), col1X, startY + 60); - - lcd.drawString("Gold:", col1X, startY + 95); - lcd.drawString(formatPriceUS(goldPrice, 2, true), col1X, startY + 120); - - lcd.drawString("Silver:", col1X, startY + 155); - lcd.drawString(formatPriceUS(silverPrice, 2, true), col1X, startY + 180); - - // Column 2: STOCKS - lcd.setTextColor(TFT_ORANGE, TFT_BLACK); - lcd.drawString("STOCKS", col2X, startY); - - lcd.setTextColor(TFT_WHITE, TFT_BLACK); - lcd.drawString("MSTR:", col2X, startY + 35); - lcd.drawString(formatPriceUS(mstrPrice, 2, true), col2X, startY + 60); - - lcd.drawString("STRC:", col2X, startY + 95); - lcd.drawString(formatPriceUS(strtPrice, 2, true), col2X, startY + 120); - - // Column 3: INDICES - lcd.setTextColor(TFT_ORANGE, TFT_BLACK); - lcd.drawString("INDICES", col3X, startY); - - lcd.setTextColor(TFT_WHITE, TFT_BLACK); - lcd.drawString("NASDAQ:", col3X, startY + 35); - lcd.drawString(formatPriceUS(nasdaqPrice, 2, true), col3X, startY + 60); - - lcd.drawString("DOW:", col3X, startY + 95); - lcd.drawString(formatPriceUS(dowPrice, 2, true), col3X, startY + 120); - - lcd.drawString("SPX:", col3X, startY + 155); - lcd.drawString(formatPriceUS(spxPrice, 2, true), col3X, startY + 180); - - // Status bar - lcd.fillRect(0, SCREEN_HEIGHT - 30, SCREEN_WIDTH, 30, TFT_GRAY); - lcd.setTextColor(TFT_BLACK, TFT_GRAY); - lcd.setTextSize(1); - lcd.setTextDatum(textdatum_t::middle_left); - - String wifiStatus = WiFi.status() == WL_CONNECTED ? "Connected" : "DISCONNECTED"; - lcd.drawString("WiFi: " + wifiStatus, 10, SCREEN_HEIGHT - 15); - lcd.setTextDatum(textdatum_t::middle_center); - lcd.drawString("TOUCH TO REFRESH", SCREEN_WIDTH / 2, SCREEN_HEIGHT - 15); + lcd.fillScreen(TFT_BLACK); + + int col1X = 20; + int col2X = 280; + int col3X = 540; + int startY = 20; + + lcd.setFont(&fonts::DejaVu18); + lcd.setTextDatum(textdatum_t::top_left); + + lcd.setTextColor(TFT_WHITE, TFT_BLACK); + lcd.drawString("HARD MONEY", col1X, startY); + + lcd.setTextColor(TFT_WHITE, TFT_BLACK); + lcd.drawString("STOCKS", col2X, startY); + + lcd.setTextColor(TFT_WHITE, TFT_BLACK); + lcd.drawString("INDICES", col3X, startY); + + int labelY = startY + 40; + int priceY = labelY + 28; + + lcd.setTextColor(TFT_ORANGE, TFT_BLACK); + lcd.setFont(&fonts::DejaVu18); + lcd.drawString("Bitcoin:", col1X, labelY); + lcd.drawString(formatPriceUS(btcPrice, 0), col1X, priceY); + + lcd.setTextColor(TFT_GOLD, TFT_BLACK); + lcd.drawString("Gold:", col1X, labelY + 60); + lcd.drawString(formatPriceUS(goldPrice, 2), col1X, priceY + 60); + + lcd.setTextColor(TFT_SILVER, TFT_BLACK); + lcd.drawString("Silver:", col1X, labelY + 120); + lcd.drawString(formatPriceUS(silverPrice, 2), col1X, priceY + 120); + + lcd.setTextColor(TFT_GREEN, TFT_BLACK); + lcd.drawString("MSTR:", col2X, labelY); + lcd.drawString(formatPriceUS(mstrPrice, 2), col2X, priceY); + + lcd.drawString("STRC:", col2X, labelY + 60); + lcd.drawString(formatPriceUS(strtPrice, 2), col2X, priceY + 60); + + lcd.drawString("NASDAQ:", col3X, labelY); + lcd.drawString(formatPriceUS(nasdaqPrice, 2), col3X, priceY); + + lcd.drawString("DOW:", col3X, labelY + 60); + lcd.drawString(formatPriceUS(dowPrice, 2), col3X, priceY + 60); + + lcd.drawString("SPX:", col3X, labelY + 120); + lcd.drawString(formatPriceUS(spxPrice, 2), col3X, priceY + 120); + + lcd.fillSmoothRoundRect(0, SCREEN_HEIGHT - 35, SCREEN_WIDTH, 35, 6, TFT_ORANGE); + lcd.setTextColor(TFT_WHITE, TFT_ORANGE); + lcd.setFont(&fonts::DejaVu12); + lcd.setTextDatum(textdatum_t::middle_left); + String wifiStatus = WiFi.status() == WL_CONNECTED ? "Connected" : "DISCONNECTED"; + lcd.drawString("WiFi: " + wifiStatus, 15, SCREEN_HEIGHT - 17); + lcd.setTextDatum(textdatum_t::middle_center); + lcd.drawString("TOUCH TO REFRESH", SCREEN_WIDTH / 2, SCREEN_HEIGHT - 17); } void setup() { - Serial.begin(115200); - delay(3000); - - Serial.println("\n\n=== STARTING ==="); - - lcd.init(); - lcd.setBrightness(255); - lcd.setRotation(0); - - lcd.fillScreen(TFT_WHITE); - delay(250); - lcd.fillScreen(TFT_RED); - delay(250); - lcd.fillScreen(TFT_GREEN); - delay(250); - lcd.fillScreen(TFT_BLUE); - delay(250); - lcd.fillScreen(TFT_BLACK); - - lcd.setTextSize(2); - lcd.setTextColor(TFT_WHITE, TFT_BLACK); - lcd.setTextDatum(textdatum_t::middle_center); - lcd.drawString("Bitcoin Dashboard", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 - 40); - lcd.drawString("Starting...", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2); - - // Load WiFi credentials - preferences.begin("wifi", true); - String ssid = preferences.getString("ssid", ""); - String pass = preferences.getString("password", ""); - preferences.end(); - - if (ssid.length() > 0) { - Serial.println("Connecting to WiFi: " + ssid); - lcd.drawString("WiFi: " + ssid, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 + 40); - - WiFi.mode(WIFI_STA); - WiFi.begin(ssid.c_str(), pass.c_str()); - - int attempts = 0; - while (WiFi.status() != WL_CONNECTED && attempts < 60) { - delay(500); - Serial.print("."); - attempts++; - } - Serial.println(); - - if (WiFi.status() == WL_CONNECTED) { - Serial.println("WiFi connected!"); - Serial.println("IP: " + WiFi.localIP().toString()); - lcd.drawString("Connected! IP:", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 + 80); - lcd.drawString(WiFi.localIP().toString(), SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 + 110); - - delay(1000); - updatePrices(); - lastRefresh = millis(); - drawDashboard(); - return; - } - - Serial.println("WiFi connection failed"); - lcd.drawString("WiFi failed", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 + 80); - delay(2000); + Serial.begin(115200); + delay(3000); + + lcd.init(); + lcd.setBrightness(255); + lcd.setRotation(0); + + lcd.fillScreen(TFT_WHITE); + delay(250); + lcd.fillScreen(TFT_ORANGE); + delay(250); + lcd.fillScreen(TFT_BLACK); + delay(250); + + lcd.setTextColor(TFT_WHITE, TFT_BLACK); + lcd.setFont(&fonts::DejaVu24); + lcd.setTextDatum(textdatum_t::middle_center); + lcd.drawString("Bitcoin Dashboard", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 - 40); + lcd.setFont(&fonts::DejaVu18); + lcd.drawString("Starting...", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2); + + preferences.begin("wifi", true); + String ssid = preferences.getString("ssid", ""); + String pass = preferences.getString("password", ""); + preferences.end(); + + if (ssid.length() > 0) { + lcd.setFont(&fonts::DejaVu18); + lcd.drawString("WiFi: " + ssid, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 + 40); + + WiFi.mode(WIFI_STA); + WiFi.begin(ssid.c_str(), pass.c_str()); + + int attempts = 0; + while (WiFi.status() != WL_CONNECTED && attempts < 60) { + delay(500); + Serial.print("."); + attempts++; } - - // Show WiFi setup screen - lcd.drawString("No WiFi configured", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 + 40); + + if (WiFi.status() == WL_CONNECTED) { + lcd.drawString("Connected! IP:", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 + 80); + lcd.setFont(&fonts::DejaVu12); + lcd.drawString(WiFi.localIP().toString(), SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 + 110); + + delay(1000); + updatePrices(); + lastRefresh = millis(); + drawDashboard(); + return; + } + + lcd.drawString("WiFi failed", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 + 80); delay(2000); - drawDashboard(); + } + + drawDashboard(); } void loop() { - int32_t x, y; - - if (lcd.getTouch(&x, &y)) { - Serial.println("Touch detected - refreshing..."); - lcd.setTextColor(TFT_WHITE, TFT_BLACK); - lcd.setTextSize(2); - lcd.setTextDatum(textdatum_t::middle_center); - lcd.fillRect(0, SCREEN_HEIGHT / 2 - 20, SCREEN_WIDTH, 40, TFT_BLACK); - lcd.drawString("Refreshing...", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2); - - updatePrices(); - lastRefresh = millis(); - drawDashboard(); - - delay(500); - } - - // Auto-refresh every 5 minutes - if (millis() - lastRefresh > REFRESH_INTERVAL) { - updatePrices(); - lastRefresh = millis(); - drawDashboard(); - } - - delay(100); + int32_t x, y; + + if (lcd.getTouch(&x, &y)) { + lcd.setTextColor(TFT_WHITE, TFT_BLACK); + lcd.setFont(&fonts::DejaVu24); + lcd.setTextDatum(textdatum_t::middle_center); + lcd.fillRect(0, SCREEN_HEIGHT / 2 - 20, SCREEN_WIDTH, 40, TFT_BLACK); + lcd.drawString("Refreshing...", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2); + + updatePrices(); + lastRefresh = millis(); + drawDashboard(); + + delay(500); + } + + if (millis() - lastRefresh > REFRESH_INTERVAL) { + updatePrices(); + lastRefresh = millis(); + drawDashboard(); + } + + delay(100); }