Upgrade ArduinoJson to version 7, add Block Fee Rate screen

This commit is contained in:
Djuri Baars 2024-03-10 20:24:55 +01:00
parent c49b8edcb8
commit 2ca85ff479
17 changed files with 128 additions and 43 deletions

View file

@ -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:

View file

@ -87,6 +87,28 @@ std::array<std::string, NUM_SCREENS> parseBlockHeight(std::uint32_t blockHeight)
return ret;
}
std::array<std::string, NUM_SCREENS> parseBlockFees(std::uint16_t blockFees) {
std::array<std::string, NUM_SCREENS> 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<std::string, NUM_SCREENS> parseHalvingCountdown(std::uint32_t blockHeight, bool asBlocks)
{
std::array<std::string, NUM_SCREENS> ret;

View file

@ -9,4 +9,5 @@ std::array<std::string, NUM_SCREENS> parsePriceData(std::uint32_t price, char cu
std::array<std::string, NUM_SCREENS> parseSatsPerCurrency(std::uint32_t price, char currencySymbol, bool withSatsSymbol);
std::array<std::string, NUM_SCREENS> parseBlockHeight(std::uint32_t blockHeight);
std::array<std::string, NUM_SCREENS> parseHalvingCountdown(std::uint32_t blockHeight, bool asBlocks);
std::array<std::string, NUM_SCREENS> parseMarketCap(std::uint32_t blockHeight, std::uint32_t price, char currencySymbol, bool bigChars);
std::array<std::string, NUM_SCREENS> parseMarketCap(std::uint32_t blockHeight, std::uint32_t price, char currencySymbol, bool bigChars);
std::array<std::string, NUM_SCREENS> parseBlockFees(std::uint16_t blockFees);

View file

@ -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

View file

@ -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<uint>();
Serial.printf("New block found: %d\r\n", block["height"].as<uint>());
//Serial.printf("New block found: %d\r\n", block["height"].as<uint>());
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<JsonArray>();
uint medianFee = (uint)round(blockInfo[0]["medianFee"].as<double>());
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);

View file

@ -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();

View file

@ -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";

View file

@ -1,4 +1,5 @@
#pragma once;
#pragma once
#include <Adafruit_MCP23X17.h>
#include <Arduino.h>
#include <Preferences.h>

View file

@ -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<JsonObject>();
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));

View file

@ -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<uint>();

View file

@ -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);

View file

@ -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<std::uint16_t>(getBlockMedianFee()));
setEpdContent(taskEpdContent);
}
break;
}
case TASK_BLOCK_UPDATE: {
if (getCurrentScreen() != SCREEN_HALVING_COUNTDOWN) {
taskEpdContent = parseBlockHeight(getBlockHeight());

View file

@ -24,6 +24,7 @@ extern QueueHandle_t workQueue;
typedef enum {
TASK_PRICE_UPDATE,
TASK_BLOCK_UPDATE,
TASK_FEE_UPDATE,
TASK_TIME_UPDATE
} TaskType;

View file

@ -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<SpiRamAllocator>;
};

View file

@ -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<JsonObject>();
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<JsonArray>();
// 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<JsonObject>();
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<JsonArray>();
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>();
JsonArray rendered = root["rendered"].to<JsonArray>();
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<JsonArray>();
std::vector<std::string> screenNameMap = getScreenNameMap();
for (int i = 0; i < screenNameMap.size(); i++) {
JsonObject o = screens.createNestedObject();
JsonObject o = screens.add<JsonObject>();
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);

View file

@ -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);

View file

@ -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<std::string, NUM_SCREENS> output = parseBlockFees(static_cast<std::uint16_t>(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<std::string, NUM_SCREENS> 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);