From 16edb47b1d909e451a5cd153275640e5f3216cd3 Mon Sep 17 00:00:00 2001 From: Chad Date: Sun, 29 Mar 2026 21:00:41 -0500 Subject: [PATCH] Configure Finnhub for Bitcoin/stocks, keep Yahoo for commodities/indices --- src/main.cpp | 106 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 89 insertions(+), 17 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 25a329c..157c90f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,6 +30,8 @@ #define MAX_INDICES 5 #define MAX_TICKER_LEN 8 +#define FINNHUB_API_KEY "d74t3bpr01qg1eo6pln0d74t3bpr01qg1eo6plng" + class LGFX : public lgfx::LGFX_Device { public: lgfx::Bus_RGB _bus_instance; @@ -194,7 +196,7 @@ String formatPriceUS(double price, int decimals = 2, bool includeDollar = true) return includeDollar ? "$" + result : result; } -bool fetchPrice(const char* symbol, double* result) { +bool fetchPriceYahoo(const char* symbol, double* result) { WiFiClientSecure client; client.setInsecure(); @@ -229,6 +231,39 @@ bool fetchPrice(const char* symbol, double* result) { return true; } +bool fetchPriceFinnhub(const char* symbol, double* result) { + WiFiClientSecure client; + client.setInsecure(); + + HTTPClient http; + String url = "https://finnhub.io/api/v1/quote?symbol=" + String(symbol) + "&token=" + String(FINNHUB_API_KEY); + + if (!http.begin(client, url)) return false; + + http.setTimeout(15000); + + int httpCode = http.GET(); + if (httpCode != HTTP_CODE_OK) { + http.end(); + return false; + } + + String payload = http.getString(); + http.end(); + + int idx = payload.indexOf("\"c\":"); + if (idx == -1) return false; + + int start = idx + 4; + 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 loadConfig() { preferences.begin("dashboard", true); @@ -273,22 +308,23 @@ void saveConfig() { } void updatePrices() { - if (WiFi.status() != WL_CONNECTED) return; - fetchPrice("BTC-USD", &btcPrice); - fetchPrice("GC=F", &goldPrice); - fetchPrice("SI=F", &silverPrice); - - for (int i = 0; i < MAX_STOCKS; i++) { - if (strlen(stockTickers[i]) > 0) { - fetchPrice(stockTickers[i], &stockPrices[i]); + if (WiFi.status() != WL_CONNECTED) return; + + fetchPriceFinnhub("BINANCE:BTCUSDT", &btcPrice); + fetchPriceYahoo("GC=F", &goldPrice); + fetchPriceYahoo("SI=F", &silverPrice); + + for (int i = 0; i < MAX_STOCKS; i++) { + if (strlen(stockTickers[i]) > 0) { + fetchPriceFinnhub(stockTickers[i], &stockPrices[i]); + } } - } - - for (int i = 0; i < MAX_INDICES; i++) { - if (indexSelected[i] && strlen(indexTickers[i]) > 0) { - fetchPrice(indexTickers[i], &indexPrices[i]); + + for (int i = 0; i < MAX_INDICES; i++) { + if (indexSelected[i] && strlen(indexTickers[i]) > 0) { + fetchPriceYahoo(indexTickers[i], &indexPrices[i]); + } } - } } void drawKeyboard(int y) { @@ -497,9 +533,17 @@ void drawDashboard() { lcd.setTextDatum(textdatum_t::middle_left); String wifiStatus = WiFi.status() == WL_CONNECTED ? "OK" : "OFFLINE"; lcd.drawString("WiFi: " + wifiStatus, 20, SCREEN_HEIGHT - 20); - - lcd.fillSmoothRoundRect(SCREEN_WIDTH - 160, SCREEN_HEIGHT - 36, 140, 32, 6, TFT_DARKGRAY); + + int secondsLeft = (REFRESH_INTERVAL - (millis() - lastRefresh)) / 1000; + if (secondsLeft < 0) secondsLeft = 0; + int mins = secondsLeft / 60; + int secs = secondsLeft % 60; + char timeStr[8]; + sprintf(timeStr, "%d:%02d", mins, secs); lcd.setTextDatum(textdatum_t::middle_center); + lcd.drawString(String(timeStr), SCREEN_WIDTH / 2, SCREEN_HEIGHT - 20); + + lcd.fillSmoothRoundRect(SCREEN_WIDTH - 160, SCREEN_HEIGHT - 36, 140, 32, 6, TFT_DARKGRAY); lcd.drawString("CUSTOMIZE", SCREEN_WIDTH - 90, SCREEN_HEIGHT - 20); } @@ -683,8 +727,15 @@ void showRefreshingMessage() { lcd.fillSmoothRoundRect(0, SCREEN_HEIGHT - 40, SCREEN_WIDTH, 40, 8, TFT_ORANGE); lcd.setTextColor(TFT_WHITE, TFT_ORANGE); lcd.setFont(&fonts::DejaVu18); + lcd.setTextDatum(textdatum_t::middle_left); + String wifiStatus = WiFi.status() == WL_CONNECTED ? "OK" : "OFFLINE"; + lcd.drawString("WiFi: " + wifiStatus, 20, SCREEN_HEIGHT - 20); + lcd.setTextDatum(textdatum_t::middle_center); lcd.drawString("REFRESHING...", SCREEN_WIDTH / 2, SCREEN_HEIGHT - 20); + + lcd.fillSmoothRoundRect(SCREEN_WIDTH - 160, SCREEN_HEIGHT - 36, 140, 32, 6, TFT_DARKGRAY); + lcd.drawString("CUSTOMIZE", SCREEN_WIDTH - 90, SCREEN_HEIGHT - 20); } void refreshDataAndShow() { @@ -769,6 +820,20 @@ void setup() { drawDashboard(); } +void drawFooterCountdown() { + int secondsLeft = (REFRESH_INTERVAL - (millis() - lastRefresh)) / 1000; + if (secondsLeft < 0) secondsLeft = 0; + int mins = secondsLeft / 60; + int secs = secondsLeft % 60; + char timeStr[8]; + sprintf(timeStr, "%d:%02d", mins, secs); + + lcd.setFont(&fonts::DejaVu18); + lcd.setTextColor(TFT_WHITE, TFT_ORANGE); + lcd.setTextDatum(textdatum_t::middle_center); + lcd.drawString(String(timeStr), SCREEN_WIDTH / 2, SCREEN_HEIGHT - 20); +} + void loop() { int32_t x, y; @@ -810,7 +875,14 @@ void loop() { delay(300); } + static unsigned long lastCountdownUpdate = 0; + if (currentScreen == SCREEN_DASHBOARD && millis() - lastCountdownUpdate >= 1000) { + lastCountdownUpdate = millis(); + drawFooterCountdown(); + } + if (currentScreen == SCREEN_DASHBOARD && millis() - lastRefresh > REFRESH_INTERVAL) { + showRefreshingMessage(); updatePrices(); lastRefresh = millis(); drawDashboard();