Add sparklines for Bitcoin, Gold, and Silver with 30-day history
This commit is contained in:
parent
ebddf13f26
commit
a5d2d7bc7c
1 changed files with 151 additions and 26 deletions
177
src/main.cpp
177
src/main.cpp
|
|
@ -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,
|
||||||
|
|
@ -232,36 +242,138 @@ bool fetchPriceYahoo(const char* symbol, double* result) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fetchPriceFinnhub(const char* symbol, double* result) {
|
bool fetchPriceFinnhub(const char* symbol, double* result) {
|
||||||
WiFiClientSecure client;
|
WiFiClientSecure client;
|
||||||
client.setInsecure();
|
client.setInsecure();
|
||||||
|
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
String url = "https://finnhub.io/api/v1/quote?symbol=" + String(symbol) + "&token=" + String(FINNHUB_API_KEY);
|
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();
|
int httpCode = http.GET();
|
||||||
if (httpCode != HTTP_CODE_OK) {
|
if (httpCode != HTTP_CODE_OK) {
|
||||||
http.end();
|
http.end();
|
||||||
return false;
|
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();
|
double value = valueStr.toDouble();
|
||||||
http.end();
|
if (value > 0) {
|
||||||
|
history[dayIndex++] = value;
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
int idx = payload.indexOf("\"c\":");
|
if (dayIndex > 0) {
|
||||||
if (idx == -1) return false;
|
*loaded = true;
|
||||||
|
|
||||||
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;
|
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() {
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue