From 66bad2b6f8100dedfc02fdbf4f24c8e09b332316 Mon Sep 17 00:00:00 2001 From: Christian Schwinne Date: Sat, 4 Dec 2021 01:05:01 +0100 Subject: [PATCH] Single json buffer (#2336) * Single/static JSON buffer for all requests. * Missing json.cpp changes. * Async fix. * Added conditional compile (WLED_USE_DYNAMIC_JSON). * Advanced locking with time-out. * Missing releaseJSONBufferLock() on error response. * Fix for config saving. * Fixes and optimisations. Dadded debugging information. * Fix for ledmaps. * No unsolicited serial sending if GPIO1 allocated * Stray semicolons * Fix JSON ledmap Co-authored-by: Blaz Kristan --- wled00/FX_fcn.cpp | 18 ++++++-- wled00/cfg.cpp | 36 +++++++++++++-- wled00/colors.cpp | 2 +- wled00/fcn_declare.h | 11 +++++ wled00/ir.cpp | 34 ++++++++------ wled00/json.cpp | 25 ++++++---- wled00/mqtt.cpp | 19 ++++---- wled00/presets.cpp | 51 +++++++++++++-------- wled00/set.cpp | 7 +++ wled00/src/dependencies/json/AsyncJson-v6.h | 11 ++++- wled00/util.cpp | 33 +++++++++++++ wled00/wled.cpp | 10 +++- wled00/wled.h | 9 +++- wled00/wled_eeprom.cpp | 17 +++++-- wled00/wled_serial.cpp | 22 +++++---- wled00/wled_server.cpp | 18 +++++--- wled00/ws.cpp | 29 ++++++++---- wled00/xml.cpp | 11 ++++- 18 files changed, 272 insertions(+), 91 deletions(-) create mode 100644 wled00/util.cpp diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index ec23636b0..20ff12b85 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -86,8 +86,6 @@ void WS2812FX::finalizeInit(void) busses.add(defCfg); } } - - deserializeMap(); _length = 0; for (uint8_t i=0; i(), true); + bool needsSave = deserializeConfig(doc.as(), true); + releaseJSONBufferLock(); + + if (needsSave) serializeConfig(); // usermods required new prameters } void serializeConfig() { @@ -446,7 +454,11 @@ void serializeConfig() { DEBUG_PRINTLN(F("Writing settings to /cfg.json...")); + #ifdef WLED_USE_DYNAMIC_JSON DynamicJsonDocument doc(JSON_BUFFER_SIZE); + #else + if (!requestJSONBufferLock(2)) return; + #endif JsonArray rev = doc.createNestedArray("rev"); rev.add(1); //major settings revision @@ -757,16 +769,24 @@ void serializeConfig() { File f = WLED_FS.open("/cfg.json", "w"); if (f) serializeJson(doc, f); f.close(); + releaseJSONBufferLock(); } //settings in /wsec.json, not accessible via webserver, for passwords and tokens bool deserializeConfigSec() { DEBUG_PRINTLN(F("Reading settings from /wsec.json...")); + #ifdef WLED_USE_DYNAMIC_JSON DynamicJsonDocument doc(JSON_BUFFER_SIZE); + #else + if (!requestJSONBufferLock(3)) return false; + #endif bool success = readObjectFromFile("/wsec.json", nullptr, &doc); - if (!success) return false; + if (!success) { + releaseJSONBufferLock(); + return false; + } JsonObject nw_ins_0 = doc["nw"]["ins"][0]; getStringFromJson(clientPass, nw_ins_0["psk"], 65); @@ -798,13 +818,18 @@ bool deserializeConfigSec() { CJSON(wifiLock, ota[F("lock-wifi")]); CJSON(aOtaEnabled, ota[F("aota")]); + releaseJSONBufferLock(); return true; } void serializeConfigSec() { DEBUG_PRINTLN(F("Writing settings to /wsec.json...")); + #ifdef WLED_USE_DYNAMIC_JSON DynamicJsonDocument doc(JSON_BUFFER_SIZE); + #else + if (!requestJSONBufferLock(4)) return; + #endif JsonObject nw = doc.createNestedObject("nw"); @@ -839,4 +864,5 @@ void serializeConfigSec() { File f = WLED_FS.open("/wsec.json", "w"); if (f) serializeJson(doc, f); f.close(); + releaseJSONBufferLock(); } diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 2677f61aa..424c99165 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -240,7 +240,7 @@ void colorRGBtoRGBW(byte* rgb) //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_M float low = minf(rgb[0],minf(rgb[1],rgb[2])); float high = maxf(rgb[0],maxf(rgb[1],rgb[2])); if (high < 0.1f) return; - float sat = 100.0f * ((high - low) / high);; // maximum saturation is 100 (corrected from 255) + float sat = 100.0f * ((high - low) / high); // maximum saturation is 100 (corrected from 255) rgb[3] = (byte)((255.0f - sat) / 255.0f * (rgb[0] + rgb[1] + rgb[2]) / 3); } */ diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 1ba20b331..af9e4a53f 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -211,6 +211,17 @@ void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w); void refreshNodeList(); void sendSysInfoUDP(); +//util.cpp +//bool oappend(const char* txt); // append new c string to temp buffer efficiently +//bool oappendi(int i); // append new number to temp buffer efficiently +//void sappend(char stype, const char* key, int val); +//void sappends(char stype, const char* key, char* val); +//void prepareHostname(char* hostname); +//void _setRandomColor(bool _sec, bool fromButton); +//bool isAsterisksOnly(const char* str, byte maxLen); +bool requestJSONBufferLock(uint8_t module=255); +void releaseJSONBufferLock(); + //um_manager.cpp class Usermod { public: diff --git a/wled00/ir.cpp b/wled00/ir.cpp index 2a04458bd..ec950db24 100644 --- a/wled00/ir.cpp +++ b/wled00/ir.cpp @@ -165,6 +165,7 @@ void decodeIR(uint32_t code) if (decodeIRCustom(code)) return; if (irEnabled == 8) { // any remote configurable with ir.json file decodeIRJson(code); + colorUpdated(CALL_MODE_BUTTON); return; } if (code > 0xFFFFFF) return; //invalid code @@ -566,25 +567,33 @@ Sample: void decodeIRJson(uint32_t code) { char objKey[10]; - const char* cmd; String cmdStr; - DynamicJsonDocument irDoc(JSON_BUFFER_SIZE); JsonObject fdo; JsonObject jsonCmdObj; - sprintf(objKey, "\"0x%X\":", code); + #ifdef WLED_USE_DYNAMIC_JSON + DynamicJsonDocument doc(JSON_BUFFER_SIZE); + #else + if (!requestJSONBufferLock(13)) return; + #endif - readObjectFromFile("/ir.json", objKey, &irDoc); - fdo = irDoc.as(); + sprintf_P(objKey, PSTR("\"0x%lX\":"), (unsigned long)code); + + // attempt to read command from ir.json + // this may fail for two reasons: ir.json does not exist or IR code not found + // if the IR code is not found readObjectFromFile() will clean() doc JSON document + // so we can differentiate between the two + readObjectFromFile("/ir.json", objKey, &doc); + fdo = doc.as(); lastValidCode = 0; if (fdo.isNull()) { //the received code does not exist if (!WLED_FS.exists("/ir.json")) errorFlag = ERR_FS_IRLOAD; //warn if IR file itself doesn't exist + releaseJSONBufferLock(); return; } - cmd = fdo["cmd"]; //string - cmdStr = String(cmd); + cmdStr = fdo["cmd"].as(); jsonCmdObj = fdo["cmd"]; //object if (!cmdStr.isEmpty()) @@ -617,16 +626,14 @@ void decodeIRJson(uint32_t code) if (!cmdStr.startsWith("win&")) { cmdStr = "win&" + cmdStr; } - handleSet(nullptr, cmdStr, false); + handleSet(nullptr, cmdStr, false); } colorUpdated(CALL_MODE_BUTTON); } else if (!jsonCmdObj.isNull()) { // command is JSON object - //allow applyPreset() to reuse JSON buffer, or it would alloc. a second buffer and run out of mem. - fileDoc = &irDoc; deserializeState(jsonCmdObj, CALL_MODE_BUTTON); - fileDoc = nullptr; } + releaseJSONBufferLock(); } void initIR() @@ -654,9 +661,8 @@ void handleIR() { if (results.value != 0) // only print results if anything is received ( != 0 ) { - Serial.print("IR recv\r\n0x"); - Serial.println((uint32_t)results.value, HEX); - Serial.println(); + if (!pinManager.isPinAllocated(1)) //GPIO 1 - Serial TX pin + Serial.printf_P(PSTR("IR recv: 0x%lX\n"), (unsigned long)results.value); } decodeIR(results.value); irrecv->resume(); diff --git a/wled00/json.cpp b/wled00/json.cpp index de55faf42..b489f0e3d 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -338,6 +338,8 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId) usermods.readFromJsonState(root); + loadLedmap = root[F("ledmap")] | loadLedmap; + byte ps = root[F("psave")]; if (ps > 0) { savePreset(ps, true, nullptr, root); @@ -832,23 +834,29 @@ void serveJson(AsyncWebServerRequest* request) return; } + #ifdef WLED_USE_DYNAMIC_JSON AsyncJsonResponse* response = new AsyncJsonResponse(JSON_BUFFER_SIZE); - JsonObject doc = response->getRoot(); + #else + if (!requestJSONBufferLock(17)) return; + AsyncJsonResponse *response = new AsyncJsonResponse(&doc); + #endif + + JsonObject lDoc = response->getRoot(); switch (subJson) { case 1: //state - serializeState(doc); break; + serializeState(lDoc); break; case 2: //info - serializeInfo(doc); break; + serializeInfo(lDoc); break; case 4: //node list - serializeNodes(doc); break; + serializeNodes(lDoc); break; case 5: //palettes - serializePalettes(doc, request); break; + serializePalettes(lDoc, request); break; default: //all - JsonObject state = doc.createNestedObject("state"); + JsonObject state = lDoc.createNestedObject("state"); serializeState(state); - JsonObject info = doc.createNestedObject("info"); + JsonObject info = lDoc.createNestedObject("info"); serializeInfo(info); if (subJson != 3) { @@ -858,10 +866,11 @@ void serveJson(AsyncWebServerRequest* request) } DEBUG_PRINT("JSON buffer size: "); - DEBUG_PRINTLN(doc.memoryUsage()); + DEBUG_PRINTLN(lDoc.memoryUsage()); response->setLength(); request->send(response); + releaseJSONBufferLock(); } #define MAX_LIVE_LEDS 180 diff --git a/wled00/mqtt.cpp b/wled00/mqtt.cpp index c5ece4646..2823eee4e 100644 --- a/wled00/mqtt.cpp +++ b/wled00/mqtt.cpp @@ -91,11 +91,14 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties colorUpdated(CALL_MODE_DIRECT_CHANGE); } else if (strcmp_P(topic, PSTR("/api")) == 0) { if (payload[0] == '{') { //JSON API + #ifdef WLED_USE_DYNAMIC_JSON DynamicJsonDocument doc(JSON_BUFFER_SIZE); + #else + if (!requestJSONBufferLock(15)) return; + #endif deserializeJson(doc, payloadStr); - fileDoc = &doc; deserializeState(doc.as()); - fileDoc = nullptr; + releaseJSONBufferLock(); } else { //HTTP API String apireq = "win&"; apireq += (char*)payloadStr; @@ -124,22 +127,22 @@ void publishMqtt() sprintf_P(s, PSTR("%u"), bri); strlcpy(subuf, mqttDeviceTopic, 33); strcat_P(subuf, PSTR("/g")); - mqtt->publish(subuf, 0, true, s); + mqtt->publish(subuf, 0, true, s); // retain message sprintf_P(s, PSTR("#%06X"), (col[3] << 24) | (col[0] << 16) | (col[1] << 8) | (col[2])); strlcpy(subuf, mqttDeviceTopic, 33); strcat_P(subuf, PSTR("/c")); - mqtt->publish(subuf, 0, true, s); + mqtt->publish(subuf, 0, true, s); // retain message strlcpy(subuf, mqttDeviceTopic, 33); strcat_P(subuf, PSTR("/status")); - mqtt->publish(subuf, 0, true, "online"); + mqtt->publish(subuf, 0, true, "online"); // retain message for a LWT - char apires[1024]; + char apires[1024]; // allocating 1024 bytes from stack can be risky XML_response(nullptr, apires); strlcpy(subuf, mqttDeviceTopic, 33); strcat_P(subuf, PSTR("/v")); - mqtt->publish(subuf, 0, false, apires); + mqtt->publish(subuf, 0, false, apires); // do not retain message } @@ -169,7 +172,7 @@ bool initMqtt() strlcpy(mqttStatusTopic, mqttDeviceTopic, 33); strcat_P(mqttStatusTopic, PSTR("/status")); - mqtt->setWill(mqttStatusTopic, 0, true, "offline"); + mqtt->setWill(mqttStatusTopic, 0, true, "offline"); // LWT message mqtt->setKeepAlive(MQTT_KEEP_ALIVE_TIME); mqtt->connect(); return true; diff --git a/wled00/presets.cpp b/wled00/presets.cpp index 8ffc96b10..2d783f1c5 100644 --- a/wled00/presets.cpp +++ b/wled00/presets.cpp @@ -7,8 +7,11 @@ bool applyPreset(byte index, byte callMode) { if (index == 0) return false; + + const char *filename = index < 255 ? "/presets.json" : "/tmp.json"; + if (fileDoc) { - errorFlag = readObjectFromFileUsingId("/presets.json", index, fileDoc) ? ERR_NONE : ERR_FS_PLOAD; + errorFlag = readObjectFromFileUsingId(filename, index, fileDoc) ? ERR_NONE : ERR_FS_PLOAD; JsonObject fdo = fileDoc->as(); if (fdo["ps"] == index) fdo.remove("ps"); //remove load request for same presets to prevent recursive crash #ifdef WLED_DEBUG_FS @@ -17,41 +20,53 @@ bool applyPreset(byte index, byte callMode) deserializeState(fdo, callMode, index); } else { DEBUGFS_PRINTLN(F("Make read buf")); - DynamicJsonDocument fDoc(JSON_BUFFER_SIZE); - errorFlag = readObjectFromFileUsingId("/presets.json", index, &fDoc) ? ERR_NONE : ERR_FS_PLOAD; - JsonObject fdo = fDoc.as(); + #ifdef WLED_USE_DYNAMIC_JSON + DynamicJsonDocument doc(JSON_BUFFER_SIZE); + #else + if (!requestJSONBufferLock(9)) return false; + #endif + errorFlag = readObjectFromFileUsingId(filename, index, &doc) ? ERR_NONE : ERR_FS_PLOAD; + JsonObject fdo = doc.as(); if (fdo["ps"] == index) fdo.remove("ps"); #ifdef WLED_DEBUG_FS - serializeJson(fDoc, Serial); + serializeJson(doc, Serial); #endif deserializeState(fdo, callMode, index); + releaseJSONBufferLock(); } if (!errorFlag) { - currentPreset = index; + if (index < 255) currentPreset = index; return true; } return false; } -//persist=false is not currently honored void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj) { - if (index == 0 || index > 250) return; - bool docAlloc = (fileDoc != nullptr); + if (index == 0 || (index > 250 && persist) || (index<255 && !persist)) return; JsonObject sObj = saveobj; - if (!docAlloc) { + const char *filename = persist ? "/presets.json" : "/tmp.json"; + + if (!fileDoc) { DEBUGFS_PRINTLN(F("Allocating saving buffer")); - DynamicJsonDocument lDoc(JSON_BUFFER_SIZE); - sObj = lDoc.to(); + #ifdef WLED_USE_DYNAMIC_JSON + DynamicJsonDocument doc(JSON_BUFFER_SIZE); + #else + if (!requestJSONBufferLock(10)) return; + #endif + sObj = doc.to(); if (pname) sObj["n"] = pname; + DEBUGFS_PRINTLN(F("Save current state")); serializeState(sObj, true); - currentPreset = index; + if (persist) currentPreset = index; - writeObjectToFileUsingId("/presets.json", index, &lDoc); - } else { //from JSON API + writeObjectToFileUsingId(filename, index, &doc); + + releaseJSONBufferLock(); + } else { //from JSON API (fileDoc != nullptr) DEBUGFS_PRINTLN(F("Reuse recv buffer")); sObj.remove(F("psave")); sObj.remove(F("v")); @@ -59,7 +74,7 @@ void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj) if (!sObj["o"]) { DEBUGFS_PRINTLN(F("Save current state")); serializeState(sObj, true, sObj["ib"], sObj["sb"]); - currentPreset = index; + if (persist) currentPreset = index; } sObj.remove("o"); sObj.remove("ib"); @@ -67,9 +82,9 @@ void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj) sObj.remove(F("error")); sObj.remove(F("time")); - writeObjectToFileUsingId("/presets.json", index, fileDoc); + writeObjectToFileUsingId(filename, index, fileDoc); } - presetsModifiedTime = toki.second(); //unix time + if (persist) presetsModifiedTime = toki.second(); //unix time updateFSInfo(); } diff --git a/wled00/set.cpp b/wled00/set.cpp index 21bf1356c..58a47ea7a 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -433,7 +433,12 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) //USERMODS if (subPage == 8) { + #ifdef WLED_USE_DYNAMIC_JSON DynamicJsonDocument doc(JSON_BUFFER_SIZE); + #else + if (!requestJSONBufferLock(5)) return; + #endif + JsonObject um = doc.createNestedObject("um"); size_t args = request->args(); @@ -508,6 +513,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) usermods.readFromConfig(um); // force change of usermod parameters } + releaseJSONBufferLock(); + if (subPage != 2 && (subPage != 6 || !doReboot)) serializeConfig(); //do not save if factory reset or LED settings (which are saved after LED re-init) if (subPage == 4) alexaInit(); } diff --git a/wled00/src/dependencies/json/AsyncJson-v6.h b/wled00/src/dependencies/json/AsyncJson-v6.h index c1288d91f..32ac54607 100644 --- a/wled00/src/dependencies/json/AsyncJson-v6.h +++ b/wled00/src/dependencies/json/AsyncJson-v6.h @@ -64,6 +64,15 @@ class AsyncJsonResponse: public AsyncAbstractResponse { public: + AsyncJsonResponse(JsonDocument *ref, bool isArray=false) : _jsonBuffer(1), _isValid{false} { + _code = 200; + _contentType = JSON_MIMETYPE; + if(isArray) + _root = ref->to(); + else + _root = ref->to(); + } + AsyncJsonResponse(size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE, bool isArray=false) : _jsonBuffer(maxJsonBufferSize), _isValid{false} { _code = 200; _contentType = JSON_MIMETYPE; @@ -84,7 +93,7 @@ class AsyncJsonResponse: public AsyncAbstractResponse { return _contentLength; } - size_t getSize() { return _jsonBuffer.size(); } + size_t getSize() { return _root.size(); } size_t _fillBuffer(uint8_t *data, size_t len){ ChunkPrint dest(data, _sentLength, len); diff --git a/wled00/util.cpp b/wled00/util.cpp new file mode 100644 index 000000000..5027e7ff8 --- /dev/null +++ b/wled00/util.cpp @@ -0,0 +1,33 @@ +#include "wled.h" +#include "fcn_declare.h" +#include "const.h" + +//threading/network callback details: https://github.com/Aircoookie/WLED/pull/2336#discussion_r762276994 +bool requestJSONBufferLock(uint8_t module) +{ + unsigned long now = millis(); + + while (jsonBufferLock && millis()-now < 1000) delay(1); // wait for a second for buffer lock + + if (millis()-now >= 1000) { + DEBUG_PRINT(F("ERROR: Locking JSON buffer failed! (")); + DEBUG_PRINT(jsonBufferLock); + DEBUG_PRINTLN(")"); + return false; // waiting time-outed + } + + jsonBufferLock = module ? module : 255; + fileDoc = &doc; // used for applying presets (presets.cpp) + doc.clear(); + return true; +} + + +void releaseJSONBufferLock() +{ + DEBUG_PRINT(F("JSON buffer released. (")); + DEBUG_PRINT(jsonBufferLock); + DEBUG_PRINTLN(")"); + fileDoc = nullptr; + jsonBufferLock = 0; +} diff --git a/wled00/wled.cpp b/wled00/wled.cpp index fe27166d2..4afdf1560 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -221,11 +221,16 @@ void WLED::loop() delete busConfigs[i]; busConfigs[i] = nullptr; } strip.finalizeInit(); + loadLedmap = 0; if (aligned) strip.makeAutoSegments(); else strip.fixInvalidSegments(); yield(); serializeConfig(); } + if (loadLedmap >= 0) { + strip.deserializeMap(loadLedmap); + loadLedmap = -1; + } yield(); handleWs(); @@ -351,7 +356,9 @@ void WLED::setup() #endif #ifdef WLED_ENABLE_ADALIGHT - if (!pinManager.isPinAllocated(3)) { + //Serial RX (Adalight, Improv, Serial JSON) only possible if GPIO3 unused + //Serial TX (Debug, Improv, Serial JSON) only possible if GPIO1 unused + if (!pinManager.isPinAllocated(3) && !pinManager.isPinAllocated(1)) { Serial.println(F("Ada")); } #endif @@ -407,6 +414,7 @@ void WLED::beginStrip() { // Initialize NeoPixel Strip and button strip.finalizeInit(); // busses created during deserializeConfig() + strip.deserializeMap(); strip.makeAutoSegments(); strip.setBrightness(0); strip.setShowCallback(handleOverlayDraw); diff --git a/wled00/wled.h b/wled00/wled.h index 6f4630a1f..f31607bb7 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -522,7 +522,7 @@ WLED_GLOBAL byte presetCycMax _INIT(5); // realtime WLED_GLOBAL byte realtimeMode _INIT(REALTIME_MODE_INACTIVE); WLED_GLOBAL byte realtimeOverride _INIT(REALTIME_OVERRIDE_NONE); -WLED_GLOBAL IPAddress realtimeIP _INIT_N(((0, 0, 0, 0)));; +WLED_GLOBAL IPAddress realtimeIP _INIT_N(((0, 0, 0, 0))); WLED_GLOBAL unsigned long realtimeTimeout _INIT(0); WLED_GLOBAL uint8_t tpmPacketCount _INIT(0); WLED_GLOBAL uint16_t tpmPayloadFrameSize _INIT(0); @@ -599,10 +599,17 @@ WLED_GLOBAL BusManager busses _INIT(BusManager()); WLED_GLOBAL WS2812FX strip _INIT(WS2812FX()); WLED_GLOBAL BusConfig* busConfigs[WLED_MAX_BUSSES] _INIT({nullptr}); //temporary, to remember values from network callback until after WLED_GLOBAL bool doInitBusses _INIT(false); +WLED_GLOBAL int8_t loadLedmap _INIT(-1); // Usermod manager WLED_GLOBAL UsermodManager usermods _INIT(UsermodManager()); +#ifndef WLED_USE_DYNAMIC_JSON +// global ArduinoJson buffer +WLED_GLOBAL StaticJsonDocument doc; +#endif +WLED_GLOBAL volatile uint8_t jsonBufferLock _INIT(0); + // enable additional debug output #ifdef WLED_DEBUG #ifndef ESP8266 diff --git a/wled00/wled_eeprom.cpp b/wled00/wled_eeprom.cpp index 55a79520a..dd9a5c95b 100644 --- a/wled00/wled_eeprom.cpp +++ b/wled00/wled_eeprom.cpp @@ -382,8 +382,13 @@ void deEEP() { DEBUG_PRINTLN(F("Preset file not found, attempting to load from EEPROM")); DEBUGFS_PRINTLN(F("Allocating saving buffer for dEEP")); - DynamicJsonDocument dDoc(JSON_BUFFER_SIZE *2); - JsonObject sObj = dDoc.to(); + #ifdef WLED_USE_DYNAMIC_JSON + DynamicJsonDocument doc(JSON_BUFFER_SIZE); + #else + if (!requestJSONBufferLock(8)) return; + #endif + + JsonObject sObj = doc.to(); sObj.createNestedObject("0"); EEPROM.begin(EEPSIZE); @@ -442,8 +447,6 @@ void deEEP() { } } - - for (uint16_t index = 1; index <= 16; index++) { //copy macros to presets.json char m[65]; readStringFromEEPROM(1024+64*(index-1), m, 64); @@ -463,10 +466,14 @@ void deEEP() { File f = WLED_FS.open("/presets.json", "w"); if (!f) { errorFlag = ERR_FS_GENERAL; + releaseJSONBufferLock(); return; } - serializeJson(dDoc, f); + serializeJson(doc, f); f.close(); + + releaseJSONBufferLock(); + DEBUG_PRINTLN(F("deEEP complete!")); } diff --git a/wled00/wled_serial.cpp b/wled00/wled_serial.cpp index 3d23d9284..0e531dbd7 100644 --- a/wled00/wled_serial.cpp +++ b/wled00/wled_serial.cpp @@ -48,18 +48,21 @@ void handleSerial() Serial.print("WLED"); Serial.write(' '); Serial.println(VERSION); } else if (next == '{') { //JSON API bool verboseResponse = false; - { - DynamicJsonDocument doc(JSON_BUFFER_SIZE); - Serial.setTimeout(100); - DeserializationError error = deserializeJson(doc, Serial); - if (error) return; - fileDoc = &doc; - verboseResponse = deserializeState(doc.as()); - fileDoc = nullptr; + #ifdef WLED_USE_DYNAMIC_JSON + DynamicJsonDocument doc(JSON_BUFFER_SIZE); + #else + if (!requestJSONBufferLock(16)) return; + #endif + Serial.setTimeout(100); + DeserializationError error = deserializeJson(doc, Serial); + if (error) { + releaseJSONBufferLock(); + return; } + verboseResponse = deserializeState(doc.as()); //only send response if TX pin is unused for other purposes if (verboseResponse && !pinManager.isPinAllocated(1)) { - DynamicJsonDocument doc(JSON_BUFFER_SIZE); + doc.clear(); JsonObject state = doc.createNestedObject("state"); serializeState(state); JsonObject info = doc.createNestedObject("info"); @@ -68,6 +71,7 @@ void handleSerial() serializeJson(doc, Serial); Serial.println(); } + releaseJSONBufferLock(); } break; case AdaState::Header_d: diff --git a/wled00/wled_server.cpp b/wled00/wled_server.cpp index 04147827c..adee38a9b 100644 --- a/wled00/wled_server.cpp +++ b/wled00/wled_server.cpp @@ -106,11 +106,18 @@ void initServer() bool verboseResponse = false; bool isConfig = false; { //scope JsonDocument so it releases its buffer - DynamicJsonDocument jsonBuffer(JSON_BUFFER_SIZE); - DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject)); - JsonObject root = jsonBuffer.as(); + #ifdef WLED_USE_DYNAMIC_JSON + DynamicJsonDocument doc(JSON_BUFFER_SIZE); + #else + if (!requestJSONBufferLock(14)) return; + #endif + + DeserializationError error = deserializeJson(doc, (uint8_t*)(request->_tempObject)); + JsonObject root = doc.as(); if (error || root.isNull()) { - request->send(400, "application/json", F("{\"error\":9}")); return; + releaseJSONBufferLock(); + request->send(400, "application/json", F("{\"error\":9}")); + return; } const String& url = request->url(); isConfig = url.indexOf("cfg") > -1; @@ -120,12 +127,11 @@ void initServer() serializeJson(root,Serial); DEBUG_PRINTLN(); #endif - fileDoc = &jsonBuffer; // used for applying presets (presets.cpp) verboseResponse = deserializeState(root); - fileDoc = nullptr; } else { verboseResponse = deserializeConfig(root); //use verboseResponse to determine whether cfg change should be saved immediately } + releaseJSONBufferLock(); } if (verboseResponse) { if (!isConfig) { diff --git a/wled00/ws.cpp b/wled00/ws.cpp index 8dc93ad96..f48c62c22 100644 --- a/wled00/ws.cpp +++ b/wled00/ws.cpp @@ -34,11 +34,18 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp } bool verboseResponse = false; { //scope JsonDocument so it releases its buffer - DynamicJsonDocument jsonBuffer(JSON_BUFFER_SIZE); - DeserializationError error = deserializeJson(jsonBuffer, data, len); - JsonObject root = jsonBuffer.as(); - if (error || root.isNull()) return; + #ifdef WLED_USE_DYNAMIC_JSON + DynamicJsonDocument doc(JSON_BUFFER_SIZE); + #else + if (!requestJSONBufferLock(11)) return; + #endif + DeserializationError error = deserializeJson(doc, data, len); + JsonObject root = doc.as(); + if (error || root.isNull()) { + releaseJSONBufferLock(); + return; + } if (root["v"] && root.size() == 1) { //if the received value is just "{"v":true}", send only to this client verboseResponse = true; @@ -46,14 +53,13 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp { wsLiveClientId = root["lv"] ? client->id() : 0; } else { - fileDoc = &jsonBuffer; verboseResponse = deserializeState(root); - fileDoc = nullptr; if (!interfaceUpdateCallMode) { //special case, only on playlist load, avoid sending twice in rapid succession if (millis() - lastInterfaceUpdate > 1700) verboseResponse = false; } } + releaseJSONBufferLock(); // will clean fileDoc } //update if it takes longer than 300ms until next "broadcast" if (verboseResponse && (millis() - lastInterfaceUpdate < 1700 || !interfaceUpdateCallMode)) sendDataWs(client); @@ -92,16 +98,23 @@ void sendDataWs(AsyncWebSocketClient * client) AsyncWebSocketMessageBuffer * buffer; { //scope JsonDocument so it releases its buffer + #ifdef WLED_USE_DYNAMIC_JSON DynamicJsonDocument doc(JSON_BUFFER_SIZE); + #else + if (!requestJSONBufferLock(12)) return; + #endif JsonObject state = doc.createNestedObject("state"); serializeState(state); JsonObject info = doc.createNestedObject("info"); serializeInfo(info); size_t len = measureJson(doc); buffer = ws.makeBuffer(len); - if (!buffer) return; //out of memory - + if (!buffer) { + releaseJSONBufferLock(); + return; //out of memory + } serializeJson(doc, (char *)buffer->get(), len +1); + releaseJSONBufferLock(); } if (client) { client->text(buffer); diff --git a/wled00/xml.cpp b/wled00/xml.cpp index 1a420a9dc..6e5295b3b 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -316,13 +316,22 @@ void getSettingsJS(byte subPage, char* dest) { char nS[8]; + // Pin reservations will become unnecessary when settings pages will read cfg.json directly // add reserved and usermod pins as d.um_p array oappend(SET_F("d.um_p=[6,7,8,9,10,11")); - DynamicJsonDocument doc(JSON_BUFFER_SIZE/2); + { // scope so buffer can be released earlier + #ifdef WLED_USE_DYNAMIC_JSON + DynamicJsonDocument doc(3072); + #else + if (!requestJSONBufferLock(6)) return; + #endif + JsonObject mods = doc.createNestedObject(F("um")); usermods.addToConfig(mods); if (!mods.isNull()) fillUMPins(mods); + releaseJSONBufferLock(); + } #ifdef WLED_ENABLE_DMX oappend(SET_F(",2")); // DMX hardcoded pin