diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 345ba64d4..e7ae481ed 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); + jsonBufferLock = false; + + if (needsSave) serializeConfig(); // usermods required new prameters } void serializeConfig() { @@ -455,7 +462,10 @@ void serializeConfig() { DEBUG_PRINTLN(F("Writing settings to /cfg.json...")); - DynamicJsonDocument doc(JSON_BUFFER_SIZE); + //DynamicJsonDocument doc(JSON_BUFFER_SIZE); + while (jsonBufferLock) delay(1); + jsonBufferLock = true; + doc.clear(); JsonArray rev = doc.createNestedArray("rev"); rev.add(1); //major settings revision @@ -762,16 +772,23 @@ void serializeConfig() { File f = WLED_FS.open("/cfg.json", "w"); if (f) serializeJson(doc, f); f.close(); + jsonBufferLock = false; } //settings in /wsec.json, not accessible via webserver, for passwords and tokens bool deserializeConfigSec() { DEBUG_PRINTLN(F("Reading settings from /wsec.json...")); - DynamicJsonDocument doc(JSON_BUFFER_SIZE); + //DynamicJsonDocument doc(JSON_BUFFER_SIZE); + while (jsonBufferLock) delay(1); + jsonBufferLock = true; + doc.clear(); bool success = readObjectFromFile("/wsec.json", nullptr, &doc); - if (!success) return false; + if (!success) { + jsonBufferLock = false; + return false; + } JsonObject nw_ins_0 = doc["nw"]["ins"][0]; getStringFromJson(clientPass, nw_ins_0["psk"], 65); @@ -803,13 +820,17 @@ bool deserializeConfigSec() { CJSON(wifiLock, ota[F("lock-wifi")]); CJSON(aOtaEnabled, ota[F("aota")]); + jsonBufferLock = false; return true; } void serializeConfigSec() { DEBUG_PRINTLN(F("Writing settings to /wsec.json...")); - DynamicJsonDocument doc(JSON_BUFFER_SIZE); + //DynamicJsonDocument doc(JSON_BUFFER_SIZE); + while (jsonBufferLock) delay(1); + jsonBufferLock = true; + doc.clear(); JsonObject nw = doc.createNestedObject("nw"); @@ -844,4 +865,5 @@ void serializeConfigSec() { File f = WLED_FS.open("/wsec.json", "w"); if (f) serializeJson(doc, f); f.close(); + jsonBufferLock = false; } diff --git a/wled00/ir.cpp b/wled00/ir.cpp index 49a72356f..d0ff0c390 100644 --- a/wled00/ir.cpp +++ b/wled00/ir.cpp @@ -572,18 +572,22 @@ void decodeIRJson(uint32_t code) { char objKey[10]; String cmdStr; - DynamicJsonDocument irDoc(JSON_BUFFER_SIZE); JsonObject fdo; JsonObject jsonCmdObj; + //DynamicJsonDocument doc(JSON_BUFFER_SIZE); + while (jsonBufferLock) delay(1); + jsonBufferLock = true; + doc.clear(); + 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() irDoc JSON document + // if the IR code is not found readObjectFromFile() will clean() doc JSON document // so we can differentiate between the two - readObjectFromFile("/ir.json", objKey, &irDoc); - fdo = irDoc.as(); + readObjectFromFile("/ir.json", objKey, &doc); + fdo = doc.as(); lastValidCode = 0; if (fdo.isNull()) { //the received code does not exist @@ -630,10 +634,11 @@ void decodeIRJson(uint32_t code) } 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; + fileDoc = &doc; deserializeState(jsonCmdObj, CALL_MODE_BUTTON); fileDoc = nullptr; } + jsonBufferLock = false; } void initIR() diff --git a/wled00/json.cpp b/wled00/json.cpp index 67d368bcb..8349a8960 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -204,13 +204,13 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId) } strip.setPixelSegment(255); strip.trigger(); -// this is now handled using the "frz" toggle. } else if (!elem["frz"] && iarr.isNull()) { //return to regular effect seg.setOption(SEG_OPTION_FREEZE, false); } return; // seg.differs(prev); } +// deserializes WLED state (fileDoc points to doc object if called from web server) bool deserializeState(JsonObject root, byte callMode, byte presetId) { DEBUG_PRINTLN(F("Deserializing state")); @@ -324,7 +324,8 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId) int8_t ledmap = root[F("ledmap")] | -1; if (ledmap >= 0) { - strip.deserializeMap(ledmap); + //strip.deserializeMap(ledmap); // requires separate JSON buffer + loadLedmap = ledmap; } int ps = root[F("psave")] | -1; diff --git a/wled00/mqtt.cpp b/wled00/mqtt.cpp index 40816ef41..1b18fdc50 100644 --- a/wled00/mqtt.cpp +++ b/wled00/mqtt.cpp @@ -91,11 +91,15 @@ 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 - DynamicJsonDocument doc(JSON_BUFFER_SIZE); + //DynamicJsonDocument doc(JSON_BUFFER_SIZE); + while (jsonBufferLock) delay(1); + jsonBufferLock = true; + doc.clear(); deserializeJson(doc, payloadStr); fileDoc = &doc; deserializeState(doc.as()); fileDoc = nullptr; + jsonBufferLock = false; } else { //HTTP API String apireq = "win&"; apireq += (char*)payloadStr; diff --git a/wled00/presets.cpp b/wled00/presets.cpp index 7ba2ea849..a7f286526 100644 --- a/wled00/presets.cpp +++ b/wled00/presets.cpp @@ -20,14 +20,18 @@ bool applyPreset(byte index, byte callMode) deserializeState(fdo, callMode, index); } else { DEBUGFS_PRINTLN(F("Make read buf")); - DynamicJsonDocument fDoc(JSON_BUFFER_SIZE); - errorFlag = readObjectFromFileUsingId(filename, index, &fDoc) ? ERR_NONE : ERR_FS_PLOAD; - JsonObject fdo = fDoc.as(); + //DynamicJsonDocument fDoc(JSON_BUFFER_SIZE); + while (jsonBufferLock) delay(1); + jsonBufferLock = true; + doc.clear(); + 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); + jsonBufferLock = false; } if (!errorFlag) { @@ -40,22 +44,27 @@ bool applyPreset(byte index, byte callMode) void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj) { if (index == 0 || (index > 250 && persist) || (index<255 && !persist)) return; - bool docAlloc = (fileDoc != nullptr); JsonObject sObj = saveobj; const char *filename = persist ? "/presets.json" : "/tmp.json"; - if (!docAlloc) { + if (!fileDoc) { DEBUGFS_PRINTLN(F("Allocating saving buffer")); - DynamicJsonDocument lDoc(JSON_BUFFER_SIZE); - sObj = lDoc.to(); + //DynamicJsonDocument lDoc(JSON_BUFFER_SIZE); + while (jsonBufferLock) delay(1); + jsonBufferLock = true; + doc.clear(); + sObj = doc.to(); if (pname) sObj["n"] = pname; + DEBUGFS_PRINTLN(F("Save current state")); serializeState(sObj, true); if (persist) currentPreset = index; - writeObjectToFileUsingId(filename, index, &lDoc); - } else { //from JSON API + writeObjectToFileUsingId(filename, index, &doc); + + jsonBufferLock = false; + } else { //from JSON API (fileDoc != nullptr) DEBUGFS_PRINTLN(F("Reuse recv buffer")); sObj.remove(F("psave")); sObj.remove(F("v")); diff --git a/wled00/set.cpp b/wled00/set.cpp index 7f30c4823..4e2e5fb2e 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -415,7 +415,11 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) //USERMODS if (subPage == 8) { - DynamicJsonDocument doc(JSON_BUFFER_SIZE); + //DynamicJsonDocument doc(JSON_BUFFER_SIZE); + while (jsonBufferLock) delay(1); + jsonBufferLock = true; + doc.clear(); + JsonObject um = doc.createNestedObject("um"); size_t args = request->args(); @@ -490,6 +494,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) usermods.readFromConfig(um); // force change of usermod parameters } + jsonBufferLock = false; + 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/wled.cpp b/wled00/wled.cpp index 2cb1d39f2..d49ee8bd0 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -150,11 +150,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(); @@ -339,6 +344,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 e2bda5bf3..c97841a6e 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -598,10 +598,15 @@ 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()); +// global ArduinoJson buffer +WLED_GLOBAL StaticJsonDocument doc; +WLED_GLOBAL volatile bool jsonBufferLock _INIT(false); + // enable additional debug output #ifdef WLED_DEBUG #ifndef ESP8266 diff --git a/wled00/wled_eeprom.cpp b/wled00/wled_eeprom.cpp index 38369a912..f45373d70 100644 --- a/wled00/wled_eeprom.cpp +++ b/wled00/wled_eeprom.cpp @@ -373,8 +373,12 @@ 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(); + //DynamicJsonDocument doc(JSON_BUFFER_SIZE *2); + while (jsonBufferLock) delay(1); + jsonBufferLock = true; + doc.clear(); + + JsonObject sObj = doc.to(); sObj.createNestedObject("0"); EEPROM.begin(EEPSIZE); @@ -433,8 +437,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); @@ -456,8 +458,11 @@ void deEEP() { errorFlag = ERR_FS_GENERAL; return; } - serializeJson(dDoc, f); + serializeJson(doc, f); f.close(); + + jsonBufferLock = false; + DEBUG_PRINTLN(F("deEEP complete!")); } diff --git a/wled00/wled_serial.cpp b/wled00/wled_serial.cpp index 0d3028278..4c40c901b 100644 --- a/wled00/wled_serial.cpp +++ b/wled00/wled_serial.cpp @@ -43,18 +43,25 @@ void handleSerial() } else if (next == '{') { //JSON API bool verboseResponse = false; + while (jsonBufferLock) delay(1); + jsonBufferLock = true; + doc.clear(); { - DynamicJsonDocument doc(JSON_BUFFER_SIZE); + //DynamicJsonDocument doc(JSON_BUFFER_SIZE); Serial.setTimeout(100); DeserializationError error = deserializeJson(doc, Serial); - if (error) return; + if (error) { + jsonBufferLock = false; + return; + } fileDoc = &doc; verboseResponse = deserializeState(doc.as()); fileDoc = nullptr; } //only send response if TX pin is unused for other purposes if (verboseResponse && !pinManager.isPinAllocated(1)) { - DynamicJsonDocument doc(JSON_BUFFER_SIZE); + //DynamicJsonDocument doc(JSON_BUFFER_SIZE); + doc.clear(); JsonObject state = doc.createNestedObject("state"); serializeState(state); JsonObject info = doc.createNestedObject("info"); @@ -62,6 +69,7 @@ void handleSerial() serializeJson(doc, Serial); } + jsonBufferLock = false; } break; case AdaState::Header_d: diff --git a/wled00/wled_server.cpp b/wled00/wled_server.cpp index e35e498db..502d28737 100644 --- a/wled00/wled_server.cpp +++ b/wled00/wled_server.cpp @@ -110,9 +110,13 @@ 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(); + //DynamicJsonDocument jsonBuffer(JSON_BUFFER_SIZE); + while (jsonBufferLock) delay(1); + jsonBufferLock = true; + doc.clear(); + + 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; } @@ -124,12 +128,13 @@ void initServer() serializeJson(root,Serial); DEBUG_PRINTLN(); #endif - fileDoc = &jsonBuffer; // used for applying presets (presets.cpp) + fileDoc = &doc; // 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 } + jsonBufferLock = false; } if (verboseResponse) { if (!isConfig) { diff --git a/wled00/ws.cpp b/wled00/ws.cpp index 7f2dc9c58..31510d35b 100644 --- a/wled00/ws.cpp +++ b/wled00/ws.cpp @@ -36,16 +36,24 @@ 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; + //DynamicJsonDocument doc(JSON_BUFFER_SIZE); + while (jsonBufferLock) delay(1); + jsonBufferLock = true; + doc.clear(); + DeserializationError error = deserializeJson(doc, data, len); + JsonObject root = doc.as(); + if (error || root.isNull()) { + jsonBufferLock = false; + return; + } + /* #ifdef WLED_DEBUG DEBUG_PRINT(F("Incoming WS: ")); serializeJson(root,Serial); DEBUG_PRINTLN(); #endif + */ if (root["v"] && root.size() == 1) { //if the received value is just "{"v":true}", send only to this client verboseResponse = true; @@ -53,7 +61,7 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp { wsLiveClientId = root["lv"] ? client->id() : 0; } else { - fileDoc = &jsonBuffer; + fileDoc = &doc; verboseResponse = deserializeState(root); fileDoc = nullptr; if (!interfaceUpdateCallMode) { @@ -61,6 +69,7 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp if (millis() - lastInterfaceUpdate > 1700) verboseResponse = false; } } + jsonBufferLock = false; } //update if it takes longer than 300ms until next "broadcast" if (verboseResponse && (millis() - lastInterfaceUpdate < 1700 || !interfaceUpdateCallMode)) sendDataWs(client); @@ -102,14 +111,20 @@ void sendDataWs(AsyncWebSocketClient * client) AsyncWebSocketMessageBuffer * buffer; { //scope JsonDocument so it releases its buffer - DynamicJsonDocument doc(JSON_BUFFER_SIZE); + //DynamicJsonDocument doc(JSON_BUFFER_SIZE); + while (jsonBufferLock) delay(1); + jsonBufferLock = true; + doc.clear(); 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) { + jsonBufferLock = false; + return; //out of memory + } /* #ifdef WLED_DEBUG DEBUG_PRINT(F("Outgoing WS: ")); @@ -118,6 +133,7 @@ void sendDataWs(AsyncWebSocketClient * client) #endif */ serializeJson(doc, (char *)buffer->get(), len +1); + jsonBufferLock = false; } DEBUG_PRINT(F("Sending WS data ")); if (client) { diff --git a/wled00/xml.cpp b/wled00/xml.cpp index 79d8fa7db..28fbf232f 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -249,13 +249,18 @@ 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); + //DynamicJsonDocument doc(JSON_BUFFER_SIZE/2); + while (jsonBufferLock) delay(1); + jsonBufferLock = true; + doc.clear(); JsonObject mods = doc.createNestedObject(F("um")); usermods.addToConfig(mods); if (!mods.isNull()) fillUMPins(mods); + jsonBufferLock = false; #ifdef WLED_ENABLE_DMX oappend(SET_F(",2")); // DMX hardcoded pin