diff --git a/platformio.ini b/platformio.ini index 700ec9d01..c4d999df4 100644 --- a/platformio.ini +++ b/platformio.ini @@ -27,11 +27,11 @@ default_envs = travis_esp8266, travis_esp32 ; default_envs = esp01_1m_ota ; default_envs = esp01_1m_full ; default_envs = esp07 -; default_envs = d1_mini +default_envs = d1_mini ; default_envs = heltec_wifi_kit_8 ; default_envs = h803wf ; default_envs = d1_mini_debug -; default_envs = d1_mini_ota +;default_envs = d1_mini_ota ; default_envs = esp32dev ; default_envs = esp8285_4CH_MagicHome ; default_envs = esp8285_4CH_H801 diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 0982fd62b..f95a3a2a2 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -46,6 +46,11 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, bool isArtnet); //file.cpp bool handleFileRead(AsyncWebServerRequest*, String path); +bool writeObjectToFileUsingId(const char* file, uint16_t id, JsonDocument* content); +bool writeObjectToFile(const char* file, const char* key, JsonDocument* content); +bool appendObjectToFile(const char* file, const char* key, JsonDocument* content, File input); +bool readObjectFromFileUsingId(const char* file, uint16_t id, JsonDocument* dest); +bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest); //hue.cpp void handleHue(); diff --git a/wled00/file.cpp b/wled00/file.cpp index eb0dcb931..7fcbc43cd 100644 --- a/wled00/file.cpp +++ b/wled00/file.cpp @@ -13,6 +13,192 @@ #include "SPIFFSEditor.h" #endif +#ifndef WLED_DISABLE_FILESYSTEM + +//find() that reads and buffers data from file stream in 256-byte blocks. +//Significantly faster, f.find(key) can take SECONDS for multi-kB files +bool bufferedFind(const char *target, File f) { + size_t targetLen = strlen(target); + Serial.println(target); + //Serial.println(f.position()); + size_t index = 0; + byte c; + uint16_t bufsize = 0, count = 0; + byte buf[256]; + + while (f.position() < f.size() -1) { + //c = f.read(); + //Serial.println(f.position()); + bufsize = f.read(buf, 256); + count = 0; + while (count < bufsize) { + if(buf[count] != target[index]) + index = 0; // reset index if any char does not match + + if(buf[count] == target[index]) { + if(++index >= targetLen) { // return true if all chars in the target match + f.seek((f.position() - bufsize) + count +1); + return true; + } + } + count++; + } + } + return false; +} + +//find empty spots in file stream in 256-byte blocks. +bool bufferedFindSpace(uint16_t targetLen, File f) { + size_t index = 0; + byte c; + uint16_t bufsize = 0, count = 0; + byte buf[256]; + + while (f.position() < f.size() -1) { + bufsize = f.read(buf, 256); + count = 0; + while (count < bufsize) { + if(buf[count] != ' ') + index = 0; // reset index if not space + + if(buf[count] == ' ') { + if(++index >= targetLen) { // return true if space long enough + f.seek(f.position() - targetLen); + return true; + } + } + count++; + } + } + return false; +} + +bool writeObjectToFileUsingId(const char* file, uint16_t id, JsonDocument* content) +{ + char objKey[10]; + sprintf(objKey, "\"%ld\":", id); + writeObjectToFile(file, objKey, content); +} + +bool writeObjectToFile(const char* file, const char* key, JsonDocument* content) +{ + uint32_t pos = 0; + File f = SPIFFS.open(file, "r+"); + if (!f) f = SPIFFS.open(file,"w"); + if (!f) return false; + //f.setTimeout(1); + f.seek(0, SeekSet); + + if (!bufferedFind(key, f)) //key does not exist in file + { + return appendObjectToFile(file, key, content, f); + } + + //exists + pos = f.position(); + //measure out end of old object + StaticJsonDocument<512> doc; + deserializeJson(doc, f); + uint32_t pos2 = f.position(); + uint32_t oldLen = pos2 - pos; + + if (!content->isNull() && measureJson(*content) <= oldLen) //replace + { + serializeJson(*content, f); + //pad rest + for (uint32_t i = f.position(); i < pos2; i++) { + f.write(' '); + } + } else { //delete + pos -+ strlen(key); + oldLen = pos2 - pos; + f.seek(pos, SeekSet); + for (uint32_t i = pos; i < pos2; i++) { + f.write(' '); + } + if (!content->isNull()) return appendObjectToFile(file, key, content, f); + } + f.close(); +} + +bool appendObjectToFile(const char* file, const char* key, JsonDocument* content, File input) +{ + Serial.println("Append"); + uint32_t pos = 0; + File f = (input) ? input : SPIFFS.open(file, "r+"); + if (!f) f = SPIFFS.open(file,"w"); + if (!f) return false; + if (f.size() < 3) f.print("{}"); + + //if there is enough empty space in file, insert there instead of appending + uint32_t contentLen = measureJson(*content); + Serial.print("clen"); Serial.println(contentLen); + if (bufferedFindSpace(contentLen, f)) { + Serial.println("space"); + serializeJson(*content, f); + return true; + } + + //check if last character in file is '}' (typical) + f.seek(1, SeekEnd); + if (f.read() == '}') pos = f.size() -1; + + if (pos == 0) //not found + { + Serial.println("not}"); + while (bufferedFind("}",f)) //find last closing bracket in JSON if not last char + { + pos = f.position(); + } + } + Serial.print("pos"); Serial.println(pos); + if (pos < 3) + { + f.seek(pos -1, SeekSet); + f.write(','); + } else { //file content is not valid JSON object + f.seek(0, SeekSet); + f.write('{'); //start JSON + } + + //f.print("\""); + f.print(key); + //f.print("\":"); + //Append object + serializeJson(*content, f); + + f.write('}'); + f.close(); +} + +bool readObjectFromFileUsingId(const char* file, uint16_t id, JsonDocument* dest) +{ + char objKey[10]; + sprintf(objKey, "\"%ld\":", id); + readObjectFromFile(file, objKey, dest); +} + +bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest) +{ + //if (id == playlistId) return true; + //playlist is already loaded, but we can't be sure that file hasn't changed since loading + + File f = SPIFFS.open(file, "r"); + if (!f) return false; + //f.setTimeout(0); + //Serial.println(key); + if (!bufferedFind(key, f)) //key does not exist in file + { + f.close(); + return false; + } + + deserializeJson(*dest, f); + + f.close(); + return true; +} +#endif #if !defined WLED_DISABLE_FILESYSTEM && defined WLED_ENABLE_FS_SERVING //Un-comment any file types you need @@ -38,11 +224,11 @@ bool handleFileRead(AsyncWebServerRequest* request, String path){ DEBUG_PRINTLN("FileRead: " + path); if(path.endsWith("/")) path += "index.htm"; String contentType = getContentType(request, path); - String pathWithGz = path + ".gz"; + /*String pathWithGz = path + ".gz"; if(SPIFFS.exists(pathWithGz)){ request->send(SPIFFS, pathWithGz, contentType); return true; - } + }*/ if(SPIFFS.exists(path)) { request->send(SPIFFS, path, contentType); return true; diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 88b387492..fab1df7fe 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -85,6 +85,14 @@ void WLED::loop() handleHue(); handleBlynk(); + if (presetToApply) { + StaticJsonDocument<1024> temp; + errorFlag = !readObjectFromFileUsingId("/presets.json", presetToApply, &temp); + serializeJson(temp, Serial); + deserializeState(temp.as()); + presetToApply = 0; + } + yield(); if (!offMode) strip.service(); diff --git a/wled00/wled.h b/wled00/wled.h index f1245f493..4f67d8654 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -34,9 +34,9 @@ #define WLED_ENABLE_WEBSOCKETS #endif -#define WLED_DISABLE_FILESYSTEM // SPIFFS is not used by any WLED feature yet -//#define WLED_ENABLE_FS_SERVING // Enable sending html file from SPIFFS before serving progmem version -//#define WLED_ENABLE_FS_EDITOR // enable /edit page for editing SPIFFS content. Will also be disabled with OTA lock +//#define WLED_DISABLE_FILESYSTEM // SPIFFS is not used by any WLED feature yet +#define WLED_ENABLE_FS_SERVING // Enable sending html file from SPIFFS before serving progmem version +#define WLED_ENABLE_FS_EDITOR // enable /edit page for editing SPIFFS content. Will also be disabled with OTA lock // to toggle usb serial debug (un)comment the following line //#define WLED_DEBUG @@ -167,6 +167,8 @@ WLED_GLOBAL char otaPass[33] _INIT(DEFAULT_OTA_PASS); // Hardware CONFIG (only changeble HERE, not at runtime) // LED strip pin, button pin and IR pin changeable in NpbWrapper.h! +WLED_GLOBAL byte presetToApply _INIT(0); + WLED_GLOBAL byte auxDefaultState _INIT(0); // 0: input 1: high 2: low WLED_GLOBAL byte auxTriggeredState _INIT(0); // 0: input 1: high 2: low WLED_GLOBAL char ntpServerName[33] _INIT("0.wled.pool.ntp.org"); // NTP server to use diff --git a/wled00/wled_eeprom.cpp b/wled00/wled_eeprom.cpp index 00c36c279..6f9108fd1 100644 --- a/wled00/wled_eeprom.cpp +++ b/wled00/wled_eeprom.cpp @@ -621,6 +621,12 @@ void savedToPresets() bool applyPreset(byte index, bool loadBri) { + StaticJsonDocument<1024> temp; + errorFlag = !readObjectFromFileUsingId("/presets.json", index, &temp); + serializeJson(temp, Serial); + deserializeState(temp.as()); + //presetToApply = index; + return true; if (index == 255 || index == 0) { loadSettingsFromEEPROM(false);//load boot defaults @@ -668,6 +674,12 @@ bool applyPreset(byte index, bool loadBri) void savePreset(byte index, bool persist) { + StaticJsonDocument<1024> doc; + serializeState(doc.to()); + doc["p"]=50; + serializeJson(doc, Serial); + writeObjectToFileUsingId("/presets.json", index, &doc); + return; if (index > 16) return; if (index < 1) {saveSettingsToEEPROM();return;} uint16_t i = 380 + index*20;//min400