Make zap notify more lightning like, verify SSL certificates, remove price fetch code
This commit is contained in:
parent
5425ea7fbf
commit
1f2110fc5a
21 changed files with 494 additions and 190 deletions
|
@ -12,11 +12,16 @@ Biggest differences with v2 are:
|
||||||
- Added market capitalization screen
|
- Added market capitalization screen
|
||||||
- LED flash on new block (and focus to block height screen on new block)
|
- LED flash on new block (and focus to block height screen on new block)
|
||||||
|
|
||||||
|
New features:
|
||||||
|
- BitAxe integration
|
||||||
|
- Zap notifier
|
||||||
|
-
|
||||||
|
|
||||||
"Steal focus on new block" means that when a new block is mined, the display will switch to the block height screen if it's not on it already.
|
"Steal focus on new block" means that when a new block is mined, the display will switch to the block height screen if it's not on it already.
|
||||||
|
|
||||||
Most [information](https://github.com/btclock/btclock_v2/wiki) about BTClock v2 is still valid for this version.
|
Most [information](https://github.com/btclock/btclock_v2/wiki) about BTClock v2 is still valid for this version.
|
||||||
|
|
||||||
**NOTE**: The software assumes that the hardware is run in a controlled private network. The Web UI and the OTA update mechanism are not password protected and accessible to anyone in the network. Also, since the device only fetches numbers through WebSockets it will skip server certificate verification to save resources.
|
**NOTE**: The software assumes that the hardware is run in a controlled private network. ~~The Web UI and the OTA update mechanism are not password protected and accessible to anyone in the network. Also, since the device only fetches numbers through WebSockets it will skip server certificate verification to save resources.~~ Since 3.2.0 the WebUI is password protectable and all certificates are verified. OTA update mechanism is not password-protected.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
|
2
data
2
data
|
@ -1 +1 @@
|
||||||
Subproject commit 1fa62ca88dc9cf85109712082e2b0d3916d03323
|
Subproject commit 1c2d8dcdd0efd846f39b0f24977c982a96f16392
|
|
@ -7,8 +7,8 @@ CONFIG_MBEDTLS_KEY_EXCHANGE_PSK=y
|
||||||
#CONFIG_FREERTOS_USE_TRACE_FACILITY=y
|
#CONFIG_FREERTOS_USE_TRACE_FACILITY=y
|
||||||
#CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
|
#CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
|
||||||
#CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=n
|
#CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=n
|
||||||
CONFIG_ESP_TLS_INSECURE=y
|
#CONFIG_ESP_TLS_INSECURE=y
|
||||||
CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y
|
#CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y
|
||||||
|
|
||||||
CONFIG_HEAP_CORRUPTION_DETECTION=CONFIG_HEAP_POISONING_LIGHT
|
CONFIG_HEAP_CORRUPTION_DETECTION=CONFIG_HEAP_POISONING_LIGHT
|
||||||
CONFIG_HEAP_POISONING_LIGHT=y
|
CONFIG_HEAP_POISONING_LIGHT=y
|
||||||
|
@ -51,3 +51,5 @@ CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n
|
||||||
CONFIG_SPIRAM_CACHE_WORKAROUND=y
|
CONFIG_SPIRAM_CACHE_WORKAROUND=y
|
||||||
CONFIG_COMPILER_OPTIMIZATION_PERF=y
|
CONFIG_COMPILER_OPTIMIZATION_PERF=y
|
||||||
CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
|
CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
|
||||||
|
CONFIG_MBEDTLS_SSL_SERVER_VERIFY=n
|
||||||
|
CONFIG_MBEDTLS_SSL_VERIFY_CLIENT_CERTIFICATE=n
|
|
@ -7,49 +7,42 @@ uint blockMedianFee = 1;
|
||||||
bool blockNotifyInit = false;
|
bool blockNotifyInit = false;
|
||||||
unsigned long int lastBlockUpdate;
|
unsigned long int lastBlockUpdate;
|
||||||
|
|
||||||
// const char *mempoolWsCert = R"(-----BEGIN CERTIFICATE-----
|
const char *mempoolWsCert = R"EOF(
|
||||||
// MIIHfTCCBmWgAwIBAgIRANFX3mhqRYDt1NFuENoSyaAwDQYJKoZIhvcNAQELBQAw
|
-----BEGIN CERTIFICATE-----
|
||||||
// gZUxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
|
MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
|
||||||
// BgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDE9MDsGA1UE
|
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
|
||||||
// AxM0U2VjdGlnbyBSU0EgT3JnYW5pemF0aW9uIFZhbGlkYXRpb24gU2VjdXJlIFNl
|
cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
|
||||||
// cnZlciBDQTAeFw0yMzA3MjQwMDAwMDBaFw0yNDA4MjIyMzU5NTlaMFcxCzAJBgNV
|
BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw
|
||||||
// BAYTAkpQMQ4wDAYDVQQIEwVUb2t5bzEgMB4GA1UEChMXTUVNUE9PTCBTUEFDRSBD
|
MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV
|
||||||
// Ty4sIExURC4xFjAUBgNVBAMTDW1lbXBvb2wuc3BhY2UwggEiMA0GCSqGSIb3DQEB
|
BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
|
||||||
// AQUAA4IBDwAwggEKAoIBAQCqmiPRWgo58d25R0biQjAksXMq5ciH7z7ZQo2w2AbB
|
aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy
|
||||||
// rHxpnlIry74b9S4wRY5UJeYmd6ZwA76NdSioDvxTJc29bLplY+Ftmfc4ET0zYb2k
|
dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
|
||||||
// Fi86z7GOWb6Ezor/qez9uMM9cxd021Bvcs0/2OrL6Sgp66u9keDZv9NyvFPpXfuR
|
AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B
|
||||||
// tdV2r4HF57VJqZn105PN4k80kNWgDbae8aw+BuUNvQYKEe71yfB7Bh6zSh9pCSfM
|
3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY
|
||||||
// I6pIJdQzoada2uY1dQMoJeIq8qKNKqAPKGsH5McemUT5ZIKU/tjk3nfX0pz/sQa4
|
tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/
|
||||||
// CN7tLH6UeUlctei92GFd6Xtn7RbKLhDUbc4Sq02Cc9iXAgMBAAGjggQDMIID/zAf
|
Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2
|
||||||
// BgNVHSMEGDAWgBQX2dYlJ2f5McJJQ9kwNkSMbKlP6zAdBgNVHQ4EFgQUXkxoddJ6
|
VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT
|
||||||
// rKobsbmDdtuCK1ywXuIwDgYDVR0PAQH/BAQDAgWgMAwGA1UdEwEB/wQCMAAwHQYD
|
79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6
|
||||||
// VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMEoGA1UdIARDMEEwNQYMKwYBBAGy
|
c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT
|
||||||
// MQECAQMEMCUwIwYIKwYBBQUHAgEWF2h0dHBzOi8vc2VjdGlnby5jb20vQ1BTMAgG
|
Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l
|
||||||
// BmeBDAECAjBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLnNlY3RpZ28uY29t
|
c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee
|
||||||
// L1NlY3RpZ29SU0FPcmdhbml6YXRpb25WYWxpZGF0aW9uU2VjdXJlU2VydmVyQ0Eu
|
UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE
|
||||||
// Y3JsMIGKBggrBgEFBQcBAQR+MHwwVQYIKwYBBQUHMAKGSWh0dHA6Ly9jcnQuc2Vj
|
Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
|
||||||
// dGlnby5jb20vU2VjdGlnb1JTQU9yZ2FuaXphdGlvblZhbGlkYXRpb25TZWN1cmVT
|
BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G
|
||||||
// ZXJ2ZXJDQS5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNlY3RpZ28uY29t
|
A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF
|
||||||
// MIIBgAYKKwYBBAHWeQIEAgSCAXAEggFsAWoAdwB2/4g/Crb7lVHCYcz1h7o0tKTN
|
Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO
|
||||||
// uyncaEIKn+ZnTFo6dAAAAYmc9m/gAAAEAwBIMEYCIQD8XOozx411S/bnZambGjTB
|
VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3
|
||||||
// yTcr2fCmggUfQLSmqksD5gIhAIjiEMg0o1VSuQW31gWzfzL6idCkIZeSKN104cdp
|
ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs
|
||||||
// xa4SAHcA2ra/az+1tiKfm8K7XGvocJFxbLtRhIU0vaQ9MEjX+6sAAAGJnPZwPwAA
|
8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR
|
||||||
// BAMASDBGAiEA2sPTZTzvxewzQ8vk36+BWAKuJS7AvJ5W3clvfwCa8OUCIQC74ekT
|
iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze
|
||||||
// Ged2fqQE4sVy74aS6HRA2ihC9VLtNrASJx1YjQB2AO7N0GTV2xrOxVy3nbTNE6Iy
|
Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ
|
||||||
// h0Z8vOzew1FIWUZxH7WbAAABiZz2cA8AAAQDAEcwRQIgEklH7wYCFuuJIFUHX5PY
|
XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/
|
||||||
// /vZ3bDoxOp+061PT3caa+rICIQC0abgfGlBKiHxp47JZxnW3wcVqWdiYX4ViLm9H
|
qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB
|
||||||
// xfx4ljCBxgYDVR0RBIG+MIG7gg1tZW1wb29sLnNwYWNlghMqLmZtdC5tZW1wb29s
|
VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB
|
||||||
// LnNwYWNlghMqLmZyYS5tZW1wb29sLnNwYWNlgg8qLm1lbXBvb2wuc3BhY2WCEyou
|
L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG
|
||||||
// dGs3Lm1lbXBvb2wuc3BhY2WCEyoudmExLm1lbXBvb2wuc3BhY2WCDGJpc3EubWFy
|
jjxDah2nGN59PRbxYvnKkKj9
|
||||||
// a2V0c4IKYmlzcS5uaW5qYYIObGlxdWlkLm5ldHdvcmuCDGxpcXVpZC5wbGFjZYIN
|
-----END CERTIFICATE-----
|
||||||
// bWVtcG9vbC5uaW5qYTANBgkqhkiG9w0BAQsFAAOCAQEAFvOSRnlHDfq9C8acjZEG
|
)EOF";
|
||||||
// 5XIqjNYigyWyjOvx83of6Z3PBKkAZB5D/UHBPp+jBDJiEb/QXC7Z7Y7kpuvnoVib
|
|
||||||
// b4jDc0RjGEsxL+3F7cSw26m3wILJhhHooGZRmFY4GOAeCZtYCOTzJsiZvFpDoQjU
|
|
||||||
// hTBxtaps05z0Ly9/eYvkXnjnBNROZJVR+KYHlq4TIoGNc4q4KvpfHv2I/vhS2M1e
|
|
||||||
// bECNNPEyRxHGKdXXO3huocE7aVKpy+JDR6cWwDu6hpdc1j/SCDqdTDFQ7McHOrqA
|
|
||||||
// fpPh4FcfePMh7Mqxtg2pSs5pXPtiP0ZjLgxd7HbAXct8Y+/jGk+k3sx3SeYXVimr
|
|
||||||
// ew==
|
|
||||||
// -----END CERTIFICATE-----)";
|
|
||||||
|
|
||||||
void setupBlockNotify()
|
void setupBlockNotify()
|
||||||
{
|
{
|
||||||
|
@ -103,13 +96,18 @@ void setupBlockNotify()
|
||||||
|
|
||||||
esp_websocket_client_config_t config = {
|
esp_websocket_client_config_t config = {
|
||||||
// .uri = "wss://mempool.space/api/v1/ws",
|
// .uri = "wss://mempool.space/api/v1/ws",
|
||||||
// .task_stack = (6*1024),
|
.task_stack = (6*1024),
|
||||||
// .cert_pem = mempoolWsCert,
|
.user_agent = USER_AGENT
|
||||||
.user_agent = USER_AGENT,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE)) {
|
||||||
|
config.cert_pem = mempoolWsCert;
|
||||||
|
}
|
||||||
|
|
||||||
config.uri = mempoolUri.c_str();
|
config.uri = mempoolUri.c_str();
|
||||||
|
|
||||||
|
Serial.printf("Connecting to %s\r\n", preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE));
|
||||||
|
|
||||||
blockNotifyClient = esp_websocket_client_init(&config);
|
blockNotifyClient = esp_websocket_client_init(&config);
|
||||||
esp_websocket_register_events(blockNotifyClient, WEBSOCKET_EVENT_ANY,
|
esp_websocket_register_events(blockNotifyClient, WEBSOCKET_EVENT_ANY,
|
||||||
onWebsocketBlockEvent, blockNotifyClient);
|
onWebsocketBlockEvent, blockNotifyClient);
|
||||||
|
@ -302,7 +300,10 @@ int getBlockFetch()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
WiFiClientSecure client;
|
WiFiClientSecure client;
|
||||||
client.setInsecure();
|
|
||||||
|
if (preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE)) {
|
||||||
|
client.setCACert(mempoolWsCert);
|
||||||
|
}
|
||||||
|
|
||||||
String mempoolInstance =
|
String mempoolInstance =
|
||||||
preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
|
preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
|
||||||
|
|
|
@ -754,3 +754,15 @@ bool isActiveCurrency(std::string ¤cy)
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* getFirmwareFilename() {
|
||||||
|
if (HW_REV == "REV_B_EPD_2_13") {
|
||||||
|
return "btclock_rev_b_213epd_firmware.bin";
|
||||||
|
} else if (HW_REV == "REV_A_EPD_2_13") {
|
||||||
|
return "lolin_s3_mini_213epd_firmware.bin";
|
||||||
|
} else if (HW_REV == "REV_A_EPD_2_9") {
|
||||||
|
return "lolin_s3_mini_29epd_firmware.bin";
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
|
@ -83,3 +83,4 @@ void addScreenMapping(int value, const char* name);
|
||||||
|
|
||||||
int findScreenIndexByValue(int value);
|
int findScreenIndexByValue(int value);
|
||||||
String replaceAmbiguousChars(String input);
|
String replaceAmbiguousChars(String input);
|
||||||
|
const char* getFirmwareFilename();
|
|
@ -298,7 +298,12 @@ void ledTask(void *parameter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
blinkDelayColor(250, 3, 142, 48, 235);
|
for (int flash = 0; flash < random(7, 10); flash++)
|
||||||
|
{
|
||||||
|
lightningStrike();
|
||||||
|
delay(random(50, 150));
|
||||||
|
}
|
||||||
|
// blinkDelayColor(250, 3, 142, 48, 235);
|
||||||
// blinkDelayTwoColor(250, 3, pixels.Color(142, 48, 235),
|
// blinkDelayTwoColor(250, 3, pixels.Color(142, 48, 235),
|
||||||
// pixels.Color(169, 21, 255));
|
// pixels.Color(169, 21, 255));
|
||||||
#ifdef HAS_FRONTLIGHT
|
#ifdef HAS_FRONTLIGHT
|
||||||
|
@ -668,4 +673,29 @@ void ledTheaterChaseRainbow(int wait)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void lightningStrike()
|
||||||
|
{
|
||||||
|
uint32_t PURPLE = pixels.Color(128, 0, 128);
|
||||||
|
uint32_t YELLOW = pixels.Color(255, 226, 41);
|
||||||
|
|
||||||
|
// Randomly choose which LEDs to light up
|
||||||
|
for (int i = 0; i < pixels.numPixels(); i++)
|
||||||
|
{
|
||||||
|
if (random(2) == 0)
|
||||||
|
{ // 50% chance for each LED
|
||||||
|
pixels.setPixelColor(i, YELLOW);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pixels.setPixelColor(i, PURPLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pixels.show();
|
||||||
|
|
||||||
|
delay(random(10, 50)); // Flash duration
|
||||||
|
|
||||||
|
// Return to purple background
|
||||||
|
// setAllPixels(PURPLE);
|
||||||
|
}
|
||||||
|
|
||||||
Adafruit_NeoPixel getPixels() { return pixels; }
|
Adafruit_NeoPixel getPixels() { return pixels; }
|
||||||
|
|
|
@ -61,6 +61,7 @@ void ledRainbow(int wait);
|
||||||
void ledTheaterChaseRainbow(int wait);
|
void ledTheaterChaseRainbow(int wait);
|
||||||
void ledTheaterChase(uint32_t color, int wait);
|
void ledTheaterChase(uint32_t color, int wait);
|
||||||
Adafruit_NeoPixel getPixels();
|
Adafruit_NeoPixel getPixels();
|
||||||
|
void lightningStrike();
|
||||||
|
|
||||||
#ifdef HAS_FRONTLIGHT
|
#ifdef HAS_FRONTLIGHT
|
||||||
void frontlightFlash(int flDelayTime);
|
void frontlightFlash(int flDelayTime);
|
||||||
|
|
|
@ -70,62 +70,62 @@ void handleOTATask(void *parameter) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void downloadUpdate() {
|
// void downloadUpdate() {
|
||||||
WiFiClientSecure client;
|
// WiFiClientSecure client;
|
||||||
client.setInsecure();
|
// client.setInsecure();
|
||||||
HTTPClient http;
|
// HTTPClient http;
|
||||||
http.setUserAgent(USER_AGENT);
|
// http.setUserAgent(USER_AGENT);
|
||||||
|
|
||||||
// Send HTTP request to CoinGecko API
|
// // Send HTTP request to CoinGecko API
|
||||||
http.useHTTP10(true);
|
// http.useHTTP10(true);
|
||||||
|
|
||||||
http.begin(client,
|
// http.begin(client,
|
||||||
"https://api.github.com/repos/btclock/btclock_v3/releases/latest");
|
// "https://api.github.com/repos/btclock/btclock_v3/releases/latest");
|
||||||
int httpCode = http.GET();
|
// int httpCode = http.GET();
|
||||||
|
|
||||||
if (httpCode == 200) {
|
// if (httpCode == 200) {
|
||||||
// WiFiClient * stream = http->getStreamPtr();
|
// // WiFiClient * stream = http->getStreamPtr();
|
||||||
|
|
||||||
JsonDocument filter;
|
// JsonDocument filter;
|
||||||
|
|
||||||
JsonObject filter_assets_0 = filter["assets"].add<JsonObject>();
|
// JsonObject filter_assets_0 = filter["assets"].add<JsonObject>();
|
||||||
filter_assets_0["name"] = true;
|
// filter_assets_0["name"] = true;
|
||||||
filter_assets_0["browser_download_url"] = true;
|
// filter_assets_0["browser_download_url"] = true;
|
||||||
|
|
||||||
JsonDocument doc;
|
// JsonDocument doc;
|
||||||
|
|
||||||
DeserializationError error = deserializeJson(
|
// DeserializationError error = deserializeJson(
|
||||||
doc, http.getStream(), DeserializationOption::Filter(filter));
|
// doc, http.getStream(), DeserializationOption::Filter(filter));
|
||||||
|
|
||||||
if (error) {
|
// if (error) {
|
||||||
Serial.print("deserializeJson() failed: ");
|
// Serial.print("deserializeJson() failed: ");
|
||||||
Serial.println(error.c_str());
|
// Serial.println(error.c_str());
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
String downloadUrl;
|
// String downloadUrl;
|
||||||
for (JsonObject asset : doc["assets"].as<JsonArray>()) {
|
// for (JsonObject asset : doc["assets"].as<JsonArray>()) {
|
||||||
if (asset["name"].as<String>().compareTo("firmware.bin") == 0) {
|
// if (asset["name"].as<String>().compareTo("firmware.bin") == 0) {
|
||||||
downloadUrl = asset["browser_download_url"].as<String>();
|
// downloadUrl = asset["browser_download_url"].as<String>();
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
Serial.printf("Download update from %s", downloadUrl);
|
// Serial.printf("Download update from %s", downloadUrl);
|
||||||
|
|
||||||
// esp_http_client_config_t config = {
|
// // esp_http_client_config_t config = {
|
||||||
// .url = CONFIG_FIRMWARE_UPGRADE_URL,
|
// // .url = CONFIG_FIRMWARE_UPGRADE_URL,
|
||||||
// };
|
// // };
|
||||||
// esp_https_ota_config_t ota_config = {
|
// // esp_https_ota_config_t ota_config = {
|
||||||
// .http_config = &config,
|
// // .http_config = &config,
|
||||||
// };
|
// // };
|
||||||
// esp_err_t ret = esp_https_ota(&ota_config);
|
// // esp_err_t ret = esp_https_ota(&ota_config);
|
||||||
// if (ret == ESP_OK)
|
// // if (ret == ESP_OK)
|
||||||
// {
|
// // {
|
||||||
// esp_restart();
|
// // esp_restart();
|
||||||
// }
|
// // }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
void onOTAError(ota_error_t error) {
|
void onOTAError(ota_error_t error) {
|
||||||
Serial.println(F("\nOTA update error, restarting"));
|
Serial.println(F("\nOTA update error, restarting"));
|
||||||
|
|
|
@ -8,7 +8,7 @@ void setupOTA();
|
||||||
void onOTAStart();
|
void onOTAStart();
|
||||||
void handleOTATask(void *parameter);
|
void handleOTATask(void *parameter);
|
||||||
void onOTAProgress(unsigned int progress, unsigned int total);
|
void onOTAProgress(unsigned int progress, unsigned int total);
|
||||||
void downloadUpdate();
|
// void downloadUpdate();
|
||||||
void onOTAError(ota_error_t error);
|
void onOTAError(ota_error_t error);
|
||||||
void onOTAComplete();
|
void onOTAComplete();
|
||||||
|
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
#include "price_fetch.hpp"
|
|
||||||
|
|
||||||
const PROGMEM char *cgApiUrl =
|
|
||||||
"https://api.coingecko.com/api/v3/simple/"
|
|
||||||
"price?ids=bitcoin&vs_currencies=usd%2Ceur";
|
|
||||||
|
|
||||||
TaskHandle_t priceFetchTaskHandle;
|
|
||||||
|
|
||||||
void taskPriceFetch(void *pvParameters) {
|
|
||||||
WiFiClientSecure *client = new WiFiClientSecure;
|
|
||||||
client->setInsecure();
|
|
||||||
for (;;) {
|
|
||||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
|
||||||
|
|
||||||
HTTPClient *http = new HTTPClient();
|
|
||||||
http->setUserAgent(USER_AGENT);
|
|
||||||
|
|
||||||
// Send HTTP request to CoinGecko API
|
|
||||||
http->begin(*client, cgApiUrl);
|
|
||||||
|
|
||||||
int httpCode = http->GET();
|
|
||||||
|
|
||||||
// Parse JSON response and extract average price
|
|
||||||
uint usdPrice, eurPrice;
|
|
||||||
if (httpCode == 200) {
|
|
||||||
String payload = http->getString();
|
|
||||||
JsonDocument doc;
|
|
||||||
deserializeJson(doc, payload);
|
|
||||||
// usdPrice = doc["bitcoin"]["usd"];
|
|
||||||
eurPrice = doc["bitcoin"]["eur"].as<uint>();
|
|
||||||
|
|
||||||
setPrice(eurPrice, CURRENCY_EUR);
|
|
||||||
if (workQueue != nullptr && (getCurrentScreen() == SCREEN_BTC_TICKER ||
|
|
||||||
getCurrentScreen() == SCREEN_SATS_PER_CURRENCY ||
|
|
||||||
getCurrentScreen() == SCREEN_MARKET_CAP)) {
|
|
||||||
WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0};
|
|
||||||
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
|
|
||||||
}
|
|
||||||
|
|
||||||
preferences.putUInt("lastPrice", eurPrice);
|
|
||||||
} else {
|
|
||||||
Serial.print(
|
|
||||||
F("Error retrieving BTC/USD price (CoinGecko). HTTP status code: "));
|
|
||||||
Serial.println(httpCode);
|
|
||||||
if (httpCode == -1) {
|
|
||||||
WiFi.reconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setupPriceFetchTask() {
|
|
||||||
xTaskCreate(taskPriceFetch, "priceFetch", (6 * 1024), NULL, tskIDLE_PRIORITY,
|
|
||||||
&priceFetchTaskHandle);
|
|
||||||
|
|
||||||
xTaskNotifyGive(priceFetchTaskHandle);
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
#include <Arduino.h>
|
|
||||||
#include <HTTPClient.h>
|
|
||||||
|
|
||||||
#include "lib/config.hpp"
|
|
||||||
#include "lib/shared.hpp"
|
|
||||||
|
|
||||||
extern TaskHandle_t priceFetchTaskHandle;
|
|
||||||
|
|
||||||
void setupPriceFetchTask();
|
|
||||||
void taskPriceFetch(void *pvParameters);
|
|
|
@ -5,6 +5,39 @@ const char *wsOwnServerV2 = "wss://ws-staging.btclock.dev/api/v2/ws";
|
||||||
|
|
||||||
const char *wsServerPrice = "wss://ws.coincap.io/prices?assets=bitcoin";
|
const char *wsServerPrice = "wss://ws.coincap.io/prices?assets=bitcoin";
|
||||||
|
|
||||||
|
const char* coincapWsCert = R"EOF(
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
)EOF";
|
||||||
|
|
||||||
// WebsocketsClient client;
|
// WebsocketsClient client;
|
||||||
esp_websocket_client_handle_t clientPrice = NULL;
|
esp_websocket_client_handle_t clientPrice = NULL;
|
||||||
|
@ -14,6 +47,7 @@ unsigned long int lastPriceUpdate;
|
||||||
bool priceNotifyInit = false;
|
bool priceNotifyInit = false;
|
||||||
std::map<char, std::uint64_t> currencyMap;
|
std::map<char, std::uint64_t> currencyMap;
|
||||||
std::map<char, unsigned long int> lastUpdateMap;
|
std::map<char, unsigned long int> lastUpdateMap;
|
||||||
|
WebSocketsClient priceNotifyWs;
|
||||||
|
|
||||||
void setupPriceNotify()
|
void setupPriceNotify()
|
||||||
{
|
{
|
||||||
|
@ -26,14 +60,51 @@ void setupPriceNotify()
|
||||||
{
|
{
|
||||||
config = {.uri = wsServerPrice,
|
config = {.uri = wsServerPrice,
|
||||||
.user_agent = USER_AGENT};
|
.user_agent = USER_AGENT};
|
||||||
|
config.cert_pem = coincapWsCert;
|
||||||
|
config.task_stack = (6*1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
clientPrice = esp_websocket_client_init(&config);
|
clientPrice = esp_websocket_client_init(&config);
|
||||||
esp_websocket_register_events(clientPrice, WEBSOCKET_EVENT_ANY,
|
esp_websocket_register_events(clientPrice, WEBSOCKET_EVENT_ANY,
|
||||||
onWebsocketPriceEvent, clientPrice);
|
onWebsocketPriceEvent, clientPrice);
|
||||||
esp_websocket_client_start(clientPrice);
|
esp_websocket_client_start(clientPrice);
|
||||||
|
|
||||||
|
// priceNotifyWs.beginSSL("ws.coincap.io", 443, "/prices?assets=bitcoin");
|
||||||
|
// priceNotifyWs.onEvent(onWebsocketPriceEvent);
|
||||||
|
// priceNotifyWs.setReconnectInterval(5000);
|
||||||
|
// priceNotifyWs.enableHeartbeat(15000, 3000, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// void onWebsocketPriceEvent(WStype_t type, uint8_t * payload, size_t length) {
|
||||||
|
// switch(type) {
|
||||||
|
// case WStype_DISCONNECTED:
|
||||||
|
// Serial.printf("[WSc] Disconnected!\n");
|
||||||
|
// break;
|
||||||
|
// case WStype_CONNECTED:
|
||||||
|
// {
|
||||||
|
// Serial.printf("[WSc] Connected to url: %s\n", payload);
|
||||||
|
|
||||||
|
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case WStype_TEXT:
|
||||||
|
// String message = String((char*)payload);
|
||||||
|
// onWebsocketPriceMessage(message);
|
||||||
|
// break;
|
||||||
|
// case WStype_BIN:
|
||||||
|
// break;
|
||||||
|
// case WStype_ERROR:
|
||||||
|
// case WStype_FRAGMENT_TEXT_START:
|
||||||
|
// case WStype_FRAGMENT_BIN_START:
|
||||||
|
// case WStype_FRAGMENT:
|
||||||
|
// case WStype_PING:
|
||||||
|
// case WStype_PONG:
|
||||||
|
// case WStype_FRAGMENT_FIN:
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base,
|
void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base,
|
||||||
int32_t event_id, void *event_data)
|
int32_t event_id, void *event_data)
|
||||||
{
|
{
|
||||||
|
@ -83,7 +154,7 @@ void processNewPrice(uint newPrice, char currency)
|
||||||
"minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE);
|
"minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE);
|
||||||
uint currentTime = esp_timer_get_time() / 1000000;
|
uint currentTime = esp_timer_get_time() / 1000000;
|
||||||
|
|
||||||
if (lastUpdateMap.find(currency) == lastUpdateMap.end()||
|
if (lastUpdateMap.find(currency) == lastUpdateMap.end() ||
|
||||||
(currentTime - lastUpdateMap[currency]) > minSecPriceUpd)
|
(currentTime - lastUpdateMap[currency]) > minSecPriceUpd)
|
||||||
{
|
{
|
||||||
// const unsigned long oldPrice = currentPrice;
|
// const unsigned long oldPrice = currentPrice;
|
||||||
|
@ -108,21 +179,25 @@ void processNewPrice(uint newPrice, char currency)
|
||||||
|
|
||||||
uint getLastPriceUpdate(char currency)
|
uint getLastPriceUpdate(char currency)
|
||||||
{
|
{
|
||||||
if (lastUpdateMap.find(currency) == lastUpdateMap.end()) {
|
if (lastUpdateMap.find(currency) == lastUpdateMap.end())
|
||||||
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return lastUpdateMap[currency];
|
return lastUpdateMap[currency];
|
||||||
}
|
}
|
||||||
|
|
||||||
uint getPrice(char currency) {
|
uint getPrice(char currency)
|
||||||
if (currencyMap.find(currency) == currencyMap.end()) {
|
{
|
||||||
|
if (currencyMap.find(currency) == currencyMap.end())
|
||||||
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return currencyMap[currency];
|
return currencyMap[currency];
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPrice(uint newPrice, char currency) {
|
void setPrice(uint newPrice, char currency)
|
||||||
|
{
|
||||||
currencyMap[currency] = newPrice;
|
currencyMap[currency] = newPrice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@ void setupPriceNotify();
|
||||||
|
|
||||||
void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base,
|
void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base,
|
||||||
int32_t event_id, void *event_data);
|
int32_t event_id, void *event_data);
|
||||||
|
//void onWebsocketPriceEvent(WStype_t type, uint8_t * payload, size_t length);
|
||||||
|
|
||||||
void onWebsocketPriceMessage(esp_websocket_event_data_t *event_data);
|
void onWebsocketPriceMessage(esp_websocket_event_data_t *event_data);
|
||||||
|
|
||||||
uint getPrice(char currency);
|
uint getPrice(char currency);
|
||||||
|
|
|
@ -126,9 +126,6 @@ void IRAM_ATTR minuteTimerISR(void *arg) {
|
||||||
// vTaskNotifyGiveFromISR(timeUpdateTaskHandle, &xHigherPriorityTaskWoken);
|
// vTaskNotifyGiveFromISR(timeUpdateTaskHandle, &xHigherPriorityTaskWoken);
|
||||||
WorkItem timeUpdate = {TASK_TIME_UPDATE, 0};
|
WorkItem timeUpdate = {TASK_TIME_UPDATE, 0};
|
||||||
xQueueSendFromISR(workQueue, &timeUpdate, &xHigherPriorityTaskWoken);
|
xQueueSendFromISR(workQueue, &timeUpdate, &xHigherPriorityTaskWoken);
|
||||||
if (priceFetchTaskHandle != NULL) {
|
|
||||||
vTaskNotifyGiveFromISR(priceFetchTaskHandle, &xHigherPriorityTaskWoken);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bitaxeFetchTaskHandle != NULL) {
|
if (bitaxeFetchTaskHandle != NULL) {
|
||||||
vTaskNotifyGiveFromISR(bitaxeFetchTaskHandle, &xHigherPriorityTaskWoken);
|
vTaskNotifyGiveFromISR(bitaxeFetchTaskHandle, &xHigherPriorityTaskWoken);
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#include <bitaxe_handler.hpp>
|
#include <bitaxe_handler.hpp>
|
||||||
|
|
||||||
#include "lib/epd.hpp"
|
#include "lib/epd.hpp"
|
||||||
#include "lib/price_fetch.hpp"
|
|
||||||
#include "lib/shared.hpp"
|
#include "lib/shared.hpp"
|
||||||
|
|
||||||
// extern TaskHandle_t priceUpdateTaskHandle;
|
// extern TaskHandle_t priceUpdateTaskHandle;
|
||||||
|
|
|
@ -1,5 +1,45 @@
|
||||||
#include "shared.hpp"
|
#include "shared.hpp"
|
||||||
|
|
||||||
|
const char *github_root_ca =
|
||||||
|
"-----BEGIN CERTIFICATE-----\n"
|
||||||
|
"MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL\n"
|
||||||
|
"MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl\n"
|
||||||
|
"eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT\n"
|
||||||
|
"JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx\n"
|
||||||
|
"MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT\n"
|
||||||
|
"Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg\n"
|
||||||
|
"VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm\n"
|
||||||
|
"aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo\n"
|
||||||
|
"I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng\n"
|
||||||
|
"o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G\n"
|
||||||
|
"A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD\n"
|
||||||
|
"VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB\n"
|
||||||
|
"zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW\n"
|
||||||
|
"RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg=\n"
|
||||||
|
"-----END CERTIFICATE-----\n"
|
||||||
|
"-----BEGIN CERTIFICATE-----\n"
|
||||||
|
"MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh\n"
|
||||||
|
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"
|
||||||
|
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH\n"
|
||||||
|
"MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT\n"
|
||||||
|
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n"
|
||||||
|
"b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG\n"
|
||||||
|
"9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI\n"
|
||||||
|
"2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx\n"
|
||||||
|
"1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ\n"
|
||||||
|
"q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz\n"
|
||||||
|
"tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ\n"
|
||||||
|
"vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP\n"
|
||||||
|
"BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV\n"
|
||||||
|
"5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY\n"
|
||||||
|
"1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4\n"
|
||||||
|
"NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG\n"
|
||||||
|
"Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91\n"
|
||||||
|
"8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe\n"
|
||||||
|
"pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl\n"
|
||||||
|
"MrY=\n"
|
||||||
|
"-----END CERTIFICATE-----\n";
|
||||||
|
|
||||||
#ifdef TEST_SCREENS
|
#ifdef TEST_SCREENS
|
||||||
uint8_t input_buffer[3 * input_buffer_pixels]; // up to depth 24
|
uint8_t input_buffer[3 * input_buffer_pixels]; // up to depth 24
|
||||||
uint8_t output_row_mono_buffer[max_row_width / 8]; // buffer for at least one row of b/w bits
|
uint8_t output_row_mono_buffer[max_row_width / 8]; // buffer for at least one row of b/w bits
|
||||||
|
@ -8,3 +48,27 @@ uint8_t mono_palette_buffer[max_palette_pixels / 8]; // palette buffer for depth
|
||||||
uint8_t color_palette_buffer[max_palette_pixels / 8]; // palette buffer for depth <= 8 c/w
|
uint8_t color_palette_buffer[max_palette_pixels / 8]; // palette buffer for depth <= 8 c/w
|
||||||
uint16_t rgb_palette_buffer[max_palette_pixels]; // palette buffer for depth <= 8 for buffered graphics, needed for 7-color display
|
uint16_t rgb_palette_buffer[max_palette_pixels]; // palette buffer for depth <= 8 for buffered graphics, needed for 7-color display
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Function to calculate SHA-256 hash
|
||||||
|
String calculateSHA256(uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
byte shaResult[32];
|
||||||
|
mbedtls_md_context_t ctx;
|
||||||
|
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
|
||||||
|
|
||||||
|
mbedtls_md_init(&ctx);
|
||||||
|
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0);
|
||||||
|
mbedtls_md_starts(&ctx);
|
||||||
|
mbedtls_md_update(&ctx, data, len);
|
||||||
|
mbedtls_md_finish(&ctx, shaResult);
|
||||||
|
mbedtls_md_free(&ctx);
|
||||||
|
|
||||||
|
char sha256_str[65];
|
||||||
|
for (int i = 0; i < 32; i++)
|
||||||
|
{
|
||||||
|
sprintf(sha256_str + (i * 2), "%02x", shaResult[i]);
|
||||||
|
}
|
||||||
|
sha256_str[64] = 0;
|
||||||
|
|
||||||
|
return String(sha256_str);
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <freertos/task.h>
|
#include <freertos/task.h>
|
||||||
#include <GxEPD2.h>
|
#include <GxEPD2.h>
|
||||||
#include <GxEPD2_BW.h>
|
#include <GxEPD2_BW.h>
|
||||||
|
#include <mbedtls/md.h>
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <utils.hpp>
|
#include <utils.hpp>
|
||||||
|
@ -65,7 +66,16 @@ const PROGMEM int screens[SCREEN_COUNT] = {
|
||||||
const int usPerSecond = 1000000;
|
const int usPerSecond = 1000000;
|
||||||
const int usPerMinute = 60 * usPerSecond;
|
const int usPerMinute = 60 * usPerSecond;
|
||||||
|
|
||||||
|
extern const char *github_root_ca;
|
||||||
|
|
||||||
|
const PROGMEM char UPDATE_FIRMWARE = 0;
|
||||||
|
const PROGMEM char UPDATE_WEBUI = 1;
|
||||||
|
|
||||||
|
|
||||||
struct ScreenMapping {
|
struct ScreenMapping {
|
||||||
int value;
|
int value;
|
||||||
const char* name;
|
const char* name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
String calculateSHA256(uint8_t* data, size_t len);
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@ void setupV2Notify()
|
||||||
if ( preferences.getBool("stagingSource", DEFAULT_STAGING_SOURCE)) {
|
if ( preferences.getBool("stagingSource", DEFAULT_STAGING_SOURCE)) {
|
||||||
Serial.println(F("Connecting to V2 staging source"));
|
Serial.println(F("Connecting to V2 staging source"));
|
||||||
hostname = "ws-staging.btclock.dev";
|
hostname = "ws-staging.btclock.dev";
|
||||||
|
} else {
|
||||||
|
Serial.println(F("Connecting to V2 source"));
|
||||||
}
|
}
|
||||||
|
|
||||||
webSocket.beginSSL(hostname, 443, "/api/v2/ws");
|
webSocket.beginSSL(hostname, 443, "/api/v2/ws");
|
||||||
|
|
|
@ -86,6 +86,8 @@ void setupWebserver()
|
||||||
{
|
{
|
||||||
server.on("/upload/firmware", HTTP_POST, onFirmwareUpdate, asyncFirmwareUpdateHandler);
|
server.on("/upload/firmware", HTTP_POST, onFirmwareUpdate, asyncFirmwareUpdateHandler);
|
||||||
server.on("/upload/webui", HTTP_POST, onFirmwareUpdate, asyncWebuiUpdateHandler);
|
server.on("/upload/webui", HTTP_POST, onFirmwareUpdate, asyncWebuiUpdateHandler);
|
||||||
|
// server.on("/update/webui", HTTP_GET, onUpdateWebUi);
|
||||||
|
// server.on("/update/firmware", HTTP_GET, onUpdateFirmware);
|
||||||
}
|
}
|
||||||
|
|
||||||
server.on("/api/restart", HTTP_GET, onApiRestart);
|
server.on("/api/restart", HTTP_GET, onApiRestart);
|
||||||
|
@ -662,7 +664,7 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
|
||||||
root["mcapBigChar"] = preferences.getBool("mcapBigChar", DEFAULT_MCAP_BIG_CHAR);
|
root["mcapBigChar"] = preferences.getBool("mcapBigChar", DEFAULT_MCAP_BIG_CHAR);
|
||||||
root["mdnsEnabled"] = preferences.getBool("mdnsEnabled", DEFAULT_MDNS_ENABLED);
|
root["mdnsEnabled"] = preferences.getBool("mdnsEnabled", DEFAULT_MDNS_ENABLED);
|
||||||
root["otaEnabled"] = preferences.getBool("otaEnabled", DEFAULT_OTA_ENABLED);
|
root["otaEnabled"] = preferences.getBool("otaEnabled", DEFAULT_OTA_ENABLED);
|
||||||
root["fetchEurPrice"] = preferences.getBool("fetchEurPrice", DEFAULT_FETCH_EUR_PRICE);
|
// root["fetchEurPrice"] = preferences.getBool("fetchEurPrice", DEFAULT_FETCH_EUR_PRICE);
|
||||||
root["useSatsSymbol"] = preferences.getBool("useSatsSymbol", DEFAULT_USE_SATS_SYMBOL);
|
root["useSatsSymbol"] = preferences.getBool("useSatsSymbol", DEFAULT_USE_SATS_SYMBOL);
|
||||||
root["useBlkCountdown"] = preferences.getBool("useBlkCountdown", DEFAULT_USE_BLOCK_COUNTDOWN);
|
root["useBlkCountdown"] = preferences.getBool("useBlkCountdown", DEFAULT_USE_BLOCK_COUNTDOWN);
|
||||||
root["suffixPrice"] = preferences.getBool("suffixPrice", DEFAULT_SUFFIX_PRICE);
|
root["suffixPrice"] = preferences.getBool("suffixPrice", DEFAULT_SUFFIX_PRICE);
|
||||||
|
@ -1043,6 +1045,167 @@ void onApiShowCurrency(AsyncWebServerRequest *request)
|
||||||
request->send(404);
|
request->send(404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String getLatestRelease(const String &fileToDownload)
|
||||||
|
{
|
||||||
|
|
||||||
|
// const char *fileToDownload = "littlefs.bin";
|
||||||
|
|
||||||
|
String releaseUrl = "https://api.github.com/repos/btclock/btclock_v3/releases/latest";
|
||||||
|
WiFiClientSecure client;
|
||||||
|
client.setCACert(github_root_ca);
|
||||||
|
HTTPClient http;
|
||||||
|
http.begin(client, releaseUrl);
|
||||||
|
http.setUserAgent(USER_AGENT);
|
||||||
|
|
||||||
|
int httpCode = http.GET();
|
||||||
|
|
||||||
|
String downloadUrl = "";
|
||||||
|
|
||||||
|
if (httpCode > 0)
|
||||||
|
{
|
||||||
|
String payload = http.getString();
|
||||||
|
|
||||||
|
JsonDocument doc;
|
||||||
|
deserializeJson(doc, payload);
|
||||||
|
|
||||||
|
JsonArray assets = doc["assets"];
|
||||||
|
|
||||||
|
for (JsonObject asset : assets)
|
||||||
|
{
|
||||||
|
if (asset["name"] == fileToDownload)
|
||||||
|
{
|
||||||
|
downloadUrl = asset["browser_download_url"].as<String>();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Serial.printf("Latest release URL: %s\r\n", downloadUrl.c_str());
|
||||||
|
}
|
||||||
|
return downloadUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void onUpdateWebUi(AsyncWebServerRequest *request)
|
||||||
|
{
|
||||||
|
request->send(downloadUpdateHandler(UPDATE_WEBUI));
|
||||||
|
}
|
||||||
|
|
||||||
|
void onUpdateFirmware(AsyncWebServerRequest *request)
|
||||||
|
{
|
||||||
|
request->send(downloadUpdateHandler(UPDATE_FIRMWARE));
|
||||||
|
}
|
||||||
|
|
||||||
|
int downloadUpdateHandler(char updateType)
|
||||||
|
{
|
||||||
|
WiFiClientSecure client;
|
||||||
|
client.setCACert(github_root_ca);
|
||||||
|
HTTPClient http;
|
||||||
|
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||||
|
|
||||||
|
String latestRelease = "";
|
||||||
|
|
||||||
|
switch (updateType)
|
||||||
|
{
|
||||||
|
case UPDATE_FIRMWARE:
|
||||||
|
latestRelease = getLatestRelease(getFirmwareFilename());
|
||||||
|
break;
|
||||||
|
case UPDATE_WEBUI:
|
||||||
|
latestRelease = getLatestRelease("littlefs.bin");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (latestRelease.equals(""))
|
||||||
|
{
|
||||||
|
return 503;
|
||||||
|
}
|
||||||
|
|
||||||
|
http.begin(client, latestRelease);
|
||||||
|
http.setUserAgent(USER_AGENT);
|
||||||
|
|
||||||
|
int httpCode = http.GET();
|
||||||
|
if (httpCode == HTTP_CODE_OK)
|
||||||
|
{
|
||||||
|
int contentLength = http.getSize();
|
||||||
|
if (contentLength > 0)
|
||||||
|
{
|
||||||
|
uint8_t *buffer = (uint8_t *)malloc(contentLength);
|
||||||
|
if (buffer)
|
||||||
|
{
|
||||||
|
WiFiClient *stream = http.getStreamPtr();
|
||||||
|
size_t written = stream->readBytes(buffer, contentLength);
|
||||||
|
|
||||||
|
if (written == contentLength)
|
||||||
|
{
|
||||||
|
String calculated_sha256 = calculateSHA256(buffer, contentLength);
|
||||||
|
Serial.print("Checksum is ");
|
||||||
|
Serial.println(calculated_sha256);
|
||||||
|
if (true)
|
||||||
|
{
|
||||||
|
Serial.println("Checksum verified. Proceeding with update.");
|
||||||
|
|
||||||
|
Update.onProgress(onOTAProgress);
|
||||||
|
|
||||||
|
int updateType = U_FLASH;
|
||||||
|
|
||||||
|
switch (updateType)
|
||||||
|
{
|
||||||
|
case UPDATE_WEBUI:
|
||||||
|
updateType = U_SPIFFS;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
updateType = U_FLASH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Update.begin(contentLength, updateType))
|
||||||
|
{
|
||||||
|
Update.write(buffer, contentLength);
|
||||||
|
if (Update.end())
|
||||||
|
{
|
||||||
|
Serial.println("Update complete. Rebooting.");
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.println("Error in update process.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.println("Not enough space to begin OTA");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.println("Checksum mismatch. Aborting update.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.println("Error downloading firmware");
|
||||||
|
}
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.println("Not enough memory to allocate buffer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.println("Invalid content length");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.print(httpCode);
|
||||||
|
Serial.println("Error on HTTP request");
|
||||||
|
return 503;
|
||||||
|
}
|
||||||
|
http.end();
|
||||||
|
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HAS_FRONTLIGHT
|
#ifdef HAS_FRONTLIGHT
|
||||||
void onApiFrontlightOn(AsyncWebServerRequest *request)
|
void onApiFrontlightOn(AsyncWebServerRequest *request)
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,10 +21,17 @@ void stopWebServer();
|
||||||
void setupWebserver();
|
void setupWebserver();
|
||||||
bool processEpdColorSettings(AsyncWebServerRequest *request);
|
bool processEpdColorSettings(AsyncWebServerRequest *request);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void onApiStatus(AsyncWebServerRequest *request);
|
void onApiStatus(AsyncWebServerRequest *request);
|
||||||
void onApiSystemStatus(AsyncWebServerRequest *request);
|
void onApiSystemStatus(AsyncWebServerRequest *request);
|
||||||
void onApiSetWifiTxPower(AsyncWebServerRequest *request);
|
void onApiSetWifiTxPower(AsyncWebServerRequest *request);
|
||||||
|
void onUpdateWebUi(AsyncWebServerRequest *request);
|
||||||
|
void onUpdateFirmware(AsyncWebServerRequest *request);
|
||||||
|
|
||||||
|
int downloadUpdateHandler(char updateType);
|
||||||
|
|
||||||
|
String getLatestRelease(const String& fileToDownload);
|
||||||
|
|
||||||
void onApiScreenNext(AsyncWebServerRequest *request);
|
void onApiScreenNext(AsyncWebServerRequest *request);
|
||||||
void onApiScreenPrevious(AsyncWebServerRequest *request);
|
void onApiScreenPrevious(AsyncWebServerRequest *request);
|
||||||
|
|
Loading…
Reference in a new issue