diff --git a/src/lib/block_notify.cpp b/src/lib/block_notify.cpp index 904b6f3..41e571d 100644 --- a/src/lib/block_notify.cpp +++ b/src/lib/block_notify.cpp @@ -5,6 +5,7 @@ esp_websocket_client_handle_t blockNotifyClient = NULL; uint currentBlockHeight = 816000; uint blockMedianFee = 1; bool blockNotifyInit = false; +unsigned long int lastBlockUpdate; // const char *mempoolWsCert = R"(-----BEGIN CERTIFICATE----- // MIIHfTCCBmWgAwIBAgIRANFX3mhqRYDt1NFuENoSyaAwDQYJKoZIhvcNAQELBQAw @@ -50,19 +51,20 @@ bool blockNotifyInit = false; // ew== // -----END CERTIFICATE-----)"; -void setupBlockNotify() { - // currentBlockHeight = preferences.getUInt("blockHeight", 816000); - +void setupBlockNotify() +{ 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(); @@ -71,18 +73,18 @@ void setupBlockNotify() { } // Get current block height through regular API - HTTPClient *http = new HTTPClient(); - http->begin("https://" + mempoolInstance + "/api/blocks/tip/height"); - int httpCode = http->GET(); - if (httpCode > 0 && httpCode == HTTP_CODE_OK) { - String blockHeightStr = http->getString(); - currentBlockHeight = blockHeightStr.toInt(); - // xTaskNotifyGive(blockUpdateTaskHandle); - if (workQueue != nullptr) { - WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0}; - xQueueSend(workQueue, &blockUpdate, portMAX_DELAY); - } + currentBlockHeight = getBlockFetch(); + + if (currentBlockHeight != -1) + { + lastBlockUpdate = esp_timer_get_time() / 1000000; + } + + if (workQueue != nullptr) + { + WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0}; + xQueueSend(workQueue, &blockUpdate, portMAX_DELAY); } // std::strcpy(wsServer, String("wss://" + mempoolInstance + @@ -102,38 +104,41 @@ void setupBlockNotify() { } void onWebsocketEvent(void *handler_args, esp_event_base_t base, - int32_t event_id, void *event_data) { + int32_t event_id, void *event_data) +{ esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data; const String sub = "{\"action\": \"want\", \"data\":[\"blocks\", \"mempool-blocks\"]}"; - switch (event_id) { - case WEBSOCKET_EVENT_CONNECTED: - blockNotifyInit = true; + switch (event_id) + { + case WEBSOCKET_EVENT_CONNECTED: + blockNotifyInit = true; - Serial.println(F("Connected to Mempool.space WebSocket")); + 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) { - Serial.println(F("Mempool.space WS Block Subscribe Error")); - } + Serial.println(sub); + if (esp_websocket_client_send_text(blockNotifyClient, sub.c_str(), + sub.length(), portMAX_DELAY) == -1) + { + Serial.println(F("Mempool.space WS Block Subscribe Error")); + } - break; - case WEBSOCKET_EVENT_DATA: - onWebsocketMessage(data); - break; - case WEBSOCKET_EVENT_ERROR: - Serial.println(F("Mempool.space WS Connnection error")); - break; - case WEBSOCKET_EVENT_DISCONNECTED: - Serial.println(F("Mempool.space WS Connnection Closed")); - break; + break; + case WEBSOCKET_EVENT_DATA: + onWebsocketMessage(data); + break; + case WEBSOCKET_EVENT_ERROR: + Serial.println(F("Mempool.space WS Connnection error")); + break; + case WEBSOCKET_EVENT_DISCONNECTED: + Serial.println(F("Mempool.space WS Connnection Closed")); + break; } } -void onWebsocketMessage(esp_websocket_event_data_t *event_data) { +void onWebsocketMessage(esp_websocket_event_data_t *event_data) +{ JsonDocument doc; - JsonDocument filter; filter["block"]["height"] = true; filter["mempool-blocks"][0]["medianFee"] = true; @@ -146,56 +151,67 @@ void onWebsocketMessage(esp_websocket_event_data_t *event_data) { // return; // } - if (doc.containsKey("block")) { + if (doc.containsKey("block")) + { JsonObject block = doc["block"]; currentBlockHeight = block["height"].as(); - //Serial.printf("New block found: %d\r\n", block["height"].as()); + // Serial.printf("New block found: %d\r\n", block["height"].as()); preferences.putUInt("blockHeight", currentBlockHeight); + lastBlockUpdate = esp_timer_get_time() / 1000000; - if (workQueue != nullptr) { + 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)) { + preferences.getBool("stealFocus", true)) + { uint64_t timerPeriod = 0; - if (isTimerActive()) { + if (isTimerActive()) + { // store timer periode before making inactive to prevent artifacts timerPeriod = getTimerSeconds(); esp_timer_stop(screenRotateTimer); } setCurrentScreen(SCREEN_BLOCK_HEIGHT); - if (timerPeriod > 0) { + if (timerPeriod > 0) + { esp_timer_start_periodic(screenRotateTimer, timerPeriod * usPerSecond); } } - if (preferences.getBool("ledFlashOnUpd", false)) { - vTaskDelay(pdMS_TO_TICKS(250)); // Wait until screens are updated + if (preferences.getBool("ledFlashOnUpd", false)) + { + vTaskDelay(pdMS_TO_TICKS(250)); // Wait until screens are updated queueLedEffect(LED_FLASH_BLOCK_NOTIFY); } } - } else if (doc.containsKey("mempool-blocks")) { - JsonArray blockInfo = doc["mempool-blocks"].as(); + } + else if (doc.containsKey("mempool-blocks")) + { + JsonArray blockInfo = doc["mempool-blocks"].as(); - uint medianFee = (uint)round(blockInfo[0]["medianFee"].as()); + uint medianFee = (uint)round(blockInfo[0]["medianFee"].as()); - if (blockMedianFee == medianFee) { - doc.clear(); - return; - } + if (blockMedianFee == medianFee) + { + doc.clear(); + return; + } - // Serial.printf("New median fee: %d\r\n", medianFee); - blockMedianFee = medianFee; + // Serial.printf("New median fee: %d\r\n", medianFee); + blockMedianFee = medianFee; - if (workQueue != nullptr) { - WorkItem blockUpdate = {TASK_FEE_UPDATE, 0}; - xQueueSend(workQueue, &blockUpdate, portMAX_DELAY); - } + if (workQueue != nullptr) + { + WorkItem blockUpdate = {TASK_FEE_UPDATE, 0}; + xQueueSend(workQueue, &blockUpdate, portMAX_DELAY); + } } doc.clear(); @@ -203,31 +219,67 @@ void onWebsocketMessage(esp_websocket_event_data_t *event_data) { uint getBlockHeight() { return currentBlockHeight; } -void setBlockHeight(uint newBlockHeight) { +void setBlockHeight(uint newBlockHeight) +{ currentBlockHeight = newBlockHeight; } uint getBlockMedianFee() { return blockMedianFee; } -void setBlockMedianFee(uint newBlockMedianFee) { +void setBlockMedianFee(uint newBlockMedianFee) +{ blockMedianFee = newBlockMedianFee; } -bool isBlockNotifyConnected() { - if (blockNotifyClient == NULL) return false; +bool isBlockNotifyConnected() +{ + if (blockNotifyClient == NULL) + return false; return esp_websocket_client_is_connected(blockNotifyClient); } -bool getBlockNotifyInit() { +bool getBlockNotifyInit() +{ return blockNotifyInit; } -void stopBlockNotify() { - if (blockNotifyClient == NULL) return; +void stopBlockNotify() +{ + if (blockNotifyClient == NULL) + return; esp_websocket_client_close(blockNotifyClient, portMAX_DELAY); esp_websocket_client_stop(blockNotifyClient); esp_websocket_client_destroy(blockNotifyClient); blockNotifyClient = NULL; +} + +int getBlockFetch() +{ + String mempoolInstance = + preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE); + + // Get current block height through regular API + HTTPClient *http = new HTTPClient(); + http->begin("https://" + mempoolInstance + "/api/blocks/tip/height"); + int httpCode = http->GET(); + + if (httpCode > 0 && httpCode == HTTP_CODE_OK) + { + String blockHeightStr = http->getString(); + return blockHeightStr.toInt(); + } + + return -1; +} + +uint getLastBlockUpdate() +{ + return lastBlockUpdate; +} + +void setLastBlockUpdate(uint lastUpdate) +{ + lastBlockUpdate = lastUpdate; } \ No newline at end of file diff --git a/src/lib/block_notify.hpp b/src/lib/block_notify.hpp index 72d9500..bfcc518 100644 --- a/src/lib/block_notify.hpp +++ b/src/lib/block_notify.hpp @@ -29,4 +29,7 @@ uint getBlockMedianFee(); bool isBlockNotifyConnected(); void stopBlockNotify(); -bool getBlockNotifyInit(); \ No newline at end of file +bool getBlockNotifyInit(); +uint getLastBlockUpdate(); +int getBlockFetch(); +void setLastBlockUpdate(uint lastUpdate); \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 47e73fb..fb7148a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2023 Djuri Baars + * Copyright 2023-2024 Djuri Baars * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,13 +23,15 @@ uint wifiLostConnection; uint priceNotifyLostConnection = 0; uint blockNotifyLostConnection = 0; -extern "C" void app_main() { +extern "C" void app_main() +{ initArduino(); Serial.begin(115200); setup(); - while (true) { + while (true) + { // vTaskList(ptrTaskList); // Serial.println(F("**********************************")); // Serial.println(F("Task State Prio Stack Num")); @@ -39,60 +41,96 @@ extern "C" void app_main() { if (eventSourceTaskHandle != NULL) xTaskNotifyGive(eventSourceTaskHandle); - int64_t currentUptime = esp_timer_get_time() / 1000000;; + int64_t currentUptime = esp_timer_get_time() / 1000000; + ; - if (!WiFi.isConnected()) { - if (!wifiLostConnection) { + if (!WiFi.isConnected()) + { + if (!wifiLostConnection) + { wifiLostConnection = currentUptime; Serial.println("Lost WiFi connection, trying to reconnect..."); } - if ((currentUptime - wifiLostConnection) > 600) { + if ((currentUptime - wifiLostConnection) > 600) + { Serial.println("Still no connection after 10 minutes, restarting..."); delay(2000); ESP.restart(); } WiFi.begin(); - } else if (wifiLostConnection) { + } + else if (wifiLostConnection) + { wifiLostConnection = 0; Serial.println("Connection restored, reset timer."); - } else if (getPriceNotifyInit() && !preferences.getBool("fetchEurPrice", false) && !isPriceNotifyConnected()) { + } + else if (getPriceNotifyInit() && !preferences.getBool("fetchEurPrice", false) && !isPriceNotifyConnected()) + { priceNotifyLostConnection++; Serial.println("Lost price data connection..."); queueLedEffect(LED_DATA_PRICE_ERROR); // if price WS connection does not come back after 6*5 seconds, destroy and recreate - if (priceNotifyLostConnection > 6) { + if (priceNotifyLostConnection > 6) + { Serial.println("Restarting price handler..."); stopPriceNotify(); setupPriceNotify(); priceNotifyLostConnection = 0; } - } else if (getBlockNotifyInit() && !isBlockNotifyConnected()) { + } + else if (getBlockNotifyInit() && !isBlockNotifyConnected()) + { blockNotifyLostConnection++; Serial.println("Lost block data connection..."); queueLedEffect(LED_DATA_BLOCK_ERROR); // if mempool WS connection does not come back after 6*5 seconds, destroy and recreate - if (blockNotifyLostConnection > 6) { + if (blockNotifyLostConnection > 6) + { Serial.println("Restarting block handler..."); stopBlockNotify(); setupBlockNotify(); blockNotifyLostConnection = 0; } - } else if (blockNotifyLostConnection > 0 || priceNotifyLostConnection > 0) { + } + else if (blockNotifyLostConnection > 0 || priceNotifyLostConnection > 0) + { blockNotifyLostConnection = 0; priceNotifyLostConnection = 0; } // if more than 5 price updates are missed, there is probably something wrong, reconnect - if ((getLastPriceUpdate() - currentUptime) > (preferences.getUInt("minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE)*5)) { - stopPriceNotify(); - setupPriceNotify(); + if ((getLastPriceUpdate() - currentUptime) > (preferences.getUInt("minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE) * 5)) + { + Serial.println("Detected 5 missed price updates... restarting price handler."); - priceNotifyLostConnection = 0; + stopPriceNotify(); + setupPriceNotify(); + + priceNotifyLostConnection = 0; + } + + // If after 45 minutes no mempool blocks, check the rest API + if ((getLastBlockUpdate() - currentUptime) > 45 * 60) + { + Serial.println("Long time (45 min) since last block, checking if I missed anything..."); + int currentBlock = getBlockFetch(); + if (currentBlock != -1) + { + if (currentBlock != getBlockHeight()) + { + Serial.println("Detected stuck block height... restarting block handler."); + // Mempool source stuck, restart + stopBlockNotify(); + setupBlockNotify(); + } + // set last block update so it doesn't fetch for 45 minutes + setLastBlockUpdate(currentUptime); + } } vTaskDelay(pdMS_TO_TICKS(5000));