diff --git a/README.md b/README.md index f4d5978..4659d70 100644 --- a/README.md +++ b/README.md @@ -495,7 +495,7 @@ ESPUI includes a range of advanced features that can customise your UIs. ### Dynamic Visibility -Cotrols can be made visible or invisible at runtime with the `updateVisibility()` function. +Controls can be made visible or invisible at runtime with the `updateVisibility()` function. ``` ESPUI.updateVisibility(controlId, false); diff --git a/src/ESPUI.cpp b/src/ESPUI.cpp index abbe40a..90f84e3 100644 --- a/src/ESPUI.cpp +++ b/src/ESPUI.cpp @@ -13,6 +13,61 @@ #include "dataTabbedcontentJS.h" #include "dataZeptoJS.h" +#if ESP8266 +#include +#endif + +static String heapInfo (const __FlashStringHelper* mode) +{ +#if ESP8266 + + uint32_t hfree; + uint32_t hmax; + uint8_t hfrag; + String result; + result.reserve(128); + +#ifdef UMM_HEAP_IRAM + // here esp8266 is configurerd to use an extra 16KB (i)ram + { + HeapSelectIram useInstructionRamHere; + ESP.getHeapStats(&hfree, &hmax, &hfrag); + } + result += F("IRAM: free: "); + result += hfree; + result += F(" max: "); + result += hmax; + result += F(" frag: "); + result += hfrag; + result += "%\n"; +#endif // !UMM_HEAP_IRAM + { + HeapSelectDram useRegularRamHere; + ESP.getHeapStats(&hfree, &hmax, &hfrag); + } + result += F("DRAM: free: "); + result += hfree; + result += F(" max: "); + result += hmax; + result += F(" frag: "); + result += hfrag; + result += "%\n"; + +#else // !ESP8266 + + result += ESP.getFreeHeap(); + result += ' '; + +#endif // !ESP8266 + + result += mode; + + return result; +} + + + + // ################# LITTLEFS functions #if defined(ESP32) void listDir(const char* dirname, uint8_t levels) @@ -1260,7 +1315,8 @@ void ESPUIClass::beginLITTLEFS(const char* _title, const char* username, const c return request->requestAuthentication(); } - request->send(200, "text/plain", String(ESP.getFreeHeap()) + " In LITTLEFS mode"); + request->send(200, "text/plain", heapInfo(F("In LITTLEFS mode"))); + }); server->onNotFound([this](AsyncWebServerRequest* request) { @@ -1418,7 +1474,7 @@ void ESPUIClass::begin(const char* _title, const char* username, const char* pas return request->requestAuthentication(); } - request->send(200, "text/plain", String(ESP.getFreeHeap()) + " In Memorymode"); + request->send(200, "text/plain", heapInfo(F("In Memorymode"))); }); server->onNotFound([this](AsyncWebServerRequest* request) { diff --git a/src/ESPUI.h b/src/ESPUI.h index fb42039..878097f 100644 --- a/src/ESPUI.h +++ b/src/ESPUI.h @@ -87,20 +87,20 @@ enum Verbosity : uint8_t class ESPUIClass { public: + +#ifdef ESP32 ESPUIClass() { - verbosity = Verbosity::Quiet; - jsonUpdateDocumentSize = 2000; - jsonInitialDocumentSize = 8000; - sliderContinuous = false; -#ifdef ESP32 ControlsSemaphore = xSemaphoreCreateMutex(); xSemaphoreGive(ControlsSemaphore); -#endif // def ESP32 } - unsigned int jsonUpdateDocumentSize; - unsigned int jsonInitialDocumentSize; - bool sliderContinuous; + SemaphoreHandle_t ControlsSemaphore = NULL; +#endif // def ESP32 + + unsigned int jsonUpdateDocumentSize = 2000; + unsigned int jsonInitialDocumentSize = 8000; + unsigned int jsonChunkNumberMax = 0; + bool sliderContinuous = false; void onWsEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len); bool captivePortal = true; @@ -205,17 +205,13 @@ public: void jsonReload(); void jsonDom(uint16_t startidx, AsyncWebSocketClient* client = nullptr, bool Updating = false); - Verbosity verbosity; + Verbosity verbosity = Verbosity::Quiet; AsyncWebServer* server; protected: friend class ESPUIclient; friend class ESPUIcontrol; -#ifdef ESP32 - SemaphoreHandle_t ControlsSemaphore = NULL; -#endif // def ESP32 - void RemoveToBeDeletedControls(); AsyncWebSocket* ws; diff --git a/src/ESPUIclient.cpp b/src/ESPUIclient.cpp index 9b7d561..9bce217 100644 --- a/src/ESPUIclient.cpp +++ b/src/ESPUIclient.cpp @@ -2,6 +2,42 @@ #include "ESPUIclient.h" #include "ESPUIcontrol.h" +// JSONSlave: +// helper to process exact JSON serialization size +// it takes ~2ms on esp8266 and avoid large String reallocation which is really worth the cost +class JSONSlave: public Print +{ +public: + size_t write (uint8_t c) override { counter++; return 1; } + size_t write (const uint8_t* buf, size_t count) override { counter += count; return count; } + size_t get_counter () { return counter; } + + static size_t serializedSize (JsonDocument& doc) + { + JSONSlave counter; + serializeJson(doc, counter); + return counter.get_counter(); + } + + static size_t serialize (JsonDocument& doc, String& str) + { + size_t s = serializedSize(doc) + 10; // 10 is paranoid + str.reserve(s); + serializeJson(doc, str); + return s; + } + + static String toString (JsonDocument& doc) + { + String str; + serialize(doc, str); + return str; + } + +protected: + size_t counter = 0; +}; + ESPUIclient::ESPUIclient(AsyncWebSocketClient * _client): client(_client) { @@ -309,7 +345,7 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex, elementcount++; control->MarshalControl(item, InUpdateMode); - if (rootDoc.overflowed()) + if (rootDoc.overflowed() || (ESPUI.jsonChunkNumberMax > 0 && (elementcount % ESPUI.jsonChunkNumberMax) == 0)) { // String("prepareJSONChunk: too much data in the message. Remove the last entry"); if (1 == elementcount) @@ -407,9 +443,8 @@ bool ESPUIclient::SendControlsToClient(uint16_t startidx, if (ESPUI.verbosity >= Verbosity::VerboseJSON) { Serial.println(F("ESPUIclient:SendControlsToClient: Sending elements --------->")); - String json; - serializeJson(document, json); - Serial.println(json); + serializeJson(document, Serial); + Serial.println(); } #endif @@ -454,10 +489,7 @@ bool ESPUIclient::SendJsonDocToWebSocket(DynamicJsonDocument& document) break; } - String json; - json.reserve(document.size() / 2); - json.clear(); - serializeJson(document, json); + String json = JSONSlave::toString(document); #if defined(DEBUG_ESPUI) if (ESPUI.verbosity >= Verbosity::VerboseJSON)