Modern UI: antialiased DejaVu fonts, color-coded assets, proper RGB565 colors
This commit is contained in:
parent
29a65760c7
commit
28ca31e0e6
2 changed files with 417 additions and 375 deletions
125
docs/README.md
Normal file
125
docs/README.md
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
# Bitcoin Dashboard for Elecrow 7-inch HMI Display
|
||||||
|
|
||||||
|
A real-time financial dashboard displaying cryptocurrency, precious metals, stocks, and market indices on the Elecrow 7.0-inch ESP32 HMI Display.
|
||||||
|
|
||||||
|
## Hardware
|
||||||
|
|
||||||
|
- **Display**: Elecrow 7.0-inch ESP32 HMI Display (Advance series)
|
||||||
|
- **Resolution**: 800x480 RGB parallel interface
|
||||||
|
- **Touch**: GT911 capacitive touch controller
|
||||||
|
- **MCU**: ESP32-S3 with N4R8 module (4MB Flash, 8MB PSRAM)
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Real-time price data from Yahoo Finance API
|
||||||
|
- Auto-refresh every 5 minutes
|
||||||
|
- Touch to manual refresh
|
||||||
|
- WiFi credentials stored in persistent memory
|
||||||
|
- Antialiased fonts (DejaVu family)
|
||||||
|
|
||||||
|
## Displayed Assets
|
||||||
|
|
||||||
|
| Column | Assets |
|
||||||
|
|--------|--------|
|
||||||
|
| Hard Money | Bitcoin (BTC/USD), Gold, Silver |
|
||||||
|
| Stocks | MSTR, STRC |
|
||||||
|
| Indices | NASDAQ, DOW, S&P 500 |
|
||||||
|
|
||||||
|
## Color Scheme
|
||||||
|
|
||||||
|
| Asset | Color | Hex |
|
||||||
|
|-------|-------|-----|
|
||||||
|
| Bitcoin | Orange | `#F7931A` |
|
||||||
|
| Gold | Gold | `#FFD700` |
|
||||||
|
| Silver | Silver | `#C0C0C0` |
|
||||||
|
| Stocks/Indices | Dollar Green | `#00A86B` |
|
||||||
|
| Status Bar | Bitcoin Orange | `#F7931A` |
|
||||||
|
|
||||||
|
## Software Stack
|
||||||
|
|
||||||
|
- **Framework**: Arduino (PlatformIO)
|
||||||
|
- **Display Library**: LovyanGFX v1.2.19
|
||||||
|
- **Fonts**: DejaVu (antialiased)
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/chad/Documents/PlatformIO/Projects/bitcoin_dashboard
|
||||||
|
pio run -t upload
|
||||||
|
```
|
||||||
|
|
||||||
|
## PlatformIO Configuration
|
||||||
|
|
||||||
|
- **Platform**: espressif32@6.8.1
|
||||||
|
- **Board**: esp32-s3-devkitc-1
|
||||||
|
- **Partition**: huge_app.csv (3MB app, no OTA)
|
||||||
|
- **Flash Mode**: QIO @ 80MHz
|
||||||
|
- **PSRAM**: OPI (8MB)
|
||||||
|
|
||||||
|
## Data Source
|
||||||
|
|
||||||
|
Yahoo Finance API:
|
||||||
|
```
|
||||||
|
https://query1.finance.yahoo.com/v8/finance/chart/{SYMBOL}?interval=1d&range=1d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Symbol Mapping
|
||||||
|
|
||||||
|
| Display | Yahoo Symbol |
|
||||||
|
|---------|--------------|
|
||||||
|
| Bitcoin | BTC-USD |
|
||||||
|
| Gold | GC=F |
|
||||||
|
| Silver | SI=F |
|
||||||
|
| MSTR | MSTR |
|
||||||
|
| STRC | STRC |
|
||||||
|
| NASDAQ | ^IXIC |
|
||||||
|
| DOW | ^DJI |
|
||||||
|
| S&P 500 | ^GSPC |
|
||||||
|
|
||||||
|
## Pin Configuration
|
||||||
|
|
||||||
|
### RGB Display Interface
|
||||||
|
|
||||||
|
| Signal | GPIO |
|
||||||
|
|--------|------|
|
||||||
|
| D0-D15 | 15, 7, 6, 5, 4, 9, 46, 3, 8, 16, 1, 14, 21, 47, 48, 45 |
|
||||||
|
| HSYNC | GPIO 39 |
|
||||||
|
| VSYNC | GPIO 40 |
|
||||||
|
| DE | GPIO 41 |
|
||||||
|
| PCLK | GPIO 0 |
|
||||||
|
| Backlight | GPIO 2 |
|
||||||
|
|
||||||
|
### Touch Controller (GT911)
|
||||||
|
|
||||||
|
| Signal | GPIO |
|
||||||
|
|--------|------|
|
||||||
|
| SDA | GPIO 19 |
|
||||||
|
| SCL | GPIO 20 |
|
||||||
|
| I2C Address | 0x14 |
|
||||||
|
|
||||||
|
## WiFi Setup
|
||||||
|
|
||||||
|
WiFi credentials are stored in ESP32 preferences (persistent storage):
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
preferences.begin("wifi", true);
|
||||||
|
preferences.putString("ssid", "your_ssid");
|
||||||
|
preferences.putString("password", "your_password");
|
||||||
|
preferences.end();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
bitcoin_dashboard/
|
||||||
|
├── src/
|
||||||
|
│ └── main.cpp # Main application
|
||||||
|
├── docs/
|
||||||
|
│ └── README.md # This file
|
||||||
|
├── platformio.ini # PlatformIO configuration
|
||||||
|
└── .gitignore
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
587
src/main.cpp
587
src/main.cpp
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* Bitcoin Dashboard for Elecrow 7-inch HMI Display
|
* Bitcoin Dashboard for Elecrow 7-inch HMI Display
|
||||||
* Simplified version with better WiFi/API debugging
|
* Three-column layout with antialiased fonts
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
@ -16,412 +16,329 @@
|
||||||
#define SCREEN_WIDTH 800
|
#define SCREEN_WIDTH 800
|
||||||
#define SCREEN_HEIGHT 480
|
#define SCREEN_HEIGHT 480
|
||||||
|
|
||||||
#define TFT_WHITE 0xFFFF
|
#define TFT_WHITE 0xFFFF
|
||||||
#define TFT_BLACK 0x0000
|
#define TFT_BLACK 0x0000
|
||||||
#define TFT_RED 0xF800
|
#define TFT_ORANGE 0xFD20
|
||||||
#define TFT_GREEN 0x07E0
|
#define TFT_GOLD 0xFEA0
|
||||||
#define TFT_BLUE 0x001F
|
#define TFT_SILVER 0xC618
|
||||||
#define TFT_ORANGE 0xFD40
|
#define TFT_GREEN 0x07E0
|
||||||
#define TFT_GRAY 0x7BEF
|
|
||||||
|
|
||||||
class LGFX : public lgfx::LGFX_Device {
|
class LGFX : public lgfx::LGFX_Device {
|
||||||
public:
|
public:
|
||||||
lgfx::Bus_RGB _bus_instance;
|
lgfx::Bus_RGB _bus_instance;
|
||||||
lgfx::Panel_RGB _panel_instance;
|
lgfx::Panel_RGB _panel_instance;
|
||||||
lgfx::Light_PWM _light_instance;
|
lgfx::Light_PWM _light_instance;
|
||||||
lgfx::Touch_GT911 _touch_instance;
|
lgfx::Touch_GT911 _touch_instance;
|
||||||
|
|
||||||
LGFX(void) {
|
LGFX(void) {
|
||||||
{
|
{
|
||||||
auto cfg = _panel_instance.config();
|
auto cfg = _panel_instance.config();
|
||||||
cfg.memory_width = 800;
|
cfg.memory_width = 800;
|
||||||
cfg.memory_height = 480;
|
cfg.memory_height = 480;
|
||||||
cfg.panel_width = 800;
|
cfg.panel_width = 800;
|
||||||
cfg.panel_height = 480;
|
cfg.panel_height = 480;
|
||||||
cfg.offset_x = 0;
|
cfg.offset_x = 0;
|
||||||
cfg.offset_y = 0;
|
cfg.offset_y = 0;
|
||||||
_panel_instance.config(cfg);
|
_panel_instance.config(cfg);
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto cfg = _bus_instance.config();
|
|
||||||
cfg.panel = &_panel_instance;
|
|
||||||
cfg.pin_d0 = GPIO_NUM_15;
|
|
||||||
cfg.pin_d1 = GPIO_NUM_7;
|
|
||||||
cfg.pin_d2 = GPIO_NUM_6;
|
|
||||||
cfg.pin_d3 = GPIO_NUM_5;
|
|
||||||
cfg.pin_d4 = GPIO_NUM_4;
|
|
||||||
cfg.pin_d5 = GPIO_NUM_9;
|
|
||||||
cfg.pin_d6 = GPIO_NUM_46;
|
|
||||||
cfg.pin_d7 = GPIO_NUM_3;
|
|
||||||
cfg.pin_d8 = GPIO_NUM_8;
|
|
||||||
cfg.pin_d9 = GPIO_NUM_16;
|
|
||||||
cfg.pin_d10 = GPIO_NUM_1;
|
|
||||||
cfg.pin_d11 = GPIO_NUM_14;
|
|
||||||
cfg.pin_d12 = GPIO_NUM_21;
|
|
||||||
cfg.pin_d13 = GPIO_NUM_47;
|
|
||||||
cfg.pin_d14 = GPIO_NUM_48;
|
|
||||||
cfg.pin_d15 = GPIO_NUM_45;
|
|
||||||
cfg.pin_henable = GPIO_NUM_41;
|
|
||||||
cfg.pin_vsync = GPIO_NUM_40;
|
|
||||||
cfg.pin_hsync = GPIO_NUM_39;
|
|
||||||
cfg.pin_pclk = GPIO_NUM_0;
|
|
||||||
cfg.freq_write = 12000000;
|
|
||||||
cfg.hsync_polarity = 0;
|
|
||||||
cfg.hsync_front_porch = 40;
|
|
||||||
cfg.hsync_pulse_width = 48;
|
|
||||||
cfg.hsync_back_porch = 40;
|
|
||||||
cfg.vsync_polarity = 0;
|
|
||||||
cfg.vsync_front_porch = 1;
|
|
||||||
cfg.vsync_pulse_width = 31;
|
|
||||||
cfg.vsync_back_porch = 13;
|
|
||||||
cfg.pclk_active_neg = 1;
|
|
||||||
cfg.de_idle_high = 0;
|
|
||||||
cfg.pclk_idle_high = 0;
|
|
||||||
_bus_instance.config(cfg);
|
|
||||||
}
|
|
||||||
_panel_instance.setBus(&_bus_instance);
|
|
||||||
|
|
||||||
{
|
|
||||||
auto cfg = _light_instance.config();
|
|
||||||
cfg.pin_bl = GPIO_NUM_2;
|
|
||||||
_light_instance.config(cfg);
|
|
||||||
}
|
|
||||||
_panel_instance.light(&_light_instance);
|
|
||||||
|
|
||||||
{
|
|
||||||
auto cfg = _touch_instance.config();
|
|
||||||
cfg.x_min = 0;
|
|
||||||
cfg.x_max = 799;
|
|
||||||
cfg.y_min = 0;
|
|
||||||
cfg.y_max = 479;
|
|
||||||
cfg.pin_int = -1;
|
|
||||||
cfg.pin_rst = -1;
|
|
||||||
cfg.bus_shared = false;
|
|
||||||
cfg.offset_rotation = 0;
|
|
||||||
cfg.i2c_port = I2C_NUM_1;
|
|
||||||
cfg.pin_sda = GPIO_NUM_19;
|
|
||||||
cfg.pin_scl = GPIO_NUM_20;
|
|
||||||
cfg.freq = 400000;
|
|
||||||
cfg.i2c_addr = 0x14;
|
|
||||||
_touch_instance.config(cfg);
|
|
||||||
_panel_instance.setTouch(&_touch_instance);
|
|
||||||
}
|
|
||||||
setPanel(&_panel_instance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto cfg = _bus_instance.config();
|
||||||
|
cfg.panel = &_panel_instance;
|
||||||
|
cfg.pin_d0 = GPIO_NUM_15;
|
||||||
|
cfg.pin_d1 = GPIO_NUM_7;
|
||||||
|
cfg.pin_d2 = GPIO_NUM_6;
|
||||||
|
cfg.pin_d3 = GPIO_NUM_5;
|
||||||
|
cfg.pin_d4 = GPIO_NUM_4;
|
||||||
|
cfg.pin_d5 = GPIO_NUM_9;
|
||||||
|
cfg.pin_d6 = GPIO_NUM_46;
|
||||||
|
cfg.pin_d7 = GPIO_NUM_3;
|
||||||
|
cfg.pin_d8 = GPIO_NUM_8;
|
||||||
|
cfg.pin_d9 = GPIO_NUM_16;
|
||||||
|
cfg.pin_d10 = GPIO_NUM_1;
|
||||||
|
cfg.pin_d11 = GPIO_NUM_14;
|
||||||
|
cfg.pin_d12 = GPIO_NUM_21;
|
||||||
|
cfg.pin_d13 = GPIO_NUM_47;
|
||||||
|
cfg.pin_d14 = GPIO_NUM_48;
|
||||||
|
cfg.pin_d15 = GPIO_NUM_45;
|
||||||
|
cfg.pin_henable = GPIO_NUM_41;
|
||||||
|
cfg.pin_vsync = GPIO_NUM_40;
|
||||||
|
cfg.pin_hsync = GPIO_NUM_39;
|
||||||
|
cfg.pin_pclk = GPIO_NUM_0;
|
||||||
|
cfg.freq_write = 12000000;
|
||||||
|
cfg.hsync_polarity = 0;
|
||||||
|
cfg.hsync_front_porch = 40;
|
||||||
|
cfg.hsync_pulse_width = 48;
|
||||||
|
cfg.hsync_back_porch = 40;
|
||||||
|
cfg.vsync_polarity = 0;
|
||||||
|
cfg.vsync_front_porch = 1;
|
||||||
|
cfg.vsync_pulse_width = 31;
|
||||||
|
cfg.vsync_back_porch = 13;
|
||||||
|
cfg.pclk_active_neg = 1;
|
||||||
|
cfg.de_idle_high = 0;
|
||||||
|
cfg.pclk_idle_high = 0;
|
||||||
|
_bus_instance.config(cfg);
|
||||||
|
}
|
||||||
|
_panel_instance.setBus(&_bus_instance);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto cfg = _light_instance.config();
|
||||||
|
cfg.pin_bl = GPIO_NUM_2;
|
||||||
|
_light_instance.config(cfg);
|
||||||
|
}
|
||||||
|
_panel_instance.light(&_light_instance);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto cfg = _touch_instance.config();
|
||||||
|
cfg.x_min = 0;
|
||||||
|
cfg.x_max = 799;
|
||||||
|
cfg.y_min = 0;
|
||||||
|
cfg.y_max = 479;
|
||||||
|
cfg.pin_int = -1;
|
||||||
|
cfg.pin_rst = -1;
|
||||||
|
cfg.bus_shared = false;
|
||||||
|
cfg.offset_rotation = 0;
|
||||||
|
cfg.i2c_port = I2C_NUM_1;
|
||||||
|
cfg.pin_sda = GPIO_NUM_19;
|
||||||
|
cfg.pin_scl = GPIO_NUM_20;
|
||||||
|
cfg.freq = 400000;
|
||||||
|
cfg.i2c_addr = 0x14;
|
||||||
|
_touch_instance.config(cfg);
|
||||||
|
_panel_instance.setTouch(&_touch_instance);
|
||||||
|
}
|
||||||
|
setPanel(&_panel_instance);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static LGFX lcd;
|
static LGFX lcd;
|
||||||
static Preferences preferences;
|
static Preferences preferences;
|
||||||
|
|
||||||
// Price data
|
double btcPrice = 0, goldPrice = 0, silverPrice = 0;
|
||||||
double btcPrice = 0;
|
double mstrPrice = 0, strtPrice = 0;
|
||||||
double goldPrice = 0;
|
double nasdaqPrice = 0, dowPrice = 0, spxPrice = 0;
|
||||||
double silverPrice = 0;
|
|
||||||
double mstrPrice = 0;
|
|
||||||
double strtPrice = 0;
|
|
||||||
double nasdaqPrice = 0;
|
|
||||||
double dowPrice = 0;
|
|
||||||
double spxPrice = 0;
|
|
||||||
|
|
||||||
unsigned long lastRefresh = 0;
|
unsigned long lastRefresh = 0;
|
||||||
const unsigned long REFRESH_INTERVAL = 300000;
|
const unsigned long REFRESH_INTERVAL = 300000;
|
||||||
|
|
||||||
// Format price with US-style commas (e.g., 66,647.29)
|
|
||||||
String formatPriceUS(double price, int decimals = 2, bool includeDollar = true) {
|
String formatPriceUS(double price, int decimals = 2, bool includeDollar = true) {
|
||||||
if (price <= 0) return "...";
|
if (price <= 0) return "...";
|
||||||
|
|
||||||
// Build the number string
|
String result = "";
|
||||||
String result = "";
|
long longPrice = (long)price;
|
||||||
|
String intStr = String(longPrice);
|
||||||
|
int len = intStr.length();
|
||||||
|
|
||||||
// Handle the integer part with commas
|
for (int i = 0; i < len; i++) {
|
||||||
long longPrice = (long)price;
|
if (i > 0 && (len - i) % 3 == 0) {
|
||||||
String intStr = String(longPrice);
|
result += ",";
|
||||||
int len = intStr.length();
|
|
||||||
int commaPos = len % 3;
|
|
||||||
|
|
||||||
for (int i = 0; i < len; i++) {
|
|
||||||
if (i > 0 && (i - (len % 3)) % 3 == 0 && (len % 3 != 0 || i > 0)) {
|
|
||||||
result += ",";
|
|
||||||
} else if (len % 3 == 0 && i > 0 && i % 3 == 0) {
|
|
||||||
result += ",";
|
|
||||||
}
|
|
||||||
result += intStr[i];
|
|
||||||
}
|
}
|
||||||
|
result += intStr[i];
|
||||||
|
}
|
||||||
|
|
||||||
// Add decimals if needed
|
if (decimals > 0) {
|
||||||
if (decimals > 0) {
|
result += ".";
|
||||||
result += ".";
|
double frac = price - longPrice;
|
||||||
double frac = price - longPrice;
|
for (int d = 0; d < decimals; d++) frac *= 10;
|
||||||
for (int d = 0; d < decimals; d++) {
|
long fracLong = (long)(frac + 0.5);
|
||||||
frac *= 10;
|
String fracStr = String(fracLong);
|
||||||
}
|
while (fracStr.length() < decimals) fracStr = "0" + fracStr;
|
||||||
long fracLong = (long)(frac + 0.5); // Round
|
result += fracStr;
|
||||||
String fracStr = String(fracLong);
|
}
|
||||||
while (fracStr.length() < decimals) {
|
|
||||||
fracStr = "0" + fracStr;
|
|
||||||
}
|
|
||||||
result += fracStr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (includeDollar) {
|
return includeDollar ? "$" + result : result;
|
||||||
return "$" + result;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fetchPrice(const char* symbol, double* result) {
|
bool fetchPrice(const char* symbol, double* result) {
|
||||||
WiFiClientSecure client;
|
WiFiClientSecure client;
|
||||||
client.setInsecure();
|
client.setInsecure();
|
||||||
|
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
String url = "https://query1.finance.yahoo.com/v8/finance/chart/" + String(symbol) + "?interval=1d&range=1d";
|
String url = "https://query1.finance.yahoo.com/v8/finance/chart/" + String(symbol) + "?interval=1d&range=1d";
|
||||||
|
|
||||||
Serial.println("Fetching: " + String(symbol));
|
if (!http.begin(client, url)) return false;
|
||||||
Serial.println("URL: " + url);
|
|
||||||
|
|
||||||
if (!http.begin(client, url)) {
|
http.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
|
||||||
Serial.println("HTTP begin failed");
|
http.setTimeout(15000);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
http.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
|
int httpCode = http.GET();
|
||||||
http.setTimeout(15000);
|
if (httpCode != HTTP_CODE_OK) {
|
||||||
|
|
||||||
int httpCode = http.GET();
|
|
||||||
Serial.println("HTTP code: " + String(httpCode));
|
|
||||||
|
|
||||||
if (httpCode != HTTP_CODE_OK) {
|
|
||||||
Serial.println("HTTP error: " + http.errorToString(httpCode));
|
|
||||||
http.end();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
String payload = http.getString();
|
|
||||||
http.end();
|
http.end();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Serial.println("Response length: " + String(payload.length()));
|
String payload = http.getString();
|
||||||
|
http.end();
|
||||||
|
|
||||||
// Parse regularMarketPrice from meta section
|
String searchStr = "\"regularMarketPrice\":";
|
||||||
// Format: "regularMarketPrice":66647.29
|
int idx = payload.indexOf(searchStr);
|
||||||
String searchStr = "\"regularMarketPrice\":";
|
if (idx == -1) return false;
|
||||||
int idx = payload.indexOf(searchStr);
|
|
||||||
|
|
||||||
if (idx == -1) {
|
int start = idx + searchStr.length();
|
||||||
Serial.println("regularMarketPrice not found");
|
int end = payload.indexOf(",", start);
|
||||||
return false;
|
if (end == -1) end = payload.indexOf("}", start);
|
||||||
}
|
if (end <= start) return false;
|
||||||
|
|
||||||
int start = idx + searchStr.length();
|
String priceStr = payload.substring(start, end);
|
||||||
int end = payload.indexOf(",", start);
|
*result = priceStr.toDouble();
|
||||||
if (end == -1) end = payload.indexOf("}", start);
|
return true;
|
||||||
|
|
||||||
if (end <= start) {
|
|
||||||
Serial.println("Could not find end of price");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
String priceStr = payload.substring(start, end);
|
|
||||||
Serial.println("Price string: " + priceStr);
|
|
||||||
|
|
||||||
*result = priceStr.toDouble();
|
|
||||||
Serial.println("Parsed: " + String(*result));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void updatePrices() {
|
void updatePrices() {
|
||||||
Serial.println("\n=== Updating prices ===");
|
if (WiFi.status() != WL_CONNECTED) return;
|
||||||
Serial.println("WiFi status: " + String(WiFi.status()));
|
fetchPrice("BTC-USD", &btcPrice);
|
||||||
Serial.println("WiFi connected: " + String(WiFi.status() == WL_CONNECTED ? "YES" : "NO"));
|
fetchPrice("GC=F", &goldPrice);
|
||||||
|
fetchPrice("SI=F", &silverPrice);
|
||||||
if (WiFi.status() != WL_CONNECTED) {
|
fetchPrice("MSTR", &mstrPrice);
|
||||||
Serial.println("WiFi not connected, skipping update");
|
fetchPrice("STRC", &strtPrice);
|
||||||
return;
|
fetchPrice("^IXIC", &nasdaqPrice);
|
||||||
}
|
fetchPrice("^DJI", &dowPrice);
|
||||||
|
fetchPrice("^GSPC", &spxPrice);
|
||||||
Serial.println("IP: " + WiFi.localIP().toString());
|
|
||||||
|
|
||||||
fetchPrice("BTC-USD", &btcPrice);
|
|
||||||
fetchPrice("GC=F", &goldPrice);
|
|
||||||
fetchPrice("SI=F", &silverPrice);
|
|
||||||
fetchPrice("MSTR", &mstrPrice);
|
|
||||||
fetchPrice("STRC", &strtPrice);
|
|
||||||
fetchPrice("^IXIC", &nasdaqPrice);
|
|
||||||
fetchPrice("^DJI", &dowPrice);
|
|
||||||
fetchPrice("^GSPC", &spxPrice);
|
|
||||||
|
|
||||||
Serial.println("=== Update complete ===\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawDashboard() {
|
void drawDashboard() {
|
||||||
lcd.fillScreen(TFT_BLACK);
|
lcd.fillScreen(TFT_BLACK);
|
||||||
|
|
||||||
// Title bar
|
int col1X = 20;
|
||||||
lcd.fillRect(0, 0, SCREEN_WIDTH, 45, TFT_ORANGE);
|
int col2X = 280;
|
||||||
lcd.setTextColor(TFT_BLACK, TFT_ORANGE);
|
int col3X = 540;
|
||||||
lcd.setTextSize(3);
|
int startY = 20;
|
||||||
lcd.setTextDatum(textdatum_t::middle_center);
|
|
||||||
lcd.drawString("BITCOIN DASHBOARD", SCREEN_WIDTH / 2, 22);
|
|
||||||
|
|
||||||
// Column positions
|
lcd.setFont(&fonts::DejaVu18);
|
||||||
int col1X = 20;
|
lcd.setTextDatum(textdatum_t::top_left);
|
||||||
int col2X = 280;
|
|
||||||
int col3X = 540;
|
|
||||||
int startY = 60;
|
|
||||||
|
|
||||||
// Column separators
|
lcd.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||||
lcd.drawLine(265, startY, 265, SCREEN_HEIGHT - 35, TFT_GRAY);
|
lcd.drawString("HARD MONEY", col1X, startY);
|
||||||
lcd.drawLine(525, startY, 525, SCREEN_HEIGHT - 35, TFT_GRAY);
|
|
||||||
|
|
||||||
// Column 1: HARD MONEY
|
lcd.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||||
lcd.setTextColor(TFT_ORANGE, TFT_BLACK);
|
lcd.drawString("STOCKS", col2X, startY);
|
||||||
lcd.setTextSize(2);
|
|
||||||
lcd.setTextDatum(textdatum_t::top_left);
|
|
||||||
lcd.drawString("HARD MONEY", col1X, startY);
|
|
||||||
|
|
||||||
lcd.setTextColor(TFT_WHITE, TFT_BLACK);
|
lcd.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||||
lcd.drawString("Bitcoin:", col1X, startY + 35);
|
lcd.drawString("INDICES", col3X, startY);
|
||||||
lcd.drawString(formatPriceUS(btcPrice, 0, true), col1X, startY + 60);
|
|
||||||
|
|
||||||
lcd.drawString("Gold:", col1X, startY + 95);
|
int labelY = startY + 40;
|
||||||
lcd.drawString(formatPriceUS(goldPrice, 2, true), col1X, startY + 120);
|
int priceY = labelY + 28;
|
||||||
|
|
||||||
lcd.drawString("Silver:", col1X, startY + 155);
|
lcd.setTextColor(TFT_ORANGE, TFT_BLACK);
|
||||||
lcd.drawString(formatPriceUS(silverPrice, 2, true), col1X, startY + 180);
|
lcd.setFont(&fonts::DejaVu18);
|
||||||
|
lcd.drawString("Bitcoin:", col1X, labelY);
|
||||||
|
lcd.drawString(formatPriceUS(btcPrice, 0), col1X, priceY);
|
||||||
|
|
||||||
// Column 2: STOCKS
|
lcd.setTextColor(TFT_GOLD, TFT_BLACK);
|
||||||
lcd.setTextColor(TFT_ORANGE, TFT_BLACK);
|
lcd.drawString("Gold:", col1X, labelY + 60);
|
||||||
lcd.drawString("STOCKS", col2X, startY);
|
lcd.drawString(formatPriceUS(goldPrice, 2), col1X, priceY + 60);
|
||||||
|
|
||||||
lcd.setTextColor(TFT_WHITE, TFT_BLACK);
|
lcd.setTextColor(TFT_SILVER, TFT_BLACK);
|
||||||
lcd.drawString("MSTR:", col2X, startY + 35);
|
lcd.drawString("Silver:", col1X, labelY + 120);
|
||||||
lcd.drawString(formatPriceUS(mstrPrice, 2, true), col2X, startY + 60);
|
lcd.drawString(formatPriceUS(silverPrice, 2), col1X, priceY + 120);
|
||||||
|
|
||||||
lcd.drawString("STRC:", col2X, startY + 95);
|
lcd.setTextColor(TFT_GREEN, TFT_BLACK);
|
||||||
lcd.drawString(formatPriceUS(strtPrice, 2, true), col2X, startY + 120);
|
lcd.drawString("MSTR:", col2X, labelY);
|
||||||
|
lcd.drawString(formatPriceUS(mstrPrice, 2), col2X, priceY);
|
||||||
|
|
||||||
// Column 3: INDICES
|
lcd.drawString("STRC:", col2X, labelY + 60);
|
||||||
lcd.setTextColor(TFT_ORANGE, TFT_BLACK);
|
lcd.drawString(formatPriceUS(strtPrice, 2), col2X, priceY + 60);
|
||||||
lcd.drawString("INDICES", col3X, startY);
|
|
||||||
|
|
||||||
lcd.setTextColor(TFT_WHITE, TFT_BLACK);
|
lcd.drawString("NASDAQ:", col3X, labelY);
|
||||||
lcd.drawString("NASDAQ:", col3X, startY + 35);
|
lcd.drawString(formatPriceUS(nasdaqPrice, 2), col3X, priceY);
|
||||||
lcd.drawString(formatPriceUS(nasdaqPrice, 2, true), col3X, startY + 60);
|
|
||||||
|
|
||||||
lcd.drawString("DOW:", col3X, startY + 95);
|
lcd.drawString("DOW:", col3X, labelY + 60);
|
||||||
lcd.drawString(formatPriceUS(dowPrice, 2, true), col3X, startY + 120);
|
lcd.drawString(formatPriceUS(dowPrice, 2), col3X, priceY + 60);
|
||||||
|
|
||||||
lcd.drawString("SPX:", col3X, startY + 155);
|
lcd.drawString("SPX:", col3X, labelY + 120);
|
||||||
lcd.drawString(formatPriceUS(spxPrice, 2, true), col3X, startY + 180);
|
lcd.drawString(formatPriceUS(spxPrice, 2), col3X, priceY + 120);
|
||||||
|
|
||||||
// Status bar
|
lcd.fillSmoothRoundRect(0, SCREEN_HEIGHT - 35, SCREEN_WIDTH, 35, 6, TFT_ORANGE);
|
||||||
lcd.fillRect(0, SCREEN_HEIGHT - 30, SCREEN_WIDTH, 30, TFT_GRAY);
|
lcd.setTextColor(TFT_WHITE, TFT_ORANGE);
|
||||||
lcd.setTextColor(TFT_BLACK, TFT_GRAY);
|
lcd.setFont(&fonts::DejaVu12);
|
||||||
lcd.setTextSize(1);
|
lcd.setTextDatum(textdatum_t::middle_left);
|
||||||
lcd.setTextDatum(textdatum_t::middle_left);
|
String wifiStatus = WiFi.status() == WL_CONNECTED ? "Connected" : "DISCONNECTED";
|
||||||
|
lcd.drawString("WiFi: " + wifiStatus, 15, SCREEN_HEIGHT - 17);
|
||||||
String wifiStatus = WiFi.status() == WL_CONNECTED ? "Connected" : "DISCONNECTED";
|
lcd.setTextDatum(textdatum_t::middle_center);
|
||||||
lcd.drawString("WiFi: " + wifiStatus, 10, SCREEN_HEIGHT - 15);
|
lcd.drawString("TOUCH TO REFRESH", SCREEN_WIDTH / 2, SCREEN_HEIGHT - 17);
|
||||||
lcd.setTextDatum(textdatum_t::middle_center);
|
|
||||||
lcd.drawString("TOUCH TO REFRESH", SCREEN_WIDTH / 2, SCREEN_HEIGHT - 15);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
delay(3000);
|
delay(3000);
|
||||||
|
|
||||||
Serial.println("\n\n=== STARTING ===");
|
lcd.init();
|
||||||
|
lcd.setBrightness(255);
|
||||||
|
lcd.setRotation(0);
|
||||||
|
|
||||||
lcd.init();
|
lcd.fillScreen(TFT_WHITE);
|
||||||
lcd.setBrightness(255);
|
delay(250);
|
||||||
lcd.setRotation(0);
|
lcd.fillScreen(TFT_ORANGE);
|
||||||
|
delay(250);
|
||||||
|
lcd.fillScreen(TFT_BLACK);
|
||||||
|
delay(250);
|
||||||
|
|
||||||
lcd.fillScreen(TFT_WHITE);
|
lcd.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||||
delay(250);
|
lcd.setFont(&fonts::DejaVu24);
|
||||||
lcd.fillScreen(TFT_RED);
|
lcd.setTextDatum(textdatum_t::middle_center);
|
||||||
delay(250);
|
lcd.drawString("Bitcoin Dashboard", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 - 40);
|
||||||
lcd.fillScreen(TFT_GREEN);
|
lcd.setFont(&fonts::DejaVu18);
|
||||||
delay(250);
|
lcd.drawString("Starting...", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
|
||||||
lcd.fillScreen(TFT_BLUE);
|
|
||||||
delay(250);
|
|
||||||
lcd.fillScreen(TFT_BLACK);
|
|
||||||
|
|
||||||
lcd.setTextSize(2);
|
preferences.begin("wifi", true);
|
||||||
lcd.setTextColor(TFT_WHITE, TFT_BLACK);
|
String ssid = preferences.getString("ssid", "");
|
||||||
lcd.setTextDatum(textdatum_t::middle_center);
|
String pass = preferences.getString("password", "");
|
||||||
lcd.drawString("Bitcoin Dashboard", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 - 40);
|
preferences.end();
|
||||||
lcd.drawString("Starting...", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
|
|
||||||
|
|
||||||
// Load WiFi credentials
|
if (ssid.length() > 0) {
|
||||||
preferences.begin("wifi", true);
|
lcd.setFont(&fonts::DejaVu18);
|
||||||
String ssid = preferences.getString("ssid", "");
|
lcd.drawString("WiFi: " + ssid, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 + 40);
|
||||||
String pass = preferences.getString("password", "");
|
|
||||||
preferences.end();
|
|
||||||
|
|
||||||
if (ssid.length() > 0) {
|
WiFi.mode(WIFI_STA);
|
||||||
Serial.println("Connecting to WiFi: " + ssid);
|
WiFi.begin(ssid.c_str(), pass.c_str());
|
||||||
lcd.drawString("WiFi: " + ssid, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 + 40);
|
|
||||||
|
|
||||||
WiFi.mode(WIFI_STA);
|
int attempts = 0;
|
||||||
WiFi.begin(ssid.c_str(), pass.c_str());
|
while (WiFi.status() != WL_CONNECTED && attempts < 60) {
|
||||||
|
delay(500);
|
||||||
int attempts = 0;
|
Serial.print(".");
|
||||||
while (WiFi.status() != WL_CONNECTED && attempts < 60) {
|
attempts++;
|
||||||
delay(500);
|
|
||||||
Serial.print(".");
|
|
||||||
attempts++;
|
|
||||||
}
|
|
||||||
Serial.println();
|
|
||||||
|
|
||||||
if (WiFi.status() == WL_CONNECTED) {
|
|
||||||
Serial.println("WiFi connected!");
|
|
||||||
Serial.println("IP: " + WiFi.localIP().toString());
|
|
||||||
lcd.drawString("Connected! IP:", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 + 80);
|
|
||||||
lcd.drawString(WiFi.localIP().toString(), SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 + 110);
|
|
||||||
|
|
||||||
delay(1000);
|
|
||||||
updatePrices();
|
|
||||||
lastRefresh = millis();
|
|
||||||
drawDashboard();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.println("WiFi connection failed");
|
|
||||||
lcd.drawString("WiFi failed", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 + 80);
|
|
||||||
delay(2000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show WiFi setup screen
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
lcd.drawString("No WiFi configured", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 + 40);
|
lcd.drawString("Connected! IP:", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 + 80);
|
||||||
|
lcd.setFont(&fonts::DejaVu12);
|
||||||
|
lcd.drawString(WiFi.localIP().toString(), SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 + 110);
|
||||||
|
|
||||||
|
delay(1000);
|
||||||
|
updatePrices();
|
||||||
|
lastRefresh = millis();
|
||||||
|
drawDashboard();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lcd.drawString("WiFi failed", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 + 80);
|
||||||
delay(2000);
|
delay(2000);
|
||||||
drawDashboard();
|
}
|
||||||
|
|
||||||
|
drawDashboard();
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
int32_t x, y;
|
int32_t x, y;
|
||||||
|
|
||||||
if (lcd.getTouch(&x, &y)) {
|
if (lcd.getTouch(&x, &y)) {
|
||||||
Serial.println("Touch detected - refreshing...");
|
lcd.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||||
lcd.setTextColor(TFT_WHITE, TFT_BLACK);
|
lcd.setFont(&fonts::DejaVu24);
|
||||||
lcd.setTextSize(2);
|
lcd.setTextDatum(textdatum_t::middle_center);
|
||||||
lcd.setTextDatum(textdatum_t::middle_center);
|
lcd.fillRect(0, SCREEN_HEIGHT / 2 - 20, SCREEN_WIDTH, 40, TFT_BLACK);
|
||||||
lcd.fillRect(0, SCREEN_HEIGHT / 2 - 20, SCREEN_WIDTH, 40, TFT_BLACK);
|
lcd.drawString("Refreshing...", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
|
||||||
lcd.drawString("Refreshing...", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
|
|
||||||
|
|
||||||
updatePrices();
|
updatePrices();
|
||||||
lastRefresh = millis();
|
lastRefresh = millis();
|
||||||
drawDashboard();
|
drawDashboard();
|
||||||
|
|
||||||
delay(500);
|
delay(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-refresh every 5 minutes
|
if (millis() - lastRefresh > REFRESH_INTERVAL) {
|
||||||
if (millis() - lastRefresh > REFRESH_INTERVAL) {
|
updatePrices();
|
||||||
updatePrices();
|
lastRefresh = millis();
|
||||||
lastRefresh = millis();
|
drawDashboard();
|
||||||
drawDashboard();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
delay(100);
|
delay(100);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue