Improved screen update tasks and display update mechanism

This commit is contained in:
Djuri Baars 2023-11-12 12:38:28 +01:00
parent 466aa5be4a
commit d0eb007c4c
12 changed files with 619 additions and 411 deletions

View file

@ -2,21 +2,67 @@
char *wsServer;
esp_websocket_client_handle_t blockNotifyClient = NULL;
unsigned long int currentBlockHeight = 816000;
uint currentBlockHeight = 816000;
// const char *mempoolWsCert = R"(-----BEGIN CERTIFICATE-----
// MIIHfTCCBmWgAwIBAgIRANFX3mhqRYDt1NFuENoSyaAwDQYJKoZIhvcNAQELBQAw
// gZUxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
// BgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDE9MDsGA1UE
// AxM0U2VjdGlnbyBSU0EgT3JnYW5pemF0aW9uIFZhbGlkYXRpb24gU2VjdXJlIFNl
// cnZlciBDQTAeFw0yMzA3MjQwMDAwMDBaFw0yNDA4MjIyMzU5NTlaMFcxCzAJBgNV
// BAYTAkpQMQ4wDAYDVQQIEwVUb2t5bzEgMB4GA1UEChMXTUVNUE9PTCBTUEFDRSBD
// Ty4sIExURC4xFjAUBgNVBAMTDW1lbXBvb2wuc3BhY2UwggEiMA0GCSqGSIb3DQEB
// AQUAA4IBDwAwggEKAoIBAQCqmiPRWgo58d25R0biQjAksXMq5ciH7z7ZQo2w2AbB
// rHxpnlIry74b9S4wRY5UJeYmd6ZwA76NdSioDvxTJc29bLplY+Ftmfc4ET0zYb2k
// Fi86z7GOWb6Ezor/qez9uMM9cxd021Bvcs0/2OrL6Sgp66u9keDZv9NyvFPpXfuR
// tdV2r4HF57VJqZn105PN4k80kNWgDbae8aw+BuUNvQYKEe71yfB7Bh6zSh9pCSfM
// I6pIJdQzoada2uY1dQMoJeIq8qKNKqAPKGsH5McemUT5ZIKU/tjk3nfX0pz/sQa4
// CN7tLH6UeUlctei92GFd6Xtn7RbKLhDUbc4Sq02Cc9iXAgMBAAGjggQDMIID/zAf
// BgNVHSMEGDAWgBQX2dYlJ2f5McJJQ9kwNkSMbKlP6zAdBgNVHQ4EFgQUXkxoddJ6
// rKobsbmDdtuCK1ywXuIwDgYDVR0PAQH/BAQDAgWgMAwGA1UdEwEB/wQCMAAwHQYD
// VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMEoGA1UdIARDMEEwNQYMKwYBBAGy
// MQECAQMEMCUwIwYIKwYBBQUHAgEWF2h0dHBzOi8vc2VjdGlnby5jb20vQ1BTMAgG
// BmeBDAECAjBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLnNlY3RpZ28uY29t
// L1NlY3RpZ29SU0FPcmdhbml6YXRpb25WYWxpZGF0aW9uU2VjdXJlU2VydmVyQ0Eu
// Y3JsMIGKBggrBgEFBQcBAQR+MHwwVQYIKwYBBQUHMAKGSWh0dHA6Ly9jcnQuc2Vj
// dGlnby5jb20vU2VjdGlnb1JTQU9yZ2FuaXphdGlvblZhbGlkYXRpb25TZWN1cmVT
// ZXJ2ZXJDQS5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNlY3RpZ28uY29t
// MIIBgAYKKwYBBAHWeQIEAgSCAXAEggFsAWoAdwB2/4g/Crb7lVHCYcz1h7o0tKTN
// uyncaEIKn+ZnTFo6dAAAAYmc9m/gAAAEAwBIMEYCIQD8XOozx411S/bnZambGjTB
// yTcr2fCmggUfQLSmqksD5gIhAIjiEMg0o1VSuQW31gWzfzL6idCkIZeSKN104cdp
// xa4SAHcA2ra/az+1tiKfm8K7XGvocJFxbLtRhIU0vaQ9MEjX+6sAAAGJnPZwPwAA
// BAMASDBGAiEA2sPTZTzvxewzQ8vk36+BWAKuJS7AvJ5W3clvfwCa8OUCIQC74ekT
// Ged2fqQE4sVy74aS6HRA2ihC9VLtNrASJx1YjQB2AO7N0GTV2xrOxVy3nbTNE6Iy
// h0Z8vOzew1FIWUZxH7WbAAABiZz2cA8AAAQDAEcwRQIgEklH7wYCFuuJIFUHX5PY
// /vZ3bDoxOp+061PT3caa+rICIQC0abgfGlBKiHxp47JZxnW3wcVqWdiYX4ViLm9H
// xfx4ljCBxgYDVR0RBIG+MIG7gg1tZW1wb29sLnNwYWNlghMqLmZtdC5tZW1wb29s
// LnNwYWNlghMqLmZyYS5tZW1wb29sLnNwYWNlgg8qLm1lbXBvb2wuc3BhY2WCEyou
// dGs3Lm1lbXBvb2wuc3BhY2WCEyoudmExLm1lbXBvb2wuc3BhY2WCDGJpc3EubWFy
// a2V0c4IKYmlzcS5uaW5qYYIObGlxdWlkLm5ldHdvcmuCDGxpcXVpZC5wbGFjZYIN
// bWVtcG9vbC5uaW5qYTANBgkqhkiG9w0BAQsFAAOCAQEAFvOSRnlHDfq9C8acjZEG
// 5XIqjNYigyWyjOvx83of6Z3PBKkAZB5D/UHBPp+jBDJiEb/QXC7Z7Y7kpuvnoVib
// b4jDc0RjGEsxL+3F7cSw26m3wILJhhHooGZRmFY4GOAeCZtYCOTzJsiZvFpDoQjU
// hTBxtaps05z0Ly9/eYvkXnjnBNROZJVR+KYHlq4TIoGNc4q4KvpfHv2I/vhS2M1e
// bECNNPEyRxHGKdXXO3huocE7aVKpy+JDR6cWwDu6hpdc1j/SCDqdTDFQ7McHOrqA
// fpPh4FcfePMh7Mqxtg2pSs5pXPtiP0ZjLgxd7HbAXct8Y+/jGk+k3sx3SeYXVimr
// ew==
// -----END CERTIFICATE-----)";
void setupBlockNotify()
{
currentBlockHeight = preferences.getULong("blockHeight", 816000);
//currentBlockHeight = preferences.getUInt("blockHeight", 816000);
IPAddress result;
int dnsErr = -1;
String mempoolInstance = preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
while (dnsErr != 1) {
while (dnsErr != 1)
{
dnsErr = WiFi.hostByName(mempoolInstance.c_str(), result);
if (dnsErr != 1) {
if (dnsErr != 1)
{
Serial.print(mempoolInstance);
Serial.println(F("mempool DNS could not be resolved"));
WiFi.reconnect();
@ -33,13 +79,20 @@ void setupBlockNotify()
{
String blockHeightStr = http->getString();
currentBlockHeight = blockHeightStr.toInt();
xTaskNotifyGive(blockUpdateTaskHandle);
// xTaskNotifyGive(blockUpdateTaskHandle);
if (workQueue != nullptr)
{
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
}
}
// std::strcpy(wsServer, String("wss://" + mempoolInstance + "/api/v1/ws").c_str());
// std::strcpy(wsServer, String("wss://" + mempoolInstance + "/api/v1/ws").c_str());
esp_websocket_client_config_t config = {
.uri = "wss://mempool.space/api/v1/ws",
// .task_stack = (6*1024),
// .cert_pem = mempoolWsCert,
.user_agent = USER_AGENT,
};
@ -56,7 +109,7 @@ void onWebsocketEvent(void *handler_args, esp_event_base_t base, int32_t event_i
{
case WEBSOCKET_EVENT_CONNECTED:
Serial.println(F("Connected to Mempool.space WebSocket"));
Serial.println(sub);
if (esp_websocket_client_send_text(blockNotifyClient, sub.c_str(), sub.length(), portMAX_DELAY) == -1)
{
@ -87,40 +140,53 @@ void onWebsocketMessage(esp_websocket_event_data_t *event_data)
{
JsonObject block = doc["block"];
currentBlockHeight = block["height"].as<long>();
currentBlockHeight = block["height"].as<uint>();
Serial.printf("New block found: %d\r\n", block["height"].as<long>());
preferences.putULong("blockHeight", currentBlockHeight);
Serial.printf("New block found: %d\r\n", block["height"].as<uint>());
size_t prefWrite = preferences.putUInt("blockHeight", currentBlockHeight);
Serial.printf("Wrote %d for block\r\n", prefWrite);
if (blockUpdateTaskHandle != nullptr) {
xTaskNotifyGive(blockUpdateTaskHandle);
if (workQueue != nullptr)
{
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
// xTaskNotifyGive(blockUpdateTaskHandle);
if (getCurrentScreen() != SCREEN_BLOCK_HEIGHT && preferences.getBool("stealFocus", true)) {
if (getCurrentScreen() != SCREEN_BLOCK_HEIGHT && preferences.getBool("stealFocus", true))
{
setCurrentScreen(SCREEN_BLOCK_HEIGHT);
}
if (getCurrentScreen() == SCREEN_BLOCK_HEIGHT && preferences.getBool("ledFlashOnUpd", false)) {
if (getCurrentScreen() == SCREEN_BLOCK_HEIGHT && preferences.getBool("ledFlashOnUpd", false))
{
vTaskDelay(pdMS_TO_TICKS(250)); // Wait until screens are updated
queueLedEffect(LED_FLASH_BLOCK_NOTIFY);
}
}
}
}
doc.clear();
}
unsigned long getBlockHeight()
uint getBlockHeight()
{
return currentBlockHeight;
}
bool isBlockNotifyConnected() {
void setBlockHeight(uint newBlockHeight)
{
currentBlockHeight = newBlockHeight;
}
bool isBlockNotifyConnected()
{
if (blockNotifyClient == NULL)
return false;
return esp_websocket_client_is_connected(blockNotifyClient);
}
void stopBlockNotify() {
void stopBlockNotify()
{
esp_websocket_client_stop(blockNotifyClient);
esp_websocket_client_destroy(blockNotifyClient);
}

View file

@ -18,6 +18,7 @@ void setupBlockNotify();
void onWebsocketEvent(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data);
void onWebsocketMessage(esp_websocket_event_data_t* event_data);
unsigned long getBlockHeight();
void setBlockHeight(uint newBlockHeight);
uint getBlockHeight();
bool isBlockNotifyConnected();
void stopBlockNotify();

View file

@ -71,16 +71,13 @@ void tryImprovSetup()
WiFi.softAPIP().toString().c_str(),
wifiManager->getConfigPortalSSID().c_str(),
softAP_password.c_str());
// vTaskDelay(pdMS_TO_TICKS(1000));
delay(6000);
const String qrText = "qrWIFI:S:" + wifiManager->getConfigPortalSSID() + ";T:WPA;P:" + softAP_password.c_str() + ";;";
const String explainText = "*SSID: *\r\n" + wifiManager->getConfigPortalSSID() + "\r\n\r\n*Password:*\r\n" + softAP_password;
std::array<String, NUM_SCREENS> epdContent = {"Welcome!", "", "To setup\r\nscan QR or\r\nconnect\r\nmanually", "", explainText, "", qrText};
setEpdContent(epdContent);
delay(3000);
Serial.println("xTask");
xTaskNotifyGive(epdTaskHandle); });
});
wm.setSaveConfigCallback([]()
{
@ -143,8 +140,6 @@ void setupTime()
delay(500);
Serial.println(F("Retry set time"));
}
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
}
void setupPreferences()
@ -154,6 +149,8 @@ void setupPreferences()
setFgColor(preferences.getUInt("fgColor", DEFAULT_FG_COLOR));
setBgColor(preferences.getUInt("bgColor", DEFAULT_BG_COLOR));
setBlockHeight(preferences.getUInt("blockHeight", 816000));
setPrice(preferences.getUInt("lastPrice", 30000));
screenNameMap[SCREEN_BLOCK_HEIGHT] = "Block Height";
screenNameMap[SCREEN_MSCW_TIME] = "Sats per dollar";
@ -201,12 +198,8 @@ void setupHardware()
WiFi.setHostname(getMyHostname().c_str());
;
if (psramInit())
{
Serial.println(F("PSRAM is correctly initialized"));
}
else
{
if (!psramInit())
{
Serial.println(F("PSRAM not available"));
}

View file

@ -53,7 +53,10 @@ std::array<String, NUM_SCREENS> currentEpdContent;
std::array<String, NUM_SCREENS> epdContent;
uint32_t lastFullRefresh[NUM_SCREENS];
TaskHandle_t tasks[NUM_SCREENS];
TaskHandle_t epdTaskHandle = NULL;
// TaskHandle_t epdTaskHandle = NULL;
#define UPDATE_QUEUE_SIZE 14
QueueHandle_t updateQueue;
SemaphoreHandle_t epdUpdateSemaphore[NUM_SCREENS];
@ -72,6 +75,10 @@ void setupDisplays()
displays[i].init();
}
updateQueue = xQueueCreate(UPDATE_QUEUE_SIZE, sizeof(UpdateDisplayTaskItem));
xTaskCreate(prepareDisplayUpdateTask, "PrepareUpd", 4096, NULL, tskIDLE_PRIORITY, NULL);
for (uint i = 0; i < NUM_SCREENS; i++)
{
epdUpdateSemaphore[i] = xSemaphoreCreateBinary();
@ -80,11 +87,9 @@ void setupDisplays()
int *taskParam = new int;
*taskParam = i;
xTaskCreate(updateDisplay, ("EpdUpd" + String(i)).c_str(), 4096, taskParam, tskIDLE_PRIORITY, &tasks[i]); // create task
xTaskCreate(updateDisplay, ("EpdUpd" + String(i)).c_str(), 2048, taskParam, tskIDLE_PRIORITY, &tasks[i]); // create task
}
xTaskCreate(taskEpd, "epd_task", 2048, NULL, tskIDLE_PRIORITY, &epdTaskHandle);
epdContent = {"B",
"T",
"C",
@ -94,99 +99,59 @@ void setupDisplays()
"K"};
setEpdContent(epdContent);
for (uint i = 0; i < NUM_SCREENS; i++)
{
xTaskNotifyGive(tasks[i]);
}
}
void taskEpd(void *pvParameters)
{
while (1)
{
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
bool updatedThisCycle = false;
for (uint i = 0; i < NUM_SCREENS; i++)
{
if (epdContent[i].compareTo(currentEpdContent[i]) != 0)
{
if (!updatedThisCycle)
{
updatedThisCycle = true;
}
if (xSemaphoreTake(epdUpdateSemaphore[i], pdMS_TO_TICKS(5000)) == pdTRUE)
{
xTaskNotifyGive(tasks[i]);
}
else
{
Serial.println("Couldnt get screen" + String(i));
}
}
}
}
}
void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent)
{
epdContent = newEpdContent;
if (epdTaskHandle != NULL)
xTaskNotifyGive(epdTaskHandle);
for (uint i = 0; i < NUM_SCREENS; i++)
{
if (epdContent[i].compareTo(currentEpdContent[i]) != 0)
{
UpdateDisplayTaskItem dispUpdate = {i};
xQueueSend(updateQueue, &dispUpdate, portMAX_DELAY);
// if (xSemaphoreTake(epdUpdateSemaphore[i], pdMS_TO_TICKS(5000)) == pdTRUE)
// {
// xTaskNotifyGive(tasks[i]);
// }
}
}
if (eventSourceTaskHandle != NULL)
xTaskNotifyGive(eventSourceTaskHandle);
}
extern "C" void updateDisplay(void *pvParameters) noexcept
void prepareDisplayUpdateTask(void *pvParameters)
{
const int epdIndex = *(int *)pvParameters;
delete (int *)pvParameters;
UpdateDisplayTaskItem receivedItem;
for (;;)
while (1)
{
// Wait for the task notification
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if (epdContent[epdIndex].compareTo(currentEpdContent[epdIndex]) != 0)
// Wait for a work item to be available in the queue
if (xQueueReceive(updateQueue, &receivedItem, portMAX_DELAY))
{
displays[epdIndex].init(0, false); // Little longer reset duration because of MCP
uint count = 0;
while (EPD_BUSY[epdIndex].digitalRead() == HIGH || count < 10)
uint epdIndex = receivedItem.dispNum;
if (epdContent[epdIndex].compareTo(currentEpdContent[epdIndex]) != 0)
{
vTaskDelay(pdMS_TO_TICKS(100));
if (count >= 9)
{
displays[epdIndex].init(0, false);
}
count++;
displays[epdIndex].init(0, false); // Little longer reset duration because of MCP
}
bool updatePartial = true;
// Full Refresh every half hour
if (!lastFullRefresh[epdIndex] || (millis() - lastFullRefresh[epdIndex]) > (preferences.getUInt("fullRefreshMin", 30) * 60 * 1000))
{
updatePartial = false;
lastFullRefresh[epdIndex] = millis();
}
// if (updatePartial)
// // Full Refresh every half hour
// if (!lastFullRefresh[epdIndex] || (millis() - lastFullRefresh[epdIndex]) > (preferences.getUInt("fullRefreshMin", 30) * 60 * 1000))
// {
// displays[epdIndex].setPartialWindow(0, 0, displays[i].width(), display[i].height());
// updatePartial = false;
// lastFullRefresh[epdIndex] = millis();
// }
if (strstr(epdContent[epdIndex].c_str(), "/") != NULL)
{
String top = epdContent[epdIndex].substring(0, epdContent[epdIndex].indexOf("/"));
String bottom = epdContent[epdIndex].substring(epdContent[epdIndex].indexOf("/") + 1);
#ifdef PAGED_WRITE
splitTextPaged(epdIndex, top, bottom, updatePartial);
#else
splitText(epdIndex, top, bottom, updatePartial);
#endif
}
else if (epdContent[epdIndex].startsWith(F("qr")))
{
@ -199,20 +164,101 @@ extern "C" void updateDisplay(void *pvParameters) noexcept
else
{
#ifdef PAGED_WRITE
showDigitPaged(epdIndex, epdContent[epdIndex].c_str()[0], updatePartial, &FONT_BIG);
#else
if (epdContent[epdIndex].length() > 1) {
showChars(epdIndex, epdContent[epdIndex], updatePartial, &Antonio_SemiBold30pt7b);
} else {
showDigit(epdIndex, epdContent[epdIndex].c_str()[0], updatePartial, &FONT_BIG);
if (epdContent[epdIndex].length() > 1)
{
showChars(epdIndex, epdContent[epdIndex], updatePartial, &Antonio_SemiBold30pt7b);
}
else
{
showDigit(epdIndex, epdContent[epdIndex].c_str()[0], updatePartial, &FONT_BIG);
}
#endif
}
#ifdef PAGED_WRITE
currentEpdContent[epdIndex] = epdContent[epdIndex];
#else
if (xSemaphoreTake(epdUpdateSemaphore[epdIndex], pdMS_TO_TICKS(5000)) == pdTRUE)
{
xTaskNotifyGive(tasks[epdIndex]);
}
}
}
}
extern "C" void updateDisplay(void *pvParameters) noexcept
{
const int epdIndex = *(int *)pvParameters;
delete (int *)pvParameters;
for (;;)
{
// Wait for the task notification
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
// if (epdContent[epdIndex].compareTo(currentEpdContent[epdIndex]) != 0)
// {
// displays[epdIndex].init(0, false); // Little longer reset duration because of MCP
uint count = 0;
while (EPD_BUSY[epdIndex].digitalRead() == HIGH || count < 10)
{
vTaskDelay(pdMS_TO_TICKS(100));
// if (count >= 9)
// {
// displays[epdIndex].init(0, false);
// }
count++;
}
bool updatePartial = true;
// Full Refresh every half hour
if (!lastFullRefresh[epdIndex] || (millis() - lastFullRefresh[epdIndex]) > (preferences.getUInt("fullRefreshMin", 30) * 60 * 1000))
{
updatePartial = false;
lastFullRefresh[epdIndex] = millis();
}
// // if (updatePartial)
// // {
// // displays[epdIndex].setPartialWindow(0, 0, displays[i].width(), display[i].height());
// // }
// if (strstr(epdContent[epdIndex].c_str(), "/") != NULL)
// {
// String top = epdContent[epdIndex].substring(0, epdContent[epdIndex].indexOf("/"));
// String bottom = epdContent[epdIndex].substring(epdContent[epdIndex].indexOf("/") + 1);
// #ifdef PAGED_WRITE
// splitTextPaged(epdIndex, top, bottom, updatePartial);
// #else
// splitText(epdIndex, top, bottom, updatePartial);
// #endif
// }
// else if (epdContent[epdIndex].startsWith(F("qr")))
// {
// renderQr(epdIndex, epdContent[epdIndex], updatePartial);
// }
// else if (epdContent[epdIndex].length() > 5)
// {
// renderText(epdIndex, epdContent[epdIndex], updatePartial);
// }
// else
// {
// #ifdef PAGED_WRITE
// showDigitPaged(epdIndex, epdContent[epdIndex].c_str()[0], updatePartial, &FONT_BIG);
// #else
// if (epdContent[epdIndex].length() > 1)
// {
// showChars(epdIndex, epdContent[epdIndex], updatePartial, &Antonio_SemiBold30pt7b);
// }
// else
// {
// showDigit(epdIndex, epdContent[epdIndex].c_str()[0], updatePartial, &FONT_BIG);
// }
// #endif
// }
// #ifdef PAGED_WRITE
// currentEpdContent[epdIndex] = epdContent[epdIndex];
// #else
char tries = 0;
while (tries < 3)
{
@ -227,8 +273,8 @@ extern "C" void updateDisplay(void *pvParameters) noexcept
}
currentEpdContent[epdIndex] = epdContent[epdIndex];
#endif
}
// #endif
// }
xSemaphoreGive(epdUpdateSemaphore[epdIndex]);
}
}
@ -273,54 +319,54 @@ void splitText(const uint dispNum, const String &top, const String &bottom, bool
displays[dispNum].print(bottom);
}
void splitTextPaged(const uint dispNum, String top, String bottom, bool partial)
{
displays[dispNum].setRotation(2);
displays[dispNum].setFont(&FONT_SMALL);
displays[dispNum].setTextColor(getFgColor());
// void splitTextPaged(const uint dispNum, String top, String bottom, bool partial)
// {
// displays[dispNum].setRotation(2);
// displays[dispNum].setFont(&FONT_SMALL);
// displays[dispNum].setTextColor(getFgColor());
// Top text
int16_t ttbx, ttby;
uint16_t ttbw, ttbh;
displays[dispNum].getTextBounds(top, 0, 0, &ttbx, &ttby, &ttbw, &ttbh);
uint16_t tx = ((displays[dispNum].width() - ttbw) / 2) - ttbx;
uint16_t ty = ((displays[dispNum].height() - ttbh) / 2) - ttby - ttbh / 2 - 12;
// // Top text
// int16_t ttbx, ttby;
// uint16_t ttbw, ttbh;
// displays[dispNum].getTextBounds(top, 0, 0, &ttbx, &ttby, &ttbw, &ttbh);
// uint16_t tx = ((displays[dispNum].width() - ttbw) / 2) - ttbx;
// uint16_t ty = ((displays[dispNum].height() - ttbh) / 2) - ttby - ttbh / 2 - 12;
// Bottom text
int16_t tbbx, tbby;
uint16_t tbbw, tbbh;
displays[dispNum].getTextBounds(bottom, 0, 0, &tbbx, &tbby, &tbbw, &tbbh);
uint16_t bx = ((displays[dispNum].width() - tbbw) / 2) - tbbx;
uint16_t by = ((displays[dispNum].height() - tbbh) / 2) - tbby + tbbh / 2 + 12;
// // Bottom text
// int16_t tbbx, tbby;
// uint16_t tbbw, tbbh;
// displays[dispNum].getTextBounds(bottom, 0, 0, &tbbx, &tbby, &tbbw, &tbbh);
// uint16_t bx = ((displays[dispNum].width() - tbbw) / 2) - tbbx;
// uint16_t by = ((displays[dispNum].height() - tbbh) / 2) - tbby + tbbh / 2 + 12;
// Make separator as wide as the shortest text.
uint16_t lineWidth, lineX;
if (tbbw < ttbh)
lineWidth = tbbw;
else
lineWidth = ttbw;
lineX = round((displays[dispNum].width() - lineWidth) / 2);
// // Make separator as wide as the shortest text.
// uint16_t lineWidth, lineX;
// if (tbbw < ttbh)
// lineWidth = tbbw;
// else
// lineWidth = ttbw;
// lineX = round((displays[dispNum].width() - lineWidth) / 2);
if (partial)
{
displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(), displays[dispNum].height());
}
else
{
displays[dispNum].setFullWindow();
}
displays[dispNum].firstPage();
// if (partial)
// {
// displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(), displays[dispNum].height());
// }
// else
// {
// displays[dispNum].setFullWindow();
// }
// displays[dispNum].firstPage();
do
{
displays[dispNum].fillScreen(getBgColor());
displays[dispNum].setCursor(tx, ty);
displays[dispNum].print(top);
displays[dispNum].fillRoundRect(lineX, displays[dispNum].height() / 2 - 3, lineWidth, 6, 3, getFgColor());
displays[dispNum].setCursor(bx, by);
displays[dispNum].print(bottom);
} while (displays[dispNum].nextPage());
}
// do
// {
// displays[dispNum].fillScreen(getBgColor());
// displays[dispNum].setCursor(tx, ty);
// displays[dispNum].print(top);
// displays[dispNum].fillRoundRect(lineX, displays[dispNum].height() / 2 - 3, lineWidth, 6, 3, getFgColor());
// displays[dispNum].setCursor(bx, by);
// displays[dispNum].print(bottom);
// } while (displays[dispNum].nextPage());
// }
void showDigit(const uint dispNum, char chr, bool partial, const GFXfont *font)
{
@ -339,7 +385,8 @@ void showDigit(const uint dispNum, char chr, bool partial, const GFXfont *font)
displays[dispNum].print(str);
}
void showChars(const uint dispNum, const String& chars, bool partial, const GFXfont *font) {
void showChars(const uint dispNum, const String &chars, bool partial, const GFXfont *font)
{
displays[dispNum].setRotation(2);
displays[dispNum].setFont(font);
displays[dispNum].setTextColor(getFgColor());
@ -354,35 +401,35 @@ void showChars(const uint dispNum, const String& chars, bool partial, const GFXf
displays[dispNum].print(chars);
}
void showDigitPaged(const uint dispNum, char chr, bool partial, const GFXfont *font)
{
String str(chr);
displays[dispNum].setRotation(2);
displays[dispNum].setFont(font);
displays[dispNum].setTextColor(getFgColor());
int16_t tbx, tby;
uint16_t tbw, tbh;
displays[dispNum].getTextBounds(str, 0, 0, &tbx, &tby, &tbw, &tbh);
// center the bounding box by transposition of the origin:
uint16_t x = ((displays[dispNum].width() - tbw) / 2) - tbx;
uint16_t y = ((displays[dispNum].height() - tbh) / 2) - tby;
if (partial)
{
displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(), displays[dispNum].height());
}
else
{
displays[dispNum].setFullWindow();
}
displays[dispNum].firstPage();
// void showDigitPaged(const uint dispNum, char chr, bool partial, const GFXfont *font)
// {
// String str(chr);
// displays[dispNum].setRotation(2);
// displays[dispNum].setFont(font);
// displays[dispNum].setTextColor(getFgColor());
// int16_t tbx, tby;
// uint16_t tbw, tbh;
// displays[dispNum].getTextBounds(str, 0, 0, &tbx, &tby, &tbw, &tbh);
// // center the bounding box by transposition of the origin:
// uint16_t x = ((displays[dispNum].width() - tbw) / 2) - tbx;
// uint16_t y = ((displays[dispNum].height() - tbh) / 2) - tby;
// if (partial)
// {
// displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(), displays[dispNum].height());
// }
// else
// {
// displays[dispNum].setFullWindow();
// }
// displays[dispNum].firstPage();
do
{
displays[dispNum].fillScreen(getBgColor());
displays[dispNum].setCursor(x, y);
displays[dispNum].print(str);
} while (displays[dispNum].nextPage());
}
// do
// {
// displays[dispNum].fillScreen(getBgColor());
// displays[dispNum].setCursor(x, y);
// displays[dispNum].print(str);
// } while (displays[dispNum].nextPage());
// }
int getBgColor()
{
@ -406,11 +453,6 @@ void setFgColor(int color)
std::array<String, NUM_SCREENS> getCurrentEpdContent()
{
// Serial.println("currentEpdContent");
// for (int i = 0; i < NUM_SCREENS; i++) {
// Serial.printf("%d = %s", i, currentEpdContent[i]);
// }
return currentEpdContent;
}
void renderText(const uint dispNum, const String &text, bool partial)
@ -445,12 +487,12 @@ void renderText(const uint dispNum, const String &text, bool partial)
}
}
//displays[dispNum].display(partial);
// displays[dispNum].display(partial);
}
void renderQr(const uint dispNum, const String &text, bool partial)
{
#ifdef USE_QR
#ifdef USE_QR
uint8_t tempBuffer[800];
bool ok = qrcodegen_encodeText(text.substring(2).c_str(), tempBuffer, qrcode, qrcodegen_Ecc_LOW,
@ -473,16 +515,19 @@ void renderQr(const uint dispNum, const String &text, bool partial)
displays[dispNum].drawPixel(padding + x, paddingY + y, qrcodegen_getModule(qrcode, floor(float(x) / 4), floor(float(y) / 4)) ? GxEPD_BLACK : GxEPD_WHITE);
}
}
//displays[dispNum].display(partial);
// displays[dispNum].display(partial);
//free(tempBuffer);
//free(qrcode);
#endif
// free(tempBuffer);
// free(qrcode);
#endif
}
void waitUntilNoneBusy() {
for (int i = 0; i < NUM_SCREENS; i++) {
while (EPD_BUSY[i].digitalRead()) {
void waitUntilNoneBusy()
{
for (int i = 0; i < NUM_SCREENS; i++)
{
while (EPD_BUSY[i].digitalRead())
{
vTaskDelay(10);
}
}

View file

@ -13,21 +13,27 @@
#ifdef USE_QR
#include "qrcodegen.h"
#endif
extern TaskHandle_t epdTaskHandle;
// extern TaskHandle_t epdTaskHandle;
typedef struct
{
char dispNum;
} UpdateDisplayTaskItem;
void setupDisplays();
void taskEpd(void *pvParameters);
// void taskEpd(void *pvParameters);
void splitText(const uint dispNum, const String& top, const String& bottom, bool partial);
void splitTextPaged(const uint dispNum, String top, String bottom, bool partial);
//void splitTextPaged(const uint dispNum, String top, String bottom, bool partial);
void showDigit(const uint dispNum, char chr, bool partial, const GFXfont *font);
void showChars(const uint dispNum, const String& chars, bool partial, const GFXfont *font);
void showDigitPaged(const uint dispNum, char chr, bool partial, const GFXfont *font);
//void showDigitPaged(const uint dispNum, char chr, bool partial, const GFXfont *font);
extern "C" void updateDisplay(void *pvParameters) noexcept;
void updateDisplayAlt(int epdIndex);
void prepareDisplayUpdateTask(void *pvParameters);
int getBgColor();
int getFgColor();

View file

@ -29,9 +29,9 @@ void onOTAStart()
esp_timer_stop(minuteTimer);
// Stop or suspend all tasks
vTaskSuspend(priceUpdateTaskHandle);
vTaskSuspend(blockUpdateTaskHandle);
vTaskSuspend(timeUpdateTaskHandle);
// vTaskSuspend(priceUpdateTaskHandle);
// vTaskSuspend(blockUpdateTaskHandle);
vTaskSuspend(workerTaskHandle);
vTaskSuspend(taskScreenRotateTaskHandle);
vTaskSuspend(ledTaskHandle);
@ -48,4 +48,7 @@ void handleOTATask(void *parameter) {
ArduinoOTA.handle(); // Allow OTA updates to occur
vTaskDelay(pdMS_TO_TICKS(2500));
}
}
void downloadUpdate() {
}

View file

@ -5,4 +5,5 @@
void setupOTA();
void onOTAStart();
void handleOTATask(void *parameter);
void handleOTATask(void *parameter);
void downloadUpdate();

View file

@ -1,16 +1,52 @@
#include "price_notify.hpp"
const char *wsServerPrice = "wss://ws.coincap.io/prices?assets=bitcoin";
// const char* coinCapWsCert = R"(-----BEGIN CERTIFICATE-----
// MIIFMjCCBNmgAwIBAgIQBtgXvFyc28MsvQ1HjCnXJTAKBggqhkjOPQQDAjBKMQsw
// CQYDVQQGEwJVUzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEgMB4GA1UEAxMX
// Q2xvdWRmbGFyZSBJbmMgRUNDIENBLTMwHhcNMjMwNTEwMDAwMDAwWhcNMjQwNTA5
// MjM1OTU5WjB1MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQG
// A1UEBxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEe
// MBwGA1UEAxMVc25pLmNsb3VkZmxhcmVzc2wuY29tMFkwEwYHKoZIzj0CAQYIKoZI
// zj0DAQcDQgAEpvFIXzQKHuqTo+IE6c6sB4p0PMXK1KsseEGf2UN/CNRhG5hO7lr8
// JtXrPZkawWBysZxOsEoetkPrDHMugCLfXKOCA3QwggNwMB8GA1UdIwQYMBaAFKXO
// N+rrsHUOlGeItEX62SQQh5YfMB0GA1UdDgQWBBShsZDJohaR1a5E0Qj7yblZjKDC
// gDA6BgNVHREEMzAxggwqLmNvaW5jYXAuaW+CCmNvaW5jYXAuaW+CFXNuaS5jbG91
// ZGZsYXJlc3NsLmNvbTAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUH
// AwEGCCsGAQUFBwMCMHsGA1UdHwR0MHIwN6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2lj
// ZXJ0LmNvbS9DbG91ZGZsYXJlSW5jRUNDQ0EtMy5jcmwwN6A1oDOGMWh0dHA6Ly9j
// cmw0LmRpZ2ljZXJ0LmNvbS9DbG91ZGZsYXJlSW5jRUNDQ0EtMy5jcmwwPgYDVR0g
// BDcwNTAzBgZngQwBAgIwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2Vy
// dC5jb20vQ1BTMHYGCCsGAQUFBwEBBGowaDAkBggrBgEFBQcwAYYYaHR0cDovL29j
// c3AuZGlnaWNlcnQuY29tMEAGCCsGAQUFBzAChjRodHRwOi8vY2FjZXJ0cy5kaWdp
// Y2VydC5jb20vQ2xvdWRmbGFyZUluY0VDQ0NBLTMuY3J0MAwGA1UdEwEB/wQCMAAw
// ggF+BgorBgEEAdZ5AgQCBIIBbgSCAWoBaAB1AO7N0GTV2xrOxVy3nbTNE6Iyh0Z8
// vOzew1FIWUZxH7WbAAABiAPnoRAAAAQDAEYwRAIgAP2W09OozuhmKeKKMsaVBcae
// o+nPHF1WUWk0i387YYYCIDIM1Wll7/4O3GNx2/Fx9bC6pi69Uya4pLxsCfW3fZMe
// AHYASLDja9qmRzQP5WoC+p0w6xxSActW3SyB2bu/qznYhHMAAAGIA+eg+QAABAMA
// RzBFAiEAuNpSqrbx47gYBgBMz5M6q0CnV/WMJqWQOxYFKrwfwVACIH3nCs4bKToT
// e+MiBrqSDaekixk4kPFEQESO9qHCkWY5AHcA2ra/az+1tiKfm8K7XGvocJFxbLtR
// hIU0vaQ9MEjX+6sAAAGIA+eg1gAABAMASDBGAiEAolCFl2IfbOHUPAOxoi4BLclS
// v9FVXb7LwIvTuCfyrEQCIQDcvehwhV9XGopKGl17F2LYYKI7hvlO3RmpPZQJt1da
// MDAKBggqhkjOPQQDAgNHADBEAiAXRWZ/JVMsfpSFFTHQHUSqRnQ/7cCOWx+9svIy
// mYnFZQIgHMEG0Cm7O4cn5KUzKOsTwwK+2U15s/jPUQi2n2IDTEM=
// -----END CERTIFICATE-----)";
// WebsocketsClient client;
esp_websocket_client_handle_t clientPrice = NULL;
unsigned long int currentPrice = 30000;
uint currentPrice = 30000;
unsigned long int lastPriceUpdate;
void setupPriceNotify()
{
// currentPrice = preferences.get("lastPrice", 30000);
esp_websocket_client_config_t config = {
.uri = wsServerPrice,
.user_agent = USER_AGENT,
// .task_stack = (7*1024),
// .cert_pem = coinCapWsCert,
.user_agent = USER_AGENT
};
clientPrice = esp_websocket_client_init(&config);
@ -53,22 +89,28 @@ void onWebsocketPriceMessage(esp_websocket_event_data_t* event_data)
if (lastPriceUpdate == 0 || (currentTime - lastPriceUpdate) > minSecPriceUpd) {
// const unsigned long oldPrice = currentPrice;
currentPrice = doc["bitcoin"].as<long>();
currentPrice = doc["bitcoin"].as<uint>();
preferences.putUInt("lastPrice", currentPrice);
lastPriceUpdate = currentTime;
// if (abs((int)(oldPrice-currentPrice)) > round(0.0015*oldPrice)) {
if (priceUpdateTaskHandle != nullptr && (getCurrentScreen() == SCREEN_BTC_TICKER || getCurrentScreen() == SCREEN_MSCW_TIME || getCurrentScreen() == SCREEN_MARKET_CAP))
xTaskNotifyGive(priceUpdateTaskHandle);
if (workQueue != nullptr && (getCurrentScreen() == SCREEN_BTC_TICKER || getCurrentScreen() == SCREEN_MSCW_TIME || getCurrentScreen() == SCREEN_MARKET_CAP)) {
WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0};
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
}
//}
}
}
}
}
unsigned long getPrice() {
uint getPrice() {
return currentPrice;
}
void setPrice(uint newPrice) {
currentPrice = newPrice;
}
bool isPriceNotifyConnected() {
if (clientPrice == NULL)
return false;

View file

@ -14,6 +14,8 @@ void setupPriceNotify();
void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data);
void onWebsocketPriceMessage(esp_websocket_event_data_t* event_data);
unsigned long getPrice();
uint getPrice();
void setPrice(uint newPrice);
bool isPriceNotifyConnected();
void stopPriceNotify();

View file

@ -1,9 +1,10 @@
#include "screen_handler.hpp"
TaskHandle_t priceUpdateTaskHandle;
TaskHandle_t blockUpdateTaskHandle;
TaskHandle_t timeUpdateTaskHandle;
// TaskHandle_t priceUpdateTaskHandle;
// TaskHandle_t blockUpdateTaskHandle;
// TaskHandle_t timeUpdateTaskHandle;
TaskHandle_t taskScreenRotateTaskHandle;
TaskHandle_t workerTaskHandle;
esp_timer_handle_t screenRotateTimer;
esp_timer_handle_t minuteTimer;
@ -11,89 +12,196 @@ std::array<String, NUM_SCREENS> taskEpdContent = {"", "", "", "", "", "", ""};
std::string priceString;
const int usPerSecond = 1000000;
const int usPerMinute = 60 * usPerSecond;
int64_t next_callback_time = 0;
// typedef enum
// {
// TASK_PRICE_UPDATE,
// TASK_BLOCK_UPDATE,
// TASK_TIME_UPDATE
// } TaskType;
// typedef struct
// {
// TaskType type;
// unsigned long data;
// } WorkItem;
#define WORK_QUEUE_SIZE 10
QueueHandle_t workQueue;
uint currentScreen;
void taskPriceUpdate(void *pvParameters)
void workerTask(void *pvParameters)
{
for (;;)
WorkItem receivedItem;
while (1)
{
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
unsigned long price = getPrice();
uint firstIndex = 0;
if (getCurrentScreen() == SCREEN_BTC_TICKER)
// Wait for a work item to be available in the queue
if (xQueueReceive(workQueue, &receivedItem, portMAX_DELAY))
{
priceString = ("$" + String(price)).c_str();
uint firstIndex = 0;
if (priceString.length() < (NUM_SCREENS))
// Process the work item based on its type
switch (receivedItem.type)
{
priceString.insert(priceString.begin(), NUM_SCREENS - priceString.length(), ' ');
taskEpdContent[0] = "BTC/USD";
firstIndex = 1;
}
}
else if (getCurrentScreen() == SCREEN_MSCW_TIME)
{
priceString = String(int(round(1 / float(price) * 10e7))).c_str();
if (priceString.length() < (NUM_SCREENS))
case TASK_PRICE_UPDATE:
{
priceString.insert(priceString.begin(), NUM_SCREENS - priceString.length(), ' ');
taskEpdContent[0] = "MSCW/TIME";
firstIndex = 1;
}
}
else
{
double supply = getSupplyAtBlock(getBlockHeight());
int64_t marketCap = static_cast<std::int64_t>(supply * double(price));
taskEpdContent[0] = "USD/MCAP";
if (preferences.getBool("mcapBigChar", true)) {
firstIndex = 1;
priceString = "$" + formatNumberWithSuffix(marketCap);
priceString.insert(priceString.begin(), NUM_SCREENS - priceString.length(), ' ');
} else {
std::string stringValue = std::to_string(marketCap);
size_t mcLength = stringValue.length();
size_t leadingSpaces = (3 - mcLength % 3) % 3;
stringValue = std::string(leadingSpaces, ' ') + stringValue;
uint groups = (mcLength + leadingSpaces) / 3;
if (groups < NUM_SCREENS) {
firstIndex = 1;
}
for (int i = firstIndex; i < NUM_SCREENS-groups-1; i++) {
taskEpdContent[i] = "";
}
taskEpdContent[NUM_SCREENS-groups-1] = " $ ";
for (uint i = 0; i < groups; i ++)
firstIndex = 0;
uint price = getPrice();
if (getCurrentScreen() == SCREEN_BTC_TICKER)
{
taskEpdContent[(NUM_SCREENS-groups+i)] = stringValue.substr(i*3, 3).c_str();
priceString = ("$" + String(price)).c_str();
if (priceString.length() < (NUM_SCREENS))
{
priceString.insert(priceString.begin(), NUM_SCREENS - priceString.length(), ' ');
taskEpdContent[0] = "BTC/USD";
firstIndex = 1;
}
}
}
}
else if (getCurrentScreen() == SCREEN_MSCW_TIME)
{
priceString = String(int(round(1 / float(price) * 10e7))).c_str();
if (!(getCurrentScreen() == SCREEN_MARKET_CAP && !preferences.getBool("mcapBigChar", true))) {
for (uint i = firstIndex; i < NUM_SCREENS; i++)
if (priceString.length() < (NUM_SCREENS))
{
priceString.insert(priceString.begin(), NUM_SCREENS - priceString.length(), ' ');
taskEpdContent[0] = "MSCW/TIME";
firstIndex = 1;
}
}
else
{
double supply = getSupplyAtBlock(getBlockHeight());
int64_t marketCap = static_cast<std::int64_t>(supply * double(price));
taskEpdContent[0] = "USD/MCAP";
if (preferences.getBool("mcapBigChar", true))
{
firstIndex = 1;
priceString = "$" + formatNumberWithSuffix(marketCap);
priceString.insert(priceString.begin(), NUM_SCREENS - priceString.length(), ' ');
}
else
{
std::string stringValue = std::to_string(marketCap);
size_t mcLength = stringValue.length();
size_t leadingSpaces = (3 - mcLength % 3) % 3;
stringValue = std::string(leadingSpaces, ' ') + stringValue;
uint groups = (mcLength + leadingSpaces) / 3;
if (groups < NUM_SCREENS)
{
firstIndex = 1;
}
for (int i = firstIndex; i < NUM_SCREENS - groups - 1; i++)
{
taskEpdContent[i] = "";
}
taskEpdContent[NUM_SCREENS - groups - 1] = " $ ";
for (uint i = 0; i < groups; i++)
{
taskEpdContent[(NUM_SCREENS - groups + i)] = stringValue.substr(i * 3, 3).c_str();
}
}
}
if (!(getCurrentScreen() == SCREEN_MARKET_CAP && !preferences.getBool("mcapBigChar", true)))
{
for (uint i = firstIndex; i < NUM_SCREENS; i++)
{
taskEpdContent[i] = priceString[i];
}
}
setEpdContent(taskEpdContent);
break;
}
case TASK_BLOCK_UPDATE:
{
taskEpdContent[i] = priceString[i];
std::string blockNrString = String(getBlockHeight()).c_str();
firstIndex = 0;
if (getCurrentScreen() != SCREEN_HALVING_COUNTDOWN)
{
if (blockNrString.length() < NUM_SCREENS)
{
blockNrString.insert(blockNrString.begin(), NUM_SCREENS - blockNrString.length(), ' ');
taskEpdContent[0] = "BLOCK/HEIGHT";
firstIndex = 1;
}
for (uint i = firstIndex; i < NUM_SCREENS; i++)
{
taskEpdContent[i] = blockNrString[i];
}
}
else
{
const uint nextHalvingBlock = 210000 - (getBlockHeight() % 210000);
const uint minutesToHalving = nextHalvingBlock * 10;
const int years = floor(minutesToHalving / 525600);
const int days = floor((minutesToHalving - (years * 525600)) / (24 * 60));
const int hours = floor((minutesToHalving - (years * 525600) - (days * (24 * 60))) / 60);
const int mins = floor(minutesToHalving - (years * 525600) - (days * (24 * 60)) - (hours * 60));
taskEpdContent[0] = "BIT/COIN";
taskEpdContent[1] = "HALV/ING";
taskEpdContent[(NUM_SCREENS - 5)] = String(years) + "/YRS";
taskEpdContent[(NUM_SCREENS - 4)] = String(days) + "/DAYS";
taskEpdContent[(NUM_SCREENS - 3)] = String(days) + "/HRS";
taskEpdContent[(NUM_SCREENS - 2)] = String(mins) + "/MINS";
taskEpdContent[(NUM_SCREENS - 1)] = "TO/GO";
}
if (getCurrentScreen() == SCREEN_HALVING_COUNTDOWN || getCurrentScreen() == SCREEN_BLOCK_HEIGHT)
{
setEpdContent(taskEpdContent);
}
break;
}
case TASK_TIME_UPDATE:
{
if (getCurrentScreen() == SCREEN_TIME)
{
time_t currentTime;
struct tm timeinfo;
time(&currentTime);
localtime_r(&currentTime, &timeinfo);
std::string timeString;
String minute = String(timeinfo.tm_min);
if (minute.length() < 2)
{
minute = "0" + minute;
}
timeString = std::to_string(timeinfo.tm_hour) + ":" + minute.c_str();
timeString.insert(timeString.begin(), NUM_SCREENS - timeString.length(), ' ');
taskEpdContent[0] = String(timeinfo.tm_mday) + "/" + String(timeinfo.tm_mon + 1);
for (uint i = 1; i < NUM_SCREENS; i++)
{
taskEpdContent[i] = timeString[i];
}
setEpdContent(taskEpdContent);
}
break;
}
// Add more cases for additional task types
}
}
setEpdContent(taskEpdContent);
}
}
void taskScreenRotate(void *pvParameters)
{
for (;;)
@ -113,97 +221,16 @@ void taskScreenRotate(void *pvParameters)
}
}
void taskBlockUpdate(void *pvParameters)
{
for (;;)
{
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
std::string blockNrString = String(getBlockHeight()).c_str();
uint firstIndex = 0;
if (getCurrentScreen() != SCREEN_HALVING_COUNTDOWN)
{
if (blockNrString.length() < NUM_SCREENS)
{
blockNrString.insert(blockNrString.begin(), NUM_SCREENS - blockNrString.length(), ' ');
taskEpdContent[0] = "BLOCK/HEIGHT";
firstIndex = 1;
}
for (uint i = firstIndex; i < NUM_SCREENS; i++)
{
taskEpdContent[i] = blockNrString[i];
}
}
else
{
const uint nextHalvingBlock = 210000 - (getBlockHeight() % 210000);
const uint minutesToHalving = nextHalvingBlock * 10;
const int years = floor(minutesToHalving / 525600);
const int days = floor((minutesToHalving - (years * 525600)) / (24 * 60));
const int hours = floor((minutesToHalving - (years * 525600) - (days * (24 * 60))) / 60);
const int mins = floor(minutesToHalving - (years * 525600) - (days * (24 * 60)) - (hours * 60));
taskEpdContent[0] = "BIT/COIN";
taskEpdContent[1] = "HALV/ING";
taskEpdContent[(NUM_SCREENS - 5)] = String(years) + "/YRS";
taskEpdContent[(NUM_SCREENS - 4)] = String(days) + "/DAYS";
taskEpdContent[(NUM_SCREENS - 3)] = String(days) + "/HRS";
taskEpdContent[(NUM_SCREENS - 2)] = String(mins) + "/MINS";
taskEpdContent[(NUM_SCREENS - 1)] = "TO/GO";
}
if (getCurrentScreen() == SCREEN_HALVING_COUNTDOWN || getCurrentScreen() == SCREEN_BLOCK_HEIGHT)
{
setEpdContent(taskEpdContent);
}
}
}
void taskTimeUpdate(void *pvParameters)
{
for (;;)
{
if (getCurrentScreen() == SCREEN_TIME)
{
time_t currentTime;
struct tm timeinfo;
time(&currentTime);
localtime_r(&currentTime, &timeinfo);
std::string timeString;
String minute = String(timeinfo.tm_min);
if (minute.length() < 2)
{
minute = "0" + minute;
}
timeString = std::to_string(timeinfo.tm_hour) + ":" + minute.c_str();
timeString.insert(timeString.begin(), NUM_SCREENS - timeString.length(), ' ');
taskEpdContent[0] = String(timeinfo.tm_mday) + "/" + String(timeinfo.tm_mon + 1);
for (uint i = 1; i < NUM_SCREENS; i++)
{
taskEpdContent[i] = timeString[i];
}
setEpdContent(taskEpdContent);
}
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
}
}
void IRAM_ATTR minuteTimerISR(void *arg)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(timeUpdateTaskHandle, &xHigherPriorityTaskWoken);
// vTaskNotifyGiveFromISR(timeUpdateTaskHandle, &xHigherPriorityTaskWoken);
WorkItem timeUpdate = {TASK_TIME_UPDATE, 0};
xQueueSendFromISR(workQueue, &timeUpdate, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken == pdTRUE)
{
portYIELD_FROM_ISR();
}
int64_t current_time = esp_timer_get_time();
next_callback_time = current_time + usPerMinute;
}
void IRAM_ATTR screenRotateTimerISR(void *arg)
@ -218,9 +245,13 @@ void IRAM_ATTR screenRotateTimerISR(void *arg)
void setupTasks()
{
xTaskCreate(taskPriceUpdate, "updatePrice", 3072, NULL, tskIDLE_PRIORITY, &priceUpdateTaskHandle);
xTaskCreate(taskBlockUpdate, "updateBlock", 2048, NULL, tskIDLE_PRIORITY, &blockUpdateTaskHandle);
xTaskCreate(taskTimeUpdate, "updateTime", 4096, NULL, tskIDLE_PRIORITY, &timeUpdateTaskHandle);
workQueue = xQueueCreate(WORK_QUEUE_SIZE, sizeof(WorkItem));
// xTaskCreate(taskPriceUpdate, "updatePrice", 1024, NULL, tskIDLE_PRIORITY, &priceUpdateTaskHandle);
// xTaskCreate(taskBlockUpdate, "updateBlock", 1024, NULL, tskIDLE_PRIORITY, &blockUpdateTaskHandle);
// xTaskCreate(taskTimeUpdate, "updateTime", 1024, NULL, tskIDLE_PRIORITY, &timeUpdateTaskHandle);
xTaskCreate(workerTask, "workerTask", 4096, NULL, tskIDLE_PRIORITY, &workerTaskHandle);
xTaskCreate(taskScreenRotate, "rotateScreen", 2048, NULL, tskIDLE_PRIORITY, &taskScreenRotateTaskHandle);
setCurrentScreen(preferences.getUInt("currentScreen", 0));
@ -244,7 +275,10 @@ void setupTimeUpdateTimer(void *pvParameters)
vTaskDelay(pdMS_TO_TICKS((secondsUntilNextMinute * 1000)));
esp_timer_start_periodic(minuteTimer, usPerMinute);
xTaskNotifyGive(timeUpdateTaskHandle);
WorkItem timeUpdate = {TASK_TIME_UPDATE, 0};
xQueueSend(workQueue, &timeUpdate, portMAX_DELAY);
// xTaskNotifyGive(timeUpdateTaskHandle);
vTaskDelete(NULL);
}
@ -316,18 +350,30 @@ void setCurrentScreen(uint newScreen)
switch (currentScreen)
{
case SCREEN_TIME:
xTaskNotifyGive(timeUpdateTaskHandle);
{
WorkItem timeUpdate = {TASK_TIME_UPDATE, 0};
xQueueSend(workQueue, &timeUpdate, portMAX_DELAY);
// xTaskNotifyGive(timeUpdateTaskHandle);
break;
}
case SCREEN_HALVING_COUNTDOWN:
case SCREEN_BLOCK_HEIGHT:
xTaskNotifyGive(blockUpdateTaskHandle);
{
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
//xTaskNotifyGive(blockUpdateTaskHandle);
break;
}
case SCREEN_MARKET_CAP:
case SCREEN_MSCW_TIME:
case SCREEN_BTC_TICKER:
xTaskNotifyGive(priceUpdateTaskHandle);
{
WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0};
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
//xTaskNotifyGive(priceUpdateTaskHandle);
break;
}
}
if (eventSourceTaskHandle != NULL)
xTaskNotifyGive(eventSourceTaskHandle);

View file

@ -8,15 +8,31 @@
#include "shared.hpp"
#include "lib/epd.hpp"
extern TaskHandle_t priceUpdateTaskHandle;
extern TaskHandle_t blockUpdateTaskHandle;
extern TaskHandle_t timeUpdateTaskHandle;
// extern TaskHandle_t priceUpdateTaskHandle;
// extern TaskHandle_t blockUpdateTaskHandle;
// extern TaskHandle_t timeUpdateTaskHandle;
extern TaskHandle_t workerTaskHandle;
extern TaskHandle_t taskScreenRotateTaskHandle;
extern esp_timer_handle_t screenRotateTimer;
extern esp_timer_handle_t minuteTimer;
extern QueueHandle_t workQueue;
typedef enum
{
TASK_PRICE_UPDATE,
TASK_BLOCK_UPDATE,
TASK_TIME_UPDATE
} TaskType;
typedef struct
{
TaskType type;
char data;
} WorkItem;
void workerTask(void *pvParameters);
uint getCurrentScreen();
void setCurrentScreen(uint newScreen);
void nextScreen();
@ -30,9 +46,9 @@ void setupScreenRotateTimer(void *pvParameters);
void IRAM_ATTR minuteTimerISR(void* arg);
void IRAM_ATTR screenRotateTimerISR(void* arg);
void taskPriceUpdate(void *pvParameters);
void taskBlockUpdate(void *pvParameters);
void taskTimeUpdate(void *pvParameters);
// void taskPriceUpdate(void *pvParameters);
// void taskBlockUpdate(void *pvParameters);
// void taskTimeUpdate(void *pvParameters);
void taskScreenRotate(void *pvParameters);
uint getTimerSeconds();

View file

@ -13,14 +13,7 @@ void setupWebserver()
}
events.onConnect([](AsyncEventSourceClient *client)
{
if (client->lastId())
{
Serial.printf("Client reconnected! Last message ID that it gat is: %u\n", client->lastId());
}
// send event with message "hello!", id current millis
// and set reconnect delay to 1 second
client->send("welcome",NULL,millis(),1000); });
{ client->send("welcome", NULL, millis(), 1000); });
server.addHandler(&events);
server.serveStatic("/css", LittleFS, "/css/");
@ -70,7 +63,8 @@ void setupWebserver()
xTaskCreate(eventSourceTask, "eventSourceTask", 4096, NULL, tskIDLE_PRIORITY, &eventSourceTaskHandle);
}
void stopWebServer() {
void stopWebServer()
{
server.end();
}
@ -145,8 +139,6 @@ void onApiStatus(AsyncWebServerRequest *request)
void onApiActionPause(AsyncWebServerRequest *request)
{
setTimerActive(false);
Serial.println(F("Update timer paused"));
request->send(200);
};
@ -156,10 +148,7 @@ void onApiActionPause(AsyncWebServerRequest *request)
*/
void onApiActionTimerRestart(AsyncWebServerRequest *request)
{
// moment = millis();
setTimerActive(true);
Serial.println(F("Update timer restarted"));
request->send(200);
}
@ -290,13 +279,13 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
AsyncWebParameter *ledFlashOnUpdate = request->getParam("ledFlashOnUpd", true);
preferences.putBool("ledFlashOnUpd", ledFlashOnUpdate->value().toInt());
Serial.printf("Setting led flash on update to %d\r\n", ledFlashOnUpdate->value().toInt());
// Serial.printf("Setting led flash on update to %d\r\n", ledFlashOnUpdate->value().toInt());
settingsChanged = true;
}
else
{
preferences.putBool("ledFlashOnUpd", 0);
Serial.print("Setting led flash on update to false");
// Serial.print("Setting led flash on update to false");
settingsChanged = true;
}
@ -305,13 +294,13 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
AsyncWebParameter *stealFocusOnBlock = request->getParam("stealFocusOnBlock", true);
preferences.putBool("stealFocus", stealFocusOnBlock->value().toInt());
Serial.printf("Setting steal focus on new block to %d\r\n", stealFocusOnBlock->value().toInt());
// Serial.printf("Setting steal focus on new block to %d\r\n", stealFocusOnBlock->value().toInt());
settingsChanged = true;
}
else
{
preferences.putBool("stealFocus", 0);
Serial.print("Setting steal focus on new block to false");
// Serial.print("Setting steal focus on new block to false");
settingsChanged = true;
}
@ -326,7 +315,7 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
else
{
preferences.putBool("mcapBigChar", 0);
Serial.print("Setting big characters for market cap to false");
// Serial.print("Setting big characters for market cap to false");
settingsChanged = true;
}
@ -335,7 +324,7 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
AsyncWebParameter *mempoolInstance = request->getParam("mempoolInstance", true);
preferences.putString("mempoolInstance", mempoolInstance->value().c_str());
Serial.printf("Setting mempool instance to %s\r\n", mempoolInstance->value().c_str());
// Serial.printf("Setting mempool instance to %s\r\n", mempoolInstance->value().c_str());
settingsChanged = true;
}
@ -344,7 +333,7 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
AsyncWebParameter *ledBrightness = request->getParam("ledBrightness", true);
preferences.putUInt("ledBrightness", ledBrightness->value().toInt());
Serial.printf("Setting brightness to %d\r\n", ledBrightness->value().toInt());
// Serial.printf("Setting brightness to %d\r\n", ledBrightness->value().toInt());
settingsChanged = true;
}
@ -353,7 +342,7 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
AsyncWebParameter *fullRefreshMin = request->getParam("fullRefreshMin", true);
preferences.putUInt("fullRefreshMin", fullRefreshMin->value().toInt());
Serial.printf("Set full refresh minutes to %d\r\n", fullRefreshMin->value().toInt());
// Serial.printf("Set full refresh minutes to %d\r\n", fullRefreshMin->value().toInt());
settingsChanged = true;
}
@ -362,7 +351,7 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
AsyncWebParameter *wpTimeout = request->getParam("wpTimeout", true);
preferences.putUInt("wpTimeout", wpTimeout->value().toInt());
Serial.printf("Set WiFi portal timeout seconds to %d\r\n", wpTimeout->value().toInt());
// Serial.printf("Set WiFi portal timeout seconds to %ld\r\n", wpTimeout->value().toInt());
settingsChanged = true;
}
@ -378,7 +367,7 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
AsyncWebParameter *screenParam = request->getParam(key, true);
visible = screenParam->value().toInt();
}
Serial.printf("Setting screen %d to %d\r\n", i, visible);
// Serial.printf("Setting screen %d to %d\r\n", i, visible);
preferences.putBool(prefKey.c_str(), visible);
}
@ -388,7 +377,7 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
AsyncWebParameter *p = request->getParam("tzOffset", true);
int tzOffsetSeconds = p->value().toInt() * 60;
preferences.putInt("gmtOffset", tzOffsetSeconds);
Serial.printf("Setting tz offset to %d\r\n", tzOffsetSeconds);
// Serial.printf("Setting tz offset to %d\r\n", tzOffsetSeconds);
settingsChanged = true;
}
@ -397,7 +386,7 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
AsyncWebParameter *p = request->getParam("minSecPriceUpd", true);
int minSecPriceUpd = p->value().toInt();
preferences.putUInt("minSecPriceUpd", minSecPriceUpd);
Serial.printf("Setting minSecPriceUpd to %d\r\n", minSecPriceUpd);
// Serial.printf("Setting minSecPriceUpd to %d\r\n", minSecPriceUpd);
settingsChanged = true;
}
@ -441,8 +430,6 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
if (settingsChanged)
{
queueLedEffect(LED_FLASH_SUCCESS);
Serial.println(F("Settings changed"));
}
}