diff --git a/src/main.cpp b/src/main.cpp index 80b2a72..5bd15b6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -153,6 +153,16 @@ const char* getIndexFriendlyName(const char* ticker) { unsigned long lastRefresh = 0; const unsigned long REFRESH_INTERVAL = 180000; +const unsigned long HISTORY_REFRESH_INTERVAL = 3600000; + +#define HISTORY_DAYS 30 +double btcHistory[HISTORY_DAYS] = {0}; +double goldHistory[HISTORY_DAYS] = {0}; +double silverHistory[HISTORY_DAYS] = {0}; +bool btcHistoryLoaded = false; +bool goldHistoryLoaded = false; +bool silverHistoryLoaded = false; +unsigned long lastHistoryRefresh = 0; enum ScreenState { SCREEN_DASHBOARD, @@ -232,36 +242,138 @@ bool fetchPriceYahoo(const char* symbol, double* result) { } bool fetchPriceFinnhub(const char* symbol, double* result) { - WiFiClientSecure client; - client.setInsecure(); + WiFiClientSecure client; + client.setInsecure(); - HTTPClient http; - String url = "https://finnhub.io/api/v1/quote?symbol=" + String(symbol) + "&token=" + String(FINNHUB_API_KEY); + 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; + if (!http.begin(client, url)) return false; - http.setTimeout(15000); + http.setTimeout(15000); - int httpCode = http.GET(); - if (httpCode != HTTP_CODE_OK) { - http.end(); - return false; + 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; +} + +bool fetchHistory(const char* symbol, double* history, bool* loaded) { + WiFiClientSecure client; + client.setInsecure(); + + HTTPClient http; + String url = "https://query1.finance.yahoo.com/v8/finance/chart/" + String(symbol) + "?interval=1d&range=1mo"; + + 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(); + return false; + } + + String payload = http.getString(); + http.end(); + + String arraySearch = "\"close\":["; + int arrayIdx = payload.indexOf(arraySearch); + if (arrayIdx == -1) return false; + + int arrayStart = arrayIdx + arraySearch.length(); + int arrayEnd = payload.indexOf("]", arrayStart); + if (arrayEnd == -1) return false; + + String closeArray = payload.substring(arrayStart, arrayEnd); + + int dayIndex = 0; + int pos = 0; + + while (pos < closeArray.length() && dayIndex < HISTORY_DAYS) { + while (pos < closeArray.length() && (closeArray[pos] == ' ' || closeArray[pos] == ',')) pos++; + if (pos >= closeArray.length()) break; + + int valueStart = pos; + while (pos < closeArray.length() && closeArray[pos] != ',' && closeArray[pos] != ' ') pos++; + + String valueStr = closeArray.substring(valueStart, pos); + + if (valueStr.equals("null") || valueStr.length() == 0) { + pos++; + continue; } - String payload = http.getString(); - http.end(); + double value = valueStr.toDouble(); + if (value > 0) { + history[dayIndex++] = value; + } + pos++; + } - 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(); + if (dayIndex > 0) { + *loaded = true; return true; + } + return false; +} + +void fetchAllHistory() { + fetchHistory("BTC-USD", btcHistory, &btcHistoryLoaded); + fetchHistory("GC=F", goldHistory, &goldHistoryLoaded); + fetchHistory("SI=F", silverHistory, &silverHistoryLoaded); +} + +void drawSparkline(int x, int y, int width, int height, double* history, bool loaded, uint16_t color) { + if (!loaded) return; + + double minVal = history[0]; + double maxVal = history[0]; + int validDays = 0; + + for (int i = 0; i < HISTORY_DAYS; i++) { + if (history[i] > 0) { + if (history[i] < minVal) minVal = history[i]; + if (history[i] > maxVal) maxVal = history[i]; + validDays++; + } + } + + if (validDays < 2 || maxVal <= minVal) return; + + double range = maxVal - minVal; + if (range < 1) range = 1; + + for (int i = 0; i < validDays - 1; i++) { + int x1 = x + (i * width) / (validDays - 1); + int x2 = x + ((i + 1) * width) / (validDays - 1); + int y1 = y + height - (int)((history[i] - minVal) * height / range); + int y2 = y + height - (int)((history[i + 1] - minVal) * height / range); + + lcd.drawLine(x1, y1, x2, y2, color); + } + + int lastX = x + ((validDays - 1) * width) / (validDays - 1); + int lastY = y + height - (int)((history[validDays - 1] - minVal) * height / range); + lcd.drawPixel(lastX, lastY, TFT_WHITE); } void loadConfig() { @@ -499,13 +611,19 @@ void drawDashboard() { lcd.drawString("Bitcoin:", col1X, labelY); lcd.drawString(formatPriceUS(btcPrice, 0), col1X, priceY); + drawSparkline(col1X, labelY + 53, 240, 35, btcHistory, btcHistoryLoaded, TFT_ORANGE); + lcd.setTextColor(TFT_GOLD, TFT_BLACK); - lcd.drawString("Gold:", col1X, labelY + 65); - lcd.drawString(formatPriceUS(goldPrice, 2), col1X, priceY + 65); + lcd.drawString("Gold:", col1X, labelY + 113); + lcd.drawString(formatPriceUS(goldPrice, 2), col1X, priceY + 113); + + drawSparkline(col1X, labelY + 178, 240, 35, goldHistory, goldHistoryLoaded, TFT_GOLD); lcd.setTextColor(TFT_SILVER, TFT_BLACK); - lcd.drawString("Silver:", col1X, labelY + 130); - lcd.drawString(formatPriceUS(silverPrice, 2), col1X, priceY + 130); + lcd.drawString("Silver:", col1X, labelY + 233); + lcd.drawString(formatPriceUS(silverPrice, 2), col1X, priceY + 233); + + drawSparkline(col1X, labelY + 298, 240, 35, silverHistory, silverHistoryLoaded, TFT_SILVER); lcd.setTextColor(TFT_GREEN, TFT_BLACK); int stockY = labelY; @@ -808,7 +926,9 @@ void setup() { delay(1000); updatePrices(); + fetchAllHistory(); lastRefresh = millis(); + lastHistoryRefresh = millis(); drawDashboard(); return; } @@ -881,6 +1001,11 @@ void loop() { drawFooterCountdown(); } + if (currentScreen == SCREEN_DASHBOARD && millis() - lastHistoryRefresh > HISTORY_REFRESH_INTERVAL) { + fetchAllHistory(); + lastHistoryRefresh = millis(); + } + if (currentScreen == SCREEN_DASHBOARD && millis() - lastRefresh > REFRESH_INTERVAL) { showRefreshingMessage(); updatePrices();