From 2ca85ff479705a559fcea768765fb7a077f7327d Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Sun, 10 Mar 2024 20:24:55 +0100 Subject: [PATCH] Upgrade ArduinoJson to version 7, add Block Fee Rate screen --- dependencies.lock | 4 +-- lib/btclock/data_handler.cpp | 22 +++++++++++++++ lib/btclock/data_handler.hpp | 3 +- platformio.ini | 4 +-- src/lib/block_notify.cpp | 43 ++++++++++++++++++++++++++--- src/lib/block_notify.hpp | 4 +++ src/lib/config.cpp | 1 + src/lib/config.hpp | 3 +- src/lib/ota.cpp | 6 ++-- src/lib/price_fetch.cpp | 2 +- src/lib/price_notify.cpp | 2 +- src/lib/screen_handler.cpp | 7 +++++ src/lib/screen_handler.hpp | 1 + src/lib/shared.hpp | 20 ++++++++------ src/lib/webserver.cpp | 34 +++++++++++------------ src/lib/webserver.hpp | 4 +-- test/test_datahandler/test_main.cpp | 11 ++++++++ 17 files changed, 128 insertions(+), 43 deletions(-) diff --git a/dependencies.lock b/dependencies.lock index 345aa6f..b815904 100644 --- a/dependencies.lock +++ b/dependencies.lock @@ -1,11 +1,11 @@ dependencies: esp_littlefs: - component_hash: 66d5afe7ad323d0159d04e296c14ffa0e39156502bc15e0520eff2af392d3aa4 + component_hash: 6ae78edac4f81c605d6d7bff75f4f9a45d25938e4796347f91ea975ed3123326 source: git: https://github.com/joltwallet/esp_littlefs.git path: . type: git - version: 41873c20fb5cdbcf28d7d6cc04e4bcb4a1305317 + version: fd64733cdf248c7a7eb207db7d28124f8857fe0b idf: component_hash: null source: diff --git a/lib/btclock/data_handler.cpp b/lib/btclock/data_handler.cpp index 8719626..fc5f4e8 100644 --- a/lib/btclock/data_handler.cpp +++ b/lib/btclock/data_handler.cpp @@ -87,6 +87,28 @@ std::array parseBlockHeight(std::uint32_t blockHeight) return ret; } +std::array parseBlockFees(std::uint16_t blockFees) { + std::array ret; + std::string blockFeesString = std::to_string(blockFees); + std::uint32_t firstIndex = 0; + + if (blockFeesString.length() < NUM_SCREENS) + { + blockFeesString.insert(blockFeesString.begin(), NUM_SCREENS - blockFeesString.length() - 1, ' '); + ret[0] = "FEE/RATE"; + firstIndex = 1; + } + + for (uint i = firstIndex; i < NUM_SCREENS-1; i++) + { + ret[i] = blockFeesString[i]; + } + + ret[NUM_SCREENS-1] = "sat/vB"; + + return ret; +} + std::array parseHalvingCountdown(std::uint32_t blockHeight, bool asBlocks) { std::array ret; diff --git a/lib/btclock/data_handler.hpp b/lib/btclock/data_handler.hpp index d385cef..1531c96 100644 --- a/lib/btclock/data_handler.hpp +++ b/lib/btclock/data_handler.hpp @@ -9,4 +9,5 @@ std::array parsePriceData(std::uint32_t price, char cu std::array parseSatsPerCurrency(std::uint32_t price, char currencySymbol, bool withSatsSymbol); std::array parseBlockHeight(std::uint32_t blockHeight); std::array parseHalvingCountdown(std::uint32_t blockHeight, bool asBlocks); -std::array parseMarketCap(std::uint32_t blockHeight, std::uint32_t price, char currencySymbol, bool bigChars); \ No newline at end of file +std::array parseMarketCap(std::uint32_t blockHeight, std::uint32_t price, char currencySymbol, bool bigChars); +std::array parseBlockFees(std::uint16_t blockFees); diff --git a/platformio.ini b/platformio.ini index e0924a9..ce953dd 100644 --- a/platformio.ini +++ b/platformio.ini @@ -30,9 +30,9 @@ build_unflags = -Werror=all -fno-exceptions lib_deps = - bblanchon/ArduinoJson@^6.21.5 + bblanchon/ArduinoJson@^7.0.3 esphome/Improv@^1.2.3 - esphome/ESPAsyncWebServer-esphome@^3.1.0 + mathieucarbou/ESP Async WebServer adafruit/Adafruit BusIO@^1.15.0 adafruit/Adafruit MCP23017 Arduino Library@^2.3.2 adafruit/Adafruit NeoPixel@^1.12.0 diff --git a/src/lib/block_notify.cpp b/src/lib/block_notify.cpp index 897d7dd..904b6f3 100644 --- a/src/lib/block_notify.cpp +++ b/src/lib/block_notify.cpp @@ -3,6 +3,7 @@ char *wsServer; esp_websocket_client_handle_t blockNotifyClient = NULL; uint currentBlockHeight = 816000; +uint blockMedianFee = 1; bool blockNotifyInit = false; // const char *mempoolWsCert = R"(-----BEGIN CERTIFICATE----- @@ -103,7 +104,7 @@ void setupBlockNotify() { void onWebsocketEvent(void *handler_args, esp_event_base_t base, 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\"]}"; + const String sub = "{\"action\": \"want\", \"data\":[\"blocks\", \"mempool-blocks\"]}"; switch (event_id) { case WEBSOCKET_EVENT_CONNECTED: blockNotifyInit = true; @@ -130,16 +131,27 @@ void onWebsocketEvent(void *handler_args, esp_event_base_t base, } void onWebsocketMessage(esp_websocket_event_data_t *event_data) { - SpiRamJsonDocument doc(event_data->data_len); + JsonDocument doc; - deserializeJson(doc, (char *)event_data->data_ptr); + + JsonDocument filter; + filter["block"]["height"] = true; + filter["mempool-blocks"][0]["medianFee"] = true; + + DeserializationError error = deserializeJson(doc, (char *)event_data->data_ptr, DeserializationOption::Filter(filter)); + + // if (error) { + // Serial.print("deserializeJson() failed: "); + // Serial.println(error.c_str()); + // return; + // } 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); if (workQueue != nullptr) { @@ -167,6 +179,23 @@ void onWebsocketMessage(esp_websocket_event_data_t *event_data) { queueLedEffect(LED_FLASH_BLOCK_NOTIFY); } } + } else if (doc.containsKey("mempool-blocks")) { + JsonArray blockInfo = doc["mempool-blocks"].as(); + + uint medianFee = (uint)round(blockInfo[0]["medianFee"].as()); + + if (blockMedianFee == medianFee) { + doc.clear(); + return; + } + + // 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); + } } doc.clear(); @@ -178,6 +207,12 @@ void setBlockHeight(uint newBlockHeight) { currentBlockHeight = newBlockHeight; } +uint getBlockMedianFee() { return blockMedianFee; } + +void setBlockMedianFee(uint newBlockMedianFee) { + blockMedianFee = newBlockMedianFee; +} + bool isBlockNotifyConnected() { if (blockNotifyClient == NULL) return false; return esp_websocket_client_is_connected(blockNotifyClient); diff --git a/src/lib/block_notify.hpp b/src/lib/block_notify.hpp index f0d903d..72d9500 100644 --- a/src/lib/block_notify.hpp +++ b/src/lib/block_notify.hpp @@ -23,6 +23,10 @@ void onWebsocketMessage(esp_websocket_event_data_t *event_data); void setBlockHeight(uint newBlockHeight); uint getBlockHeight(); + +void setBlockMedianFee(uint blockMedianFee); +uint getBlockMedianFee(); + bool isBlockNotifyConnected(); void stopBlockNotify(); bool getBlockNotifyInit(); \ No newline at end of file diff --git a/src/lib/config.cpp b/src/lib/config.cpp index 068f8fb..8f18a84 100644 --- a/src/lib/config.cpp +++ b/src/lib/config.cpp @@ -208,6 +208,7 @@ void setupPreferences() setPrice(preferences.getUInt("lastPrice", 30000)); screenNameMap[SCREEN_BLOCK_HEIGHT] = "Block Height"; + screenNameMap[SCREEN_BLOCK_FEE_RATE] = "Block Fee Rate"; screenNameMap[SCREEN_MSCW_TIME] = "Sats per dollar"; screenNameMap[SCREEN_BTC_TICKER] = "Ticker"; screenNameMap[SCREEN_TIME] = "Time"; diff --git a/src/lib/config.hpp b/src/lib/config.hpp index 228beea..cd4b262 100644 --- a/src/lib/config.hpp +++ b/src/lib/config.hpp @@ -1,4 +1,5 @@ -#pragma once; +#pragma once + #include #include #include diff --git a/src/lib/ota.cpp b/src/lib/ota.cpp index d23929f..945a4d4 100644 --- a/src/lib/ota.cpp +++ b/src/lib/ota.cpp @@ -85,13 +85,13 @@ void downloadUpdate() { if (httpCode == 200) { // WiFiClient * stream = http->getStreamPtr(); - StaticJsonDocument<64> filter; + JsonDocument filter; - JsonObject filter_assets_0 = filter["assets"].createNestedObject(); + JsonObject filter_assets_0 = filter["assets"].add(); filter_assets_0["name"] = true; filter_assets_0["browser_download_url"] = true; - SpiRamJsonDocument doc(1536); + JsonDocument doc; DeserializationError error = deserializeJson( doc, http.getStream(), DeserializationOption::Filter(filter)); diff --git a/src/lib/price_fetch.cpp b/src/lib/price_fetch.cpp index 73606a2..d5e6e10 100644 --- a/src/lib/price_fetch.cpp +++ b/src/lib/price_fetch.cpp @@ -24,7 +24,7 @@ void taskPriceFetch(void *pvParameters) { uint usdPrice, eurPrice; if (httpCode == 200) { String payload = http->getString(); - StaticJsonDocument<96> doc; + JsonDocument doc; deserializeJson(doc, payload); // usdPrice = doc["bitcoin"]["usd"]; eurPrice = doc["bitcoin"]["eur"].as(); diff --git a/src/lib/price_notify.cpp b/src/lib/price_notify.cpp index a380942..c07143e 100644 --- a/src/lib/price_notify.cpp +++ b/src/lib/price_notify.cpp @@ -76,7 +76,7 @@ void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base, } void onWebsocketPriceMessage(esp_websocket_event_data_t *event_data) { - SpiRamJsonDocument doc(event_data->data_len); + JsonDocument doc; deserializeJson(doc, (char *)event_data->data_ptr); diff --git a/src/lib/screen_handler.cpp b/src/lib/screen_handler.cpp index be2e8ca..da175de 100644 --- a/src/lib/screen_handler.cpp +++ b/src/lib/screen_handler.cpp @@ -46,6 +46,13 @@ void workerTask(void *pvParameters) { setEpdContent(taskEpdContent); break; } + case TASK_FEE_UPDATE: { + if (getCurrentScreen() == SCREEN_BLOCK_FEE_RATE) { + taskEpdContent = parseBlockFees(static_cast(getBlockMedianFee())); + setEpdContent(taskEpdContent); + } + break; + } case TASK_BLOCK_UPDATE: { if (getCurrentScreen() != SCREEN_HALVING_COUNTDOWN) { taskEpdContent = parseBlockHeight(getBlockHeight()); diff --git a/src/lib/screen_handler.hpp b/src/lib/screen_handler.hpp index 464969b..6cbb2d0 100644 --- a/src/lib/screen_handler.hpp +++ b/src/lib/screen_handler.hpp @@ -24,6 +24,7 @@ extern QueueHandle_t workQueue; typedef enum { TASK_PRICE_UPDATE, TASK_BLOCK_UPDATE, + TASK_FEE_UPDATE, TASK_TIME_UPDATE } TaskType; diff --git a/src/lib/shared.hpp b/src/lib/shared.hpp index c4c6fab..9f99c19 100644 --- a/src/lib/shared.hpp +++ b/src/lib/shared.hpp @@ -22,26 +22,28 @@ const PROGMEM int SCREEN_BTC_TICKER = 2; const PROGMEM int SCREEN_TIME = 3; const PROGMEM int SCREEN_HALVING_COUNTDOWN = 4; const PROGMEM int SCREEN_MARKET_CAP = 5; +const PROGMEM int SCREEN_BLOCK_FEE_RATE = 6; const PROGMEM int SCREEN_COUNTDOWN = 98; const PROGMEM int SCREEN_CUSTOM = 99; -const int SCREEN_COUNT = 6; +const int SCREEN_COUNT = 7; const PROGMEM int screens[SCREEN_COUNT] = { SCREEN_BLOCK_HEIGHT, SCREEN_MSCW_TIME, SCREEN_BTC_TICKER, - SCREEN_TIME, SCREEN_HALVING_COUNTDOWN, SCREEN_MARKET_CAP}; + SCREEN_TIME, SCREEN_HALVING_COUNTDOWN, SCREEN_MARKET_CAP, + SCREEN_BLOCK_FEE_RATE}; const int usPerSecond = 1000000; const int usPerMinute = 60 * usPerSecond; -struct SpiRamAllocator { - void *allocate(size_t size) { +struct SpiRamAllocator : ArduinoJson::Allocator { + void* allocate(size_t size) override { return heap_caps_malloc(size, MALLOC_CAP_SPIRAM); } - void deallocate(void *pointer) { heap_caps_free(pointer); } + void deallocate(void* pointer) override { + heap_caps_free(pointer); + } - void *reallocate(void *ptr, size_t new_size) { + void* reallocate(void* ptr, size_t new_size) override { return heap_caps_realloc(ptr, new_size, MALLOC_CAP_SPIRAM); } -}; - -using SpiRamJsonDocument = BasicJsonDocument; +}; \ No newline at end of file diff --git a/src/lib/webserver.cpp b/src/lib/webserver.cpp index 69f3bc9..eded90f 100644 --- a/src/lib/webserver.cpp +++ b/src/lib/webserver.cpp @@ -94,8 +94,8 @@ void setupWebserver() { void stopWebServer() { server.end(); } -StaticJsonDocument<768> getStatusObject() { - StaticJsonDocument<768> root; +JsonDocument getStatusObject() { + JsonDocument root; root["currentScreen"] = getCurrentScreen(); root["numScreens"] = NUM_SCREENS; @@ -108,7 +108,7 @@ StaticJsonDocument<768> getStatusObject() { // root["espFreePsram"] = ESP.getFreePsram(); // root["espPsramSize"] = ESP.getPsramSize(); - JsonObject conStatus = root.createNestedObject("connectionStatus"); + JsonObject conStatus = root["connectionStatus"].to(); conStatus["price"] = isPriceNotifyConnected(); conStatus["blocks"] = isBlockNotifyConnected(); @@ -117,9 +117,9 @@ StaticJsonDocument<768> getStatusObject() { return root; } -StaticJsonDocument<512> getLedStatusObject() { - StaticJsonDocument<512> root; - JsonArray colors = root.createNestedArray("data"); +JsonDocument getLedStatusObject() { + JsonDocument root; + JsonArray colors = root["data"].to(); // Adafruit_NeoPixel pix = getPixels(); for (uint i = 0; i < pixels.numPixels(); i++) { @@ -131,7 +131,7 @@ StaticJsonDocument<512> getLedStatusObject() { char hexColor[8]; sprintf(hexColor, "#%02X%02X%02X", red, green, blue); - JsonObject object = colors.createNestedObject(); + JsonObject object = colors.add(); object["red"] = red; object["green"] = green; object["blue"] = blue; @@ -143,8 +143,8 @@ StaticJsonDocument<512> getLedStatusObject() { void eventSourceUpdate() { if (!events.count()) return; - StaticJsonDocument<768> root = getStatusObject(); - JsonArray data = root.createNestedArray("data"); + JsonDocument root = getStatusObject(); + JsonArray data = root["data"].to(); root["leds"] = getLedStatusObject()["data"]; @@ -168,9 +168,9 @@ void onApiStatus(AsyncWebServerRequest *request) { AsyncResponseStream *response = request->beginResponseStream("application/json"); - StaticJsonDocument<1024> root = getStatusObject(); - JsonArray data = root.createNestedArray("data"); - JsonArray rendered = root.createNestedArray("rendered"); + JsonDocument root = getStatusObject(); + JsonArray data = root["data"].to(); + JsonArray rendered = root["rendered"].to(); String epdContent[NUM_SCREENS]; root["leds"] = getLedStatusObject()["data"]; @@ -384,7 +384,7 @@ void onApiRestart(AsyncWebServerRequest *request) { * @Path("/api/settings") */ void onApiSettingsGet(AsyncWebServerRequest *request) { - StaticJsonDocument<1536> root; + JsonDocument root; root["numScreens"] = NUM_SCREENS; root["fgColor"] = getFgColor(); root["bgColor"] = getBgColor(); @@ -421,12 +421,12 @@ void onApiSettingsGet(AsyncWebServerRequest *request) { #ifdef LAST_BUILD_TIME root["lastBuildTime"] = String(LAST_BUILD_TIME); #endif - JsonArray screens = root.createNestedArray("screens"); + JsonArray screens = root["screens"].to(); std::vector screenNameMap = getScreenNameMap(); for (int i = 0; i < screenNameMap.size(); i++) { - JsonObject o = screens.createNestedObject(); + JsonObject o = screens.add(); String key = "screen" + String(i) + "Visible"; o["id"] = i; o["name"] = screenNameMap[i]; @@ -650,7 +650,7 @@ void onApiSystemStatus(AsyncWebServerRequest *request) { AsyncResponseStream *response = request->beginResponseStream("application/json"); - StaticJsonDocument<128> root; + JsonDocument root; root["espFreeHeap"] = ESP.getFreeHeap(); root["espHeapSize"] = ESP.getHeapSize(); @@ -743,7 +743,7 @@ void onApiLightsSetColor(AsyncWebServerRequest *request) { setLights(r, g, b); } - StaticJsonDocument<48> doc; + JsonDocument doc; doc["result"] = rgbColor; serializeJson(getLedStatusObject()["data"], *response); diff --git a/src/lib/webserver.hpp b/src/lib/webserver.hpp index f8afa98..c910c3e 100644 --- a/src/lib/webserver.hpp +++ b/src/lib/webserver.hpp @@ -46,8 +46,8 @@ void onApiRestart(AsyncWebServerRequest *request); void onIndex(AsyncWebServerRequest *request); void onNotFound(AsyncWebServerRequest *request); -StaticJsonDocument<512> getLedStatusObject(); -StaticJsonDocument<768> getStatusObject(); +JsonDocument getLedStatusObject(); +JsonDocument getStatusObject(); void eventSourceUpdate(); void eventSourceTask(void *pvParameters); diff --git a/test/test_datahandler/test_main.cpp b/test/test_datahandler/test_main.cpp index f863551..2c9ffb2 100644 --- a/test/test_datahandler/test_main.cpp +++ b/test/test_datahandler/test_main.cpp @@ -31,6 +31,16 @@ void test_SevenCharacterBlockHeight(void) { TEST_ASSERT_EQUAL_STRING("0", output[1].c_str()); } +void test_FeeRateDisplay(void) { + uint testValue = 21; + std::array output = parseBlockFees(static_cast(testValue)); + TEST_ASSERT_EQUAL_STRING("FEE/RATE", output[0].c_str()); + TEST_ASSERT_EQUAL_STRING("2", output[NUM_SCREENS-3].c_str()); + TEST_ASSERT_EQUAL_STRING("1", output[NUM_SCREENS-2].c_str()); + TEST_ASSERT_EQUAL_STRING("sat/vB", output[NUM_SCREENS-1].c_str()); +} + + void test_PriceOf100kusd(void) { std::array output = parsePriceData(100000, '$'); TEST_ASSERT_EQUAL_STRING("$", output[0].c_str()); @@ -89,6 +99,7 @@ int runUnityTests(void) { RUN_TEST(test_CorrectSatsPerDollarConversion); RUN_TEST(test_SixCharacterBlockHeight); RUN_TEST(test_SevenCharacterBlockHeight); + RUN_TEST(test_FeeRateDisplay); RUN_TEST(test_PriceOf100kusd); RUN_TEST(test_McapLowerUsd); RUN_TEST(test_Mcap1TrillionUsd);