Add HARD MONEY detail screen with sparklines and high/low data
- New SCREEN_HARD_MONEY_DETAIL accessible by tapping 'HARD MONEY' header - 3-column layout: Bitcoin | Gold | Silver with spot prices - Day/Month/Year high/low values (color-coded green/red) - Day high/low from intraday hourly data - Month high/low from last 22 trading days - Year high/low from 1-year daily data - Togglable sparklines: D(5m/5d)/W(1h/7d)/M(1d/1mo)/Y(1wk/1y)/A(1mo/max) - Default view is Monthly sparkline - Auto-refresh on period change
This commit is contained in:
parent
7d932adb20
commit
d0c8459a17
1 changed files with 676 additions and 54 deletions
638
src/main.cpp
638
src/main.cpp
|
|
@ -13,6 +13,7 @@
|
|||
#include <WiFiClientSecure.h>
|
||||
#include <HTTPClient.h>
|
||||
#include <Preferences.h>
|
||||
#include <time.h>
|
||||
|
||||
#define SCREEN_WIDTH 800
|
||||
#define SCREEN_HEIGHT 480
|
||||
|
|
@ -159,6 +160,11 @@ const unsigned long WIFI_CONNECT_TIMEOUT = 15000;
|
|||
bool wifiConnecting = false;
|
||||
unsigned long wifiConnectStartTime = 0;
|
||||
|
||||
bool timeSynced = false;
|
||||
bool firstBoot = true;
|
||||
const char* ntpServer = "pool.ntp.org";
|
||||
bool displayFlipped = false;
|
||||
|
||||
#define HISTORY_DAYS 30
|
||||
double btcHistory[HISTORY_DAYS] = {0};
|
||||
double goldHistory[HISTORY_DAYS] = {0};
|
||||
|
|
@ -168,12 +174,31 @@ bool goldHistoryLoaded = false;
|
|||
bool silverHistoryLoaded = false;
|
||||
unsigned long lastHistoryRefresh = 0;
|
||||
|
||||
#define HM_HISTORY_MAX 100
|
||||
double btcHmHistory[HM_HISTORY_MAX] = {0};
|
||||
double goldHmHistory[HM_HISTORY_MAX] = {0};
|
||||
double silverHmHistory[HM_HISTORY_MAX] = {0};
|
||||
int btcHmCount = 0;
|
||||
int goldHmCount = 0;
|
||||
int silverHmCount = 0;
|
||||
bool btcHmLoaded = false;
|
||||
bool goldHmLoaded = false;
|
||||
bool silverHmLoaded = false;
|
||||
double btcHighDay = 0, btcLowDay = 0, btcHighMonth = 0, btcLowMonth = 0, btcHighYear = 0, btcLowYear = 0;
|
||||
double goldHighDay = 0, goldLowDay = 0, goldHighMonth = 0, goldLowMonth = 0, goldHighYear = 0, goldLowYear = 0;
|
||||
double silverHighDay = 0, silverLowDay = 0, silverHighMonth = 0, silverLowMonth = 0, silverHighYear = 0, silverLowYear = 0;
|
||||
int hmSparklinePeriod = 2;
|
||||
bool hmNeedsRefresh = false;
|
||||
unsigned long hmLastFetch = 0;
|
||||
const unsigned long HM_FETCH_INTERVAL = 60000;
|
||||
|
||||
enum ScreenState {
|
||||
SCREEN_DASHBOARD,
|
||||
SCREEN_CUSTOMIZE_STOCKS,
|
||||
SCREEN_CUSTOMIZE_INDICES,
|
||||
SCREEN_WIFI_CONFIG,
|
||||
SCREEN_WIFI_PASSWORD
|
||||
SCREEN_WIFI_PASSWORD,
|
||||
SCREEN_HARD_MONEY_DETAIL
|
||||
};
|
||||
ScreenState currentScreen = SCREEN_DASHBOARD;
|
||||
|
||||
|
|
@ -360,6 +385,223 @@ void fetchAllHistory() {
|
|||
fetchHistory("SI=F", silverHistory, &silverHistoryLoaded);
|
||||
}
|
||||
|
||||
bool fetchHmHistoryForSymbol(const char* symbol, double* history, int* count, int period) {
|
||||
const char* interval;
|
||||
const char* range;
|
||||
|
||||
switch (period) {
|
||||
case 0: interval = "5m"; range = "5d"; break;
|
||||
case 1: interval = "1h"; range = "7d"; break;
|
||||
case 2: interval = "1d"; range = "1mo"; break;
|
||||
case 3: interval = "1wk"; range = "1y"; break;
|
||||
case 4: interval = "1mo"; range = "max"; break;
|
||||
default: interval = "1d"; range = "1mo"; break;
|
||||
}
|
||||
|
||||
WiFiClientSecure client;
|
||||
client.setInsecure();
|
||||
|
||||
HTTPClient http;
|
||||
String url = "https://query1.finance.yahoo.com/v8/finance/chart/" + String(symbol) + "?interval=" + String(interval) + "&range=" + String(range);
|
||||
|
||||
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);
|
||||
|
||||
*count = 0;
|
||||
int pos = 0;
|
||||
|
||||
while (pos < closeArray.length() && *count < HM_HISTORY_MAX) {
|
||||
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) {
|
||||
double value = valueStr.toDouble();
|
||||
if (value > 0) {
|
||||
history[*count] = value;
|
||||
(*count)++;
|
||||
}
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
|
||||
return *count > 0;
|
||||
}
|
||||
|
||||
void fetchHmHighLowForSymbol(const char* symbol, double* highDay, double* lowDay, double* highMonth, double* lowMonth, double* highYear, double* lowYear) {
|
||||
if (WiFi.status() != WL_CONNECTED) return;
|
||||
|
||||
WiFiClientSecure client;
|
||||
client.setInsecure();
|
||||
HTTPClient http;
|
||||
|
||||
// First API call: hourly data for today's intraday high/low
|
||||
String url = "https://query1.finance.yahoo.com/v8/finance/chart/" + String(symbol) + "?interval=1h&range=1d";
|
||||
if (http.begin(client, url)) {
|
||||
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) {
|
||||
String payload = http.getString();
|
||||
http.end();
|
||||
|
||||
String arraySearch = "\"close\":[";
|
||||
int arrayIdx = payload.indexOf(arraySearch);
|
||||
if (arrayIdx != -1) {
|
||||
int arrayStart = arrayIdx + arraySearch.length();
|
||||
int arrayEnd = payload.indexOf("]", arrayStart);
|
||||
if (arrayEnd != -1) {
|
||||
String closeArray = payload.substring(arrayStart, arrayEnd);
|
||||
|
||||
double minDay = 1e18, maxDay = 0;
|
||||
int pos = 0;
|
||||
while (pos < closeArray.length()) {
|
||||
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) {
|
||||
double value = valueStr.toDouble();
|
||||
if (value > 0) {
|
||||
if (value < minDay) minDay = value;
|
||||
if (value > maxDay) maxDay = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (maxDay > 0) {
|
||||
*highDay = maxDay;
|
||||
*lowDay = minDay;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
http.end();
|
||||
}
|
||||
}
|
||||
|
||||
// Second API call: daily data for month and year high/low
|
||||
url = "https://query1.finance.yahoo.com/v8/finance/chart/" + String(symbol) + "?interval=1d&range=1y";
|
||||
if (!http.begin(client, url)) return;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
String payload = http.getString();
|
||||
http.end();
|
||||
|
||||
String arraySearch = "\"close\":[";
|
||||
int arrayIdx = payload.indexOf(arraySearch);
|
||||
if (arrayIdx == -1) return;
|
||||
|
||||
int arrayStart = arrayIdx + arraySearch.length();
|
||||
int arrayEnd = payload.indexOf("]", arrayStart);
|
||||
if (arrayEnd == -1) return;
|
||||
|
||||
String closeArray = payload.substring(arrayStart, arrayEnd);
|
||||
|
||||
// First pass: count values and find year high/low
|
||||
double minYear = 1e18, maxYear = 0;
|
||||
int valueCount = 0;
|
||||
int pos = 0;
|
||||
|
||||
while (pos < closeArray.length()) {
|
||||
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) {
|
||||
double value = valueStr.toDouble();
|
||||
if (value > 0) {
|
||||
valueCount++;
|
||||
if (value < minYear) minYear = value;
|
||||
if (value > maxYear) maxYear = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (valueCount > 0) {
|
||||
*highYear = maxYear;
|
||||
*lowYear = minYear;
|
||||
|
||||
// Second pass: find month high/low (last 22 trading days)
|
||||
int monthStart = valueCount > 22 ? valueCount - 22 : 0;
|
||||
double minMonth = 1e18, maxMonth = 0;
|
||||
int idx = 0;
|
||||
pos = 0;
|
||||
while (pos < closeArray.length()) {
|
||||
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) {
|
||||
double value = valueStr.toDouble();
|
||||
if (value > 0) {
|
||||
if (idx >= monthStart) {
|
||||
if (value < minMonth) minMonth = value;
|
||||
if (value > maxMonth) maxMonth = value;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
*highMonth = maxMonth;
|
||||
*lowMonth = minMonth;
|
||||
}
|
||||
}
|
||||
|
||||
void fetchAllHmData(int period) {
|
||||
btcHmCount = goldHmCount = silverHmCount = 0;
|
||||
btcHmLoaded = goldHmLoaded = silverHmLoaded = false;
|
||||
|
||||
fetchHmHistoryForSymbol("BTC-USD", btcHmHistory, &btcHmCount, period);
|
||||
btcHmLoaded = btcHmCount > 0;
|
||||
|
||||
fetchHmHistoryForSymbol("GC=F", goldHmHistory, &goldHmCount, period);
|
||||
goldHmLoaded = goldHmCount > 0;
|
||||
|
||||
fetchHmHistoryForSymbol("SI=F", silverHmHistory, &silverHmCount, period);
|
||||
silverHmLoaded = silverHmCount > 0;
|
||||
|
||||
fetchHmHighLowForSymbol("BTC-USD", &btcHighDay, &btcLowDay, &btcHighMonth, &btcLowMonth, &btcHighYear, &btcLowYear);
|
||||
fetchHmHighLowForSymbol("GC=F", &goldHighDay, &goldLowDay, &goldHighMonth, &goldLowMonth, &goldHighYear, &goldLowYear);
|
||||
fetchHmHighLowForSymbol("SI=F", &silverHighDay, &silverLowDay, &silverHighMonth, &silverLowMonth, &silverHighYear, &silverLowYear);
|
||||
|
||||
hmLastFetch = millis();
|
||||
}
|
||||
|
||||
void drawSparkline(int x, int y, int width, int height, double* history, bool loaded, uint16_t color) {
|
||||
if (!loaded) return;
|
||||
|
||||
|
|
@ -437,6 +679,88 @@ void saveConfig() {
|
|||
preferences.end();
|
||||
}
|
||||
|
||||
int dayOfWeek(int year, int month, int day) {
|
||||
if (month < 3) {
|
||||
month += 12;
|
||||
year--;
|
||||
}
|
||||
return (day + 13 * (month + 1) / 5 + year + year / 4 - year / 100 + year / 400) % 7;
|
||||
}
|
||||
|
||||
bool isUSDST(int year, int month, int day, int hour) {
|
||||
int springDOW = dayOfWeek(year, 3, 8);
|
||||
int springStart = 8 + ((14 - springDOW) % 7);
|
||||
|
||||
int fallDOW = dayOfWeek(year, 11, 1);
|
||||
int fallEnd = 1 + ((14 - fallDOW) % 7);
|
||||
|
||||
if (month < 3 || month > 11) return false;
|
||||
if (month > 3 && month < 11) return true;
|
||||
|
||||
if (month == 3) {
|
||||
if (day < springStart) return false;
|
||||
if (day > springStart) return true;
|
||||
return hour >= 2;
|
||||
}
|
||||
|
||||
if (month == 11) {
|
||||
if (day < fallEnd) return true;
|
||||
if (day > fallEnd) return false;
|
||||
return hour < 2;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isUSMarketOpen() {
|
||||
if (!timeSynced) return firstBoot;
|
||||
|
||||
time_t now = time(nullptr);
|
||||
struct tm* utc = gmtime(&now);
|
||||
|
||||
int utcHour = utc->tm_hour;
|
||||
int utcMinute = utc->tm_min;
|
||||
int year = utc->tm_year + 1900;
|
||||
int month = utc->tm_mon + 1;
|
||||
int day = utc->tm_mday;
|
||||
int utcWday = utc->tm_wday;
|
||||
|
||||
bool dst = isUSDST(year, month, day, utcHour);
|
||||
int etOffset = dst ? -4 : -5;
|
||||
|
||||
int etHour = utcHour + etOffset;
|
||||
int etWday = utcWday;
|
||||
|
||||
if (etHour < 0) {
|
||||
etHour += 24;
|
||||
etWday = (etWday + 6) % 7;
|
||||
} else if (etHour >= 24) {
|
||||
etHour -= 24;
|
||||
etWday = (etWday + 1) % 7;
|
||||
}
|
||||
|
||||
if (etWday == 0 || etWday == 6) return false;
|
||||
|
||||
if (etHour < 9 || etHour >= 16) return false;
|
||||
if (etHour == 9 && utcMinute < 30) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void syncTime() {
|
||||
configTime(0, 0, ntpServer);
|
||||
|
||||
int retries = 0;
|
||||
while (time(nullptr) < 100000 && retries < 20) {
|
||||
delay(500);
|
||||
retries++;
|
||||
}
|
||||
|
||||
if (time(nullptr) >= 100000) {
|
||||
timeSynced = true;
|
||||
}
|
||||
}
|
||||
|
||||
void updatePrices() {
|
||||
if (WiFi.status() != WL_CONNECTED) return;
|
||||
|
||||
|
|
@ -444,6 +768,7 @@ void updatePrices() {
|
|||
fetchPriceYahoo("GC=F", &goldPrice);
|
||||
fetchPriceYahoo("SI=F", &silverPrice);
|
||||
|
||||
if (isUSMarketOpen()) {
|
||||
for (int i = 0; i < MAX_STOCKS; i++) {
|
||||
if (strlen(stockTickers[i]) > 0) {
|
||||
fetchPriceFinnhub(stockTickers[i], &stockPrices[i]);
|
||||
|
|
@ -457,6 +782,11 @@ void updatePrices() {
|
|||
}
|
||||
}
|
||||
|
||||
if (firstBoot) {
|
||||
firstBoot = false;
|
||||
}
|
||||
}
|
||||
|
||||
void drawKeyboard(int y) {
|
||||
const char* rows[] = {
|
||||
"QWERTYUIOP",
|
||||
|
|
@ -714,6 +1044,42 @@ lcd.setTextColor(TFT_WHITE, TFT_ORANGE);
|
|||
lcd.drawString("BACK", 125, SCREEN_HEIGHT - 30);
|
||||
}
|
||||
|
||||
void drawGearIcon(int cx, int cy, int radius, uint16_t color) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
float angle = i * 45.0 * DEG_TO_RAD;
|
||||
float halfWidth = 0.35;
|
||||
float cosA = cos(angle);
|
||||
float sinA = sin(angle);
|
||||
int x0 = cx + cosA * radius;
|
||||
int y0 = cy + sinA * radius;
|
||||
int x1 = cx + cosA * (radius + 6);
|
||||
int y1 = cy + sinA * (radius + 6);
|
||||
int dx = halfWidth * (-sinA) * radius;
|
||||
int dy = halfWidth * cosA * radius;
|
||||
lcd.fillTriangle(x0 - dx, y0 - dy, x1 - dx, y1 - dy, x1 + dx, y1 + dy, color);
|
||||
lcd.fillTriangle(x0 - dx, y0 - dy, x1 + dx, y1 + dy, x0 + dx, y0 + dy, color);
|
||||
}
|
||||
lcd.fillCircle(cx, cy, radius, color);
|
||||
lcd.fillCircle(cx, cy, radius - 3, TFT_ORANGE);
|
||||
}
|
||||
|
||||
void drawFlipIcon(int cx, int cy, int radius, uint16_t color) {
|
||||
lcd.setColor(color);
|
||||
lcd.drawArc(cx, cy, radius, radius - 3, 60, 300);
|
||||
float arrowAngle = 60.0 * DEG_TO_RAD;
|
||||
int arrowX = cx + cos(arrowAngle) * radius;
|
||||
int arrowY = cy - sin(arrowAngle) * radius;
|
||||
float tangentAngle = arrowAngle - PI / 2;
|
||||
int tipX = arrowX + cos(tangentAngle) * 6;
|
||||
int tipY = arrowY - sin(tangentAngle) * 6;
|
||||
lcd.fillTriangle(
|
||||
arrowX + cos(tangentAngle) * 2 - sin(tangentAngle) * 4,
|
||||
arrowY - sin(tangentAngle) * 2 - cos(tangentAngle) * 4,
|
||||
arrowX + cos(tangentAngle) * 2 + sin(tangentAngle) * 4,
|
||||
arrowY - sin(tangentAngle) * 2 + cos(tangentAngle) * 4,
|
||||
tipX, tipY, color);
|
||||
}
|
||||
|
||||
void drawDashboard() {
|
||||
lcd.fillScreen(TFT_BLACK);
|
||||
|
||||
|
|
@ -724,6 +1090,7 @@ void drawDashboard() {
|
|||
lcd.drawString("HARD MONEY", 20, 22);
|
||||
lcd.drawString("STOCKS", 280, 22);
|
||||
lcd.drawString("INDICES", 540, 22);
|
||||
drawFlipIcon(SCREEN_WIDTH - 32, 22, 12, TFT_WHITE);
|
||||
|
||||
int col1X = 20;
|
||||
int col2X = 280;
|
||||
|
|
@ -777,8 +1144,13 @@ indexY += 65;
|
|||
String wifiStatus;
|
||||
if (wifiConnecting) {
|
||||
wifiStatus = "Connecting...";
|
||||
} else if (WiFi.status() == WL_CONNECTED) {
|
||||
wifiStatus = WiFi.SSID();
|
||||
if (wifiStatus.length() > 20) {
|
||||
wifiStatus = wifiStatus.substring(0, 17) + "...";
|
||||
}
|
||||
} else {
|
||||
wifiStatus = WiFi.status() == WL_CONNECTED ? "OK" : "OFFLINE";
|
||||
wifiStatus = "OFFLINE";
|
||||
}
|
||||
lcd.drawString("WiFi: " + wifiStatus, 20, SCREEN_HEIGHT - 20);
|
||||
|
||||
|
|
@ -791,8 +1163,7 @@ indexY += 65;
|
|||
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);
|
||||
drawGearIcon(SCREEN_WIDTH - 32, SCREEN_HEIGHT - 20, 9, TFT_WHITE);
|
||||
}
|
||||
|
||||
void drawFullKeyboard(int y, bool showSpecials) {
|
||||
|
|
@ -1277,8 +1648,215 @@ void showRefreshingMessage() {
|
|||
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);
|
||||
drawGearIcon(SCREEN_WIDTH - 32, SCREEN_HEIGHT - 20, 9, TFT_WHITE);
|
||||
}
|
||||
|
||||
void drawHardMoneyDetailScreen() {
|
||||
lcd.fillScreen(TFT_BLACK);
|
||||
|
||||
lcd.fillSmoothRoundRect(0, 0, SCREEN_WIDTH, 45, 8, TFT_ORANGE);
|
||||
lcd.setTextColor(TFT_WHITE, TFT_ORANGE);
|
||||
lcd.setFont(&fonts::DejaVu24);
|
||||
lcd.setTextDatum(textdatum_t::middle_center);
|
||||
lcd.drawString("HARD MONEY", SCREEN_WIDTH / 2, 22);
|
||||
|
||||
int col1X = 50;
|
||||
int col2X = 310;
|
||||
int col3X = 570;
|
||||
int colWidth = 180;
|
||||
|
||||
int headerY = 65;
|
||||
int priceY = 90;
|
||||
|
||||
lcd.setTextColor(TFT_ORANGE, TFT_BLACK);
|
||||
lcd.setFont(&fonts::Roboto_Thin_24);
|
||||
lcd.setTextDatum(textdatum_t::middle_left);
|
||||
lcd.drawString("BITCOIN", col1X, headerY);
|
||||
lcd.drawString(formatPriceUS(btcPrice, 0), col1X, priceY);
|
||||
|
||||
lcd.setTextColor(TFT_GOLD, TFT_BLACK);
|
||||
lcd.drawString("GOLD", col2X, headerY);
|
||||
lcd.drawString(formatPriceUS(goldPrice, 2), col2X, priceY);
|
||||
|
||||
lcd.setTextColor(TFT_SILVER, TFT_BLACK);
|
||||
lcd.drawString("SILVER", col3X, headerY);
|
||||
lcd.drawString(formatPriceUS(silverPrice, 2), col3X, priceY);
|
||||
|
||||
int highLowY = priceY + 30;
|
||||
int rowHeight = 22;
|
||||
|
||||
lcd.setFont(&fonts::DejaVu12);
|
||||
lcd.setTextDatum(textdatum_t::middle_left);
|
||||
|
||||
lcd.setTextColor(TFT_GREEN, TFT_BLACK);
|
||||
lcd.drawString("Day High:", col1X, highLowY);
|
||||
lcd.drawString(formatPriceUS(btcHighDay, 0, true), col1X + 65, highLowY);
|
||||
lcd.setTextColor(TFT_GREEN, TFT_BLACK);
|
||||
lcd.drawString("Day High:", col2X, highLowY);
|
||||
lcd.drawString(formatPriceUS(goldHighDay, 2, true), col2X + 65, highLowY);
|
||||
lcd.drawString("Day High:", col3X, highLowY);
|
||||
lcd.drawString(formatPriceUS(silverHighDay, 2, true), col3X + 65, highLowY);
|
||||
|
||||
lcd.setTextColor(0xF800, TFT_BLACK);
|
||||
lcd.drawString("Day Low:", col1X, highLowY + rowHeight);
|
||||
lcd.drawString(formatPriceUS(btcLowDay, 0, true), col1X + 65, highLowY + rowHeight);
|
||||
lcd.drawString("Day Low:", col2X, highLowY + rowHeight);
|
||||
lcd.drawString(formatPriceUS(goldLowDay, 2, true), col2X + 65, highLowY + rowHeight);
|
||||
lcd.drawString("Day Low:", col3X, highLowY + rowHeight);
|
||||
lcd.drawString(formatPriceUS(silverLowDay, 2, true), col3X + 65, highLowY + rowHeight);
|
||||
|
||||
lcd.setTextColor(TFT_GREEN, TFT_BLACK);
|
||||
lcd.drawString("Mo High:", col1X, highLowY + rowHeight * 2);
|
||||
lcd.drawString(formatPriceUS(btcHighMonth, 0, true), col1X + 65, highLowY + rowHeight * 2);
|
||||
lcd.drawString("Mo High:", col2X, highLowY + rowHeight * 2);
|
||||
lcd.drawString(formatPriceUS(goldHighMonth, 2, true), col2X + 65, highLowY + rowHeight * 2);
|
||||
lcd.drawString("Mo High:", col3X, highLowY + rowHeight * 2);
|
||||
lcd.drawString(formatPriceUS(silverHighMonth, 2, true), col3X + 65, highLowY + rowHeight * 2);
|
||||
|
||||
lcd.setTextColor(0xF800, TFT_BLACK);
|
||||
lcd.drawString("Mo Low:", col1X, highLowY + rowHeight * 3);
|
||||
lcd.drawString(formatPriceUS(btcLowMonth, 0, true), col1X + 65, highLowY + rowHeight * 3);
|
||||
lcd.drawString("Mo Low:", col2X, highLowY + rowHeight * 3);
|
||||
lcd.drawString(formatPriceUS(goldLowMonth, 2, true), col2X + 65, highLowY + rowHeight * 3);
|
||||
lcd.drawString("Mo Low:", col3X, highLowY + rowHeight * 3);
|
||||
lcd.drawString(formatPriceUS(silverLowMonth, 2, true), col3X + 65, highLowY + rowHeight * 3);
|
||||
|
||||
lcd.setTextColor(TFT_GREEN, TFT_BLACK);
|
||||
lcd.drawString("Yr High:", col1X, highLowY + rowHeight * 4);
|
||||
lcd.drawString(formatPriceUS(btcHighYear, 0, true), col1X + 65, highLowY + rowHeight * 4);
|
||||
lcd.drawString("Yr High:", col2X, highLowY + rowHeight * 4);
|
||||
lcd.drawString(formatPriceUS(goldHighYear, 2, true), col2X + 65, highLowY + rowHeight * 4);
|
||||
lcd.drawString("Yr High:", col3X, highLowY + rowHeight * 4);
|
||||
lcd.drawString(formatPriceUS(silverHighYear, 2, true), col3X + 65, highLowY + rowHeight * 4);
|
||||
|
||||
lcd.setTextColor(0xF800, TFT_BLACK);
|
||||
lcd.drawString("Yr Low:", col1X, highLowY + rowHeight * 5);
|
||||
lcd.drawString(formatPriceUS(btcLowYear, 0, true), col1X + 65, highLowY + rowHeight * 5);
|
||||
lcd.drawString("Yr Low:", col2X, highLowY + rowHeight * 5);
|
||||
lcd.drawString(formatPriceUS(goldLowYear, 2, true), col2X + 65, highLowY + rowHeight * 5);
|
||||
lcd.drawString("Yr Low:", col3X, highLowY + rowHeight * 5);
|
||||
lcd.drawString(formatPriceUS(silverLowYear, 2, true), col3X + 65, highLowY + rowHeight * 5);
|
||||
|
||||
int sparklineY = highLowY + rowHeight * 6 + 15;
|
||||
|
||||
const char* periodLabels[] = {"D", "W", "M", "Y", "A"};
|
||||
int btnWidth = 50;
|
||||
int btnHeight = 30;
|
||||
int btnGap = 10;
|
||||
int totalBtnWidth = 5 * btnWidth + 4 * btnGap;
|
||||
int btnStartX = (SCREEN_WIDTH - totalBtnWidth) / 2;
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
int btnX = btnStartX + i * (btnWidth + btnGap);
|
||||
uint16_t bgColor = (hmSparklinePeriod == i) ? TFT_ORANGE : TFT_DARKGRAY;
|
||||
lcd.fillSmoothRoundRect(btnX, sparklineY, btnWidth, btnHeight, 6, bgColor);
|
||||
lcd.setTextColor(TFT_WHITE, bgColor);
|
||||
lcd.setFont(&fonts::DejaVu18);
|
||||
lcd.setTextDatum(textdatum_t::middle_center);
|
||||
lcd.drawString(periodLabels[i], btnX + btnWidth / 2, sparklineY + btnHeight / 2);
|
||||
}
|
||||
|
||||
int sparklineDrawY = sparklineY + btnHeight + 15;
|
||||
int sparklineHeight = 80;
|
||||
|
||||
if (btcHmLoaded && btcHmCount > 1) {
|
||||
double minVal = btcHmHistory[0];
|
||||
double maxVal = btcHmHistory[0];
|
||||
for (int i = 0; i < btcHmCount; i++) {
|
||||
if (btcHmHistory[i] < minVal) minVal = btcHmHistory[i];
|
||||
if (btcHmHistory[i] > maxVal) maxVal = btcHmHistory[i];
|
||||
}
|
||||
double range = maxVal - minVal;
|
||||
if (range < 1) range = 1;
|
||||
|
||||
for (int i = 0; i < btcHmCount - 1; i++) {
|
||||
int x1 = col1X + (i * colWidth) / (btcHmCount - 1);
|
||||
int x2 = col1X + ((i + 1) * colWidth) / (btcHmCount - 1);
|
||||
int y1 = sparklineDrawY + sparklineHeight - (int)((btcHmHistory[i] - minVal) * sparklineHeight / range);
|
||||
int y2 = sparklineDrawY + sparklineHeight - (int)((btcHmHistory[i + 1] - minVal) * sparklineHeight / range);
|
||||
lcd.drawLine(x1, y1, x2, y2, TFT_ORANGE);
|
||||
}
|
||||
}
|
||||
|
||||
if (goldHmLoaded && goldHmCount > 1) {
|
||||
double minVal = goldHmHistory[0];
|
||||
double maxVal = goldHmHistory[0];
|
||||
for (int i = 0; i < goldHmCount; i++) {
|
||||
if (goldHmHistory[i] < minVal) minVal = goldHmHistory[i];
|
||||
if (goldHmHistory[i] > maxVal) maxVal = goldHmHistory[i];
|
||||
}
|
||||
double range = maxVal - minVal;
|
||||
if (range < 1) range = 1;
|
||||
|
||||
for (int i = 0; i < goldHmCount - 1; i++) {
|
||||
int x1 = col2X + (i * colWidth) / (goldHmCount - 1);
|
||||
int x2 = col2X + ((i + 1) * colWidth) / (goldHmCount - 1);
|
||||
int y1 = sparklineDrawY + sparklineHeight - (int)((goldHmHistory[i] - minVal) * sparklineHeight / range);
|
||||
int y2 = sparklineDrawY + sparklineHeight - (int)((goldHmHistory[i + 1] - minVal) * sparklineHeight / range);
|
||||
lcd.drawLine(x1, y1, x2, y2, TFT_GOLD);
|
||||
}
|
||||
}
|
||||
|
||||
if (silverHmLoaded && silverHmCount > 1) {
|
||||
double minVal = silverHmHistory[0];
|
||||
double maxVal = silverHmHistory[0];
|
||||
for (int i = 0; i < silverHmCount; i++) {
|
||||
if (silverHmHistory[i] < minVal) minVal = silverHmHistory[i];
|
||||
if (silverHmHistory[i] > maxVal) maxVal = silverHmHistory[i];
|
||||
}
|
||||
double range = maxVal - minVal;
|
||||
if (range < 1) range = 1;
|
||||
|
||||
for (int i = 0; i < silverHmCount - 1; i++) {
|
||||
int x1 = col3X + (i * colWidth) / (silverHmCount - 1);
|
||||
int x2 = col3X + ((i + 1) * colWidth) / (silverHmCount - 1);
|
||||
int y1 = sparklineDrawY + sparklineHeight - (int)((silverHmHistory[i] - minVal) * sparklineHeight / range);
|
||||
int y2 = sparklineDrawY + sparklineHeight - (int)((silverHmHistory[i + 1] - minVal) * sparklineHeight / range);
|
||||
lcd.drawLine(x1, y1, x2, y2, TFT_SILVER);
|
||||
}
|
||||
}
|
||||
|
||||
lcd.fillSmoothRoundRect(20, SCREEN_HEIGHT - 50, 150, 40, 8, TFT_GRAY);
|
||||
lcd.setTextColor(TFT_WHITE, TFT_GRAY);
|
||||
lcd.setFont(&fonts::DejaVu18);
|
||||
lcd.setTextDatum(textdatum_t::middle_center);
|
||||
lcd.drawString("BACK", 95, SCREEN_HEIGHT - 30);
|
||||
}
|
||||
|
||||
bool handleHardMoneyTouch(int32_t x, int32_t y) {
|
||||
if (x >= 20 && x < 170 && y >= SCREEN_HEIGHT - 50 && y < SCREEN_HEIGHT - 10) {
|
||||
currentScreen = SCREEN_DASHBOARD;
|
||||
needsRefresh = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
int btnWidth = 50;
|
||||
int btnHeight = 30;
|
||||
int btnGap = 10;
|
||||
int totalBtnWidth = 5 * btnWidth + 4 * btnGap;
|
||||
int btnStartX = (SCREEN_WIDTH - totalBtnWidth) / 2;
|
||||
|
||||
int headerY = 55;
|
||||
int priceY = 80;
|
||||
int highLowY = priceY + 30;
|
||||
int rowHeight = 22;
|
||||
int sparklineY = highLowY + rowHeight * 6 + 15;
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
int btnX = btnStartX + i * (btnWidth + btnGap);
|
||||
if (x >= btnX && x < btnX + btnWidth && y >= sparklineY && y < sparklineY + btnHeight) {
|
||||
if (hmSparklinePeriod != i) {
|
||||
hmSparklinePeriod = i;
|
||||
btcHmLoaded = false;
|
||||
goldHmLoaded = false;
|
||||
silverHmLoaded = false;
|
||||
hmNeedsRefresh = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void refreshDataAndShow() {
|
||||
|
|
@ -1290,7 +1868,24 @@ void refreshDataAndShow() {
|
|||
}
|
||||
|
||||
bool handleDashboardTouch(int32_t x, int32_t y) {
|
||||
if (x >= SCREEN_WIDTH - 160 && x < SCREEN_WIDTH - 20 && y >= SCREEN_HEIGHT - 36 && y < SCREEN_HEIGHT - 4) {
|
||||
if (x >= SCREEN_WIDTH - 55 && x < SCREEN_WIDTH - 10 && y >= 5 && y < 40) {
|
||||
displayFlipped = !displayFlipped;
|
||||
preferences.begin("dashboard", false);
|
||||
preferences.putBool("flipped", displayFlipped);
|
||||
preferences.end();
|
||||
lcd.setRotation(displayFlipped ? 2 : 0);
|
||||
needsRefresh = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (x >= 20 && x < 200 && y >= 0 && y < 45) {
|
||||
currentScreen = SCREEN_HARD_MONEY_DETAIL;
|
||||
hmSparklinePeriod = 2;
|
||||
hmNeedsRefresh = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (x >= SCREEN_WIDTH - 50 && x < SCREEN_WIDTH - 14 && y >= SCREEN_HEIGHT - 32 && y < SCREEN_HEIGHT - 8) {
|
||||
currentScreen = SCREEN_CUSTOMIZE_STOCKS;
|
||||
selectedStockSlot = -1;
|
||||
keyboardPos = 0;
|
||||
|
|
@ -1322,7 +1917,12 @@ void setup() {
|
|||
|
||||
lcd.init();
|
||||
lcd.setBrightness(255);
|
||||
lcd.setRotation(0);
|
||||
|
||||
preferences.begin("dashboard", true);
|
||||
displayFlipped = preferences.getBool("flipped", false);
|
||||
preferences.end();
|
||||
|
||||
lcd.setRotation(displayFlipped ? 2 : 0);
|
||||
|
||||
lcd.fillScreen(TFT_WHITE);
|
||||
delay(250);
|
||||
|
|
@ -1420,6 +2020,9 @@ void loop() {
|
|||
case SCREEN_WIFI_PASSWORD:
|
||||
handleWifiPasswordTouch(x, y);
|
||||
break;
|
||||
case SCREEN_HARD_MONEY_DETAIL:
|
||||
handleHardMoneyTouch(x, y);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!needsRefresh) {
|
||||
|
|
@ -1441,6 +2044,9 @@ void loop() {
|
|||
case SCREEN_WIFI_PASSWORD:
|
||||
drawWifiPasswordScreen();
|
||||
break;
|
||||
case SCREEN_HARD_MONEY_DETAIL:
|
||||
drawHardMoneyDetailScreen();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1453,9 +2059,25 @@ void loop() {
|
|||
drawFooterCountdown();
|
||||
}
|
||||
|
||||
if (currentScreen == SCREEN_HARD_MONEY_DETAIL) {
|
||||
if (hmNeedsRefresh || !btcHmLoaded || !goldHmLoaded || !silverHmLoaded) {
|
||||
hmNeedsRefresh = false;
|
||||
drawHardMoneyDetailScreen();
|
||||
fetchAllHmData(hmSparklinePeriod);
|
||||
drawHardMoneyDetailScreen();
|
||||
}
|
||||
if (millis() - hmLastFetch > HM_FETCH_INTERVAL) {
|
||||
fetchAllHmData(hmSparklinePeriod);
|
||||
drawHardMoneyDetailScreen();
|
||||
}
|
||||
}
|
||||
|
||||
if (wifiConnecting) {
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
wifiConnecting = false;
|
||||
if (!timeSynced) {
|
||||
syncTime();
|
||||
}
|
||||
if (currentScreen == SCREEN_DASHBOARD) {
|
||||
drawDashboard();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue