Add sparklines for Bitcoin, Gold, and Silver with 30-day history

This commit is contained in:
Chad 2026-03-30 09:50:27 -05:00
parent ebddf13f26
commit a5d2d7bc7c

View file

@ -153,6 +153,16 @@ const char* getIndexFriendlyName(const char* ticker) {
unsigned long lastRefresh = 0; unsigned long lastRefresh = 0;
const unsigned long REFRESH_INTERVAL = 180000; 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 { enum ScreenState {
SCREEN_DASHBOARD, SCREEN_DASHBOARD,
@ -264,6 +274,108 @@ bool fetchPriceFinnhub(const char* symbol, double* result) {
return true; 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;
}
double value = valueStr.toDouble();
if (value > 0) {
history[dayIndex++] = value;
}
pos++;
}
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() { void loadConfig() {
preferences.begin("dashboard", true); preferences.begin("dashboard", true);
@ -499,13 +611,19 @@ void drawDashboard() {
lcd.drawString("Bitcoin:", col1X, labelY); lcd.drawString("Bitcoin:", col1X, labelY);
lcd.drawString(formatPriceUS(btcPrice, 0), col1X, priceY); lcd.drawString(formatPriceUS(btcPrice, 0), col1X, priceY);
drawSparkline(col1X, labelY + 53, 240, 35, btcHistory, btcHistoryLoaded, TFT_ORANGE);
lcd.setTextColor(TFT_GOLD, TFT_BLACK); lcd.setTextColor(TFT_GOLD, TFT_BLACK);
lcd.drawString("Gold:", col1X, labelY + 65); lcd.drawString("Gold:", col1X, labelY + 113);
lcd.drawString(formatPriceUS(goldPrice, 2), col1X, priceY + 65); 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.setTextColor(TFT_SILVER, TFT_BLACK);
lcd.drawString("Silver:", col1X, labelY + 130); lcd.drawString("Silver:", col1X, labelY + 233);
lcd.drawString(formatPriceUS(silverPrice, 2), col1X, priceY + 130); lcd.drawString(formatPriceUS(silverPrice, 2), col1X, priceY + 233);
drawSparkline(col1X, labelY + 298, 240, 35, silverHistory, silverHistoryLoaded, TFT_SILVER);
lcd.setTextColor(TFT_GREEN, TFT_BLACK); lcd.setTextColor(TFT_GREEN, TFT_BLACK);
int stockY = labelY; int stockY = labelY;
@ -808,7 +926,9 @@ void setup() {
delay(1000); delay(1000);
updatePrices(); updatePrices();
fetchAllHistory();
lastRefresh = millis(); lastRefresh = millis();
lastHistoryRefresh = millis();
drawDashboard(); drawDashboard();
return; return;
} }
@ -881,6 +1001,11 @@ void loop() {
drawFooterCountdown(); drawFooterCountdown();
} }
if (currentScreen == SCREEN_DASHBOARD && millis() - lastHistoryRefresh > HISTORY_REFRESH_INTERVAL) {
fetchAllHistory();
lastHistoryRefresh = millis();
}
if (currentScreen == SCREEN_DASHBOARD && millis() - lastRefresh > REFRESH_INTERVAL) { if (currentScreen == SCREEN_DASHBOARD && millis() - lastRefresh > REFRESH_INTERVAL) {
showRefreshingMessage(); showRefreshingMessage();
updatePrices(); updatePrices();