diff --git a/ESP32_AP-Flasher/data/www/index.html b/ESP32_AP-Flasher/data/www/index.html index e602545b..4837e20b 100644 --- a/ESP32_AP-Flasher/data/www/index.html +++ b/ESP32_AP-Flasher/data/www/index.html @@ -160,7 +160,7 @@ Latency will be around 40 seconds.">
loading
AP config
-
edit littleFS
+
edit contentFS
diff --git a/ESP32_AP-Flasher/esp32_sdcard.csv b/ESP32_AP-Flasher/esp32_sdcard.csv new file mode 100644 index 00000000..f3bcf0db --- /dev/null +++ b/ESP32_AP-Flasher/esp32_sdcard.csv @@ -0,0 +1,7 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x150000, +app1, app, ota_1, 0x160000,0x150000, +spiffs, data, spiffs, 0x2B0000,0x140000, +coredump, data, coredump,0x3F0000,0x10000, \ No newline at end of file diff --git a/ESP32_AP-Flasher/include/storage.h b/ESP32_AP-Flasher/include/storage.h new file mode 100644 index 00000000..a55dab09 --- /dev/null +++ b/ESP32_AP-Flasher/include/storage.h @@ -0,0 +1,37 @@ +#ifndef _DYN_STORAGE_H_ +#define _DYN_STORAGE_H_ + +#include "FS.h" + +#ifdef HAS_SDCARD +#ifndef SD_CARD_SS +#error SD_CARD_SS UNDEFINED +#endif + +#ifndef SD_CARD_CLK +#define SD_CARD_CLK 18 +#endif + +#ifndef SD_CARD_MISO +#define SD_CARD_MISO 19 +#endif + +#ifndef SD_CARD_MOSI +#define SD_CARD_MOSI 23 +#endif +#endif + +class DynStorage { + public: + DynStorage(); + void begin(); + void listFiles(); + + private: + bool isInited; +}; + +extern DynStorage Storage; +extern fs::FS *contentFS; + +#endif \ No newline at end of file diff --git a/ESP32_AP-Flasher/src/contentmanager.cpp b/ESP32_AP-Flasher/src/contentmanager.cpp index 6be2ae98..7d886afb 100644 --- a/ESP32_AP-Flasher/src/contentmanager.cpp +++ b/ESP32_AP-Flasher/src/contentmanager.cpp @@ -13,7 +13,7 @@ #ifdef CONTENT_RSS #include #endif -#include +#include "storage.h" #include #include @@ -143,7 +143,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { if (imageParams.hasRed) imageParams.dataType = DATATYPE_IMG_RAW_2BPP; if (prepareDataAvail(&filename, imageParams.dataType, mac, cfgobj["timetolive"].as())) { cfgobj["#fetched"] = true; - if (cfgobj["delete"].as() == "1") LittleFS.remove("/" + cfgobj["filename"].as()); + if (cfgobj["delete"].as() == "1") contentFS->remove("/" + cfgobj["filename"].as()); } else { wsErr("Error accessing " + filename); } @@ -323,7 +323,7 @@ void drawString(TFT_eSprite &spr, String content, uint16_t posx, uint16_t posy, spr.setTextColor(PAL_BLACK, PAL_WHITE); spr.drawString(content, posx, posy); } else { - if (font != "") spr.loadFont(font, LittleFS); + if (font != "") spr.loadFont(font, *contentFS); spr.setTextColor(color, PAL_WHITE); spr.drawString(content, posx, posy); if (font != "") spr.unloadFont(); @@ -420,7 +420,7 @@ void drawNumber(String &filename, int32_t count, int32_t thresholdred, tagRecord if (count > 99) font = loc["fonts"][1].as(); if (count > 999) font = loc["fonts"][2].as(); if (count > 9999) font = loc["fonts"][3].as(); - spr.loadFont(font, LittleFS); + spr.loadFont(font, *contentFS); spr.drawString(String(count), loc["xy"][0].as(), loc["xy"][1].as()); spr.unloadFont(); @@ -505,7 +505,7 @@ void drawWeather(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgP dtostrf(temperature, 2, 1, tmpOutput); drawString(spr, String(tmpOutput), loc["temp"][0], loc["temp"][1], loc["temp"][2], TL_DATUM, (temperature < 0 ? PAL_RED : PAL_BLACK)); - spr.loadFont(loc["icon"][2], LittleFS); + spr.loadFont(loc["icon"][2], *contentFS); if (weathercode == 55 || weathercode == 65 || weathercode == 75 || weathercode == 82 || weathercode == 86 || weathercode == 95 || weathercode == 99) { spr.setTextColor(PAL_RED, PAL_WHITE); } else { @@ -515,7 +515,7 @@ void drawWeather(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgP spr.printToSprite(weatherIcons[weathercode]); spr.unloadFont(); - spr.loadFont(loc["dir"][2], LittleFS); + spr.loadFont(loc["dir"][2], *contentFS); spr.setTextColor(PAL_BLACK, PAL_WHITE); spr.setCursor(loc["dir"][0], loc["dir"][1]); spr.printToSprite(windDirectionIcon(winddirection)); @@ -578,7 +578,7 @@ void drawForecast(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, img uint8_t weathercode = doc["daily"]["weathercode"][dag].as(); if (weathercode > 40) weathercode -= 40; - spr.loadFont(loc["icon"][2], LittleFS); + spr.loadFont(loc["icon"][2], *contentFS); if (weathercode == 55 || weathercode == 65 || weathercode == 75 || weathercode == 82 || weathercode == 86 || weathercode == 95 || weathercode == 99) { spr.setTextColor(PAL_RED, PAL_WHITE); } else { @@ -597,7 +597,7 @@ void drawForecast(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, img int8_t tmax = round(doc["daily"]["temperature_2m_max"][dag].as()); uint8_t wind = windSpeedToBeaufort(doc["daily"]["windspeed_10m_max"][dag].as()); - spr.loadFont(loc["day"][2], LittleFS); + spr.loadFont(loc["day"][2], *contentFS); if (loc["rain"]) { int8_t rain = round(doc["daily"]["precipitation_sum"][dag].as()); @@ -624,7 +624,7 @@ void drawForecast(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, img int getImgURL(String &filename, String URL, time_t fetched, imgParam &imageParams, String MAC) { // https://images.klari.net/kat-bw29.jpg - LittleFS.begin(); + Storage.begin(); Serial.println("get external " + URL); HTTPClient http; @@ -634,7 +634,7 @@ int getImgURL(String &filename, String URL, time_t fetched, imgParam &imageParam http.setTimeout(5000); // timeout in ms int httpCode = http.GET(); if (httpCode == 200) { - File f = LittleFS.open("/temp/temp.jpg", "w"); + File f = contentFS->open("/temp/temp.jpg", "w"); if (f) { http.writeToStream(&f); f.close(); @@ -796,7 +796,7 @@ void drawQR(String &filename, String qrcontent, String title, tagRecord *&taginf #ifdef CONTENT_QR TFT_eSPI tft = TFT_eSPI(); TFT_eSprite spr = TFT_eSprite(&tft); - LittleFS.begin(); + Storage.begin(); const char *text = qrcontent.c_str(); QRCode qrcode; @@ -1031,7 +1031,7 @@ void prepareConfigFile(uint8_t *dst, JsonObject config) { } void getTemplate(JsonDocument &json, const char *filePath, uint8_t id, uint8_t hwtype) { - File jsonFile = LittleFS.open(filePath, "r"); + File jsonFile = contentFS->open(filePath, "r"); if (!jsonFile) { Serial.println("Failed to open content template file " + String(filePath)); return; diff --git a/ESP32_AP-Flasher/src/flasher.cpp b/ESP32_AP-Flasher/src/flasher.cpp index 4f9b3964..f1764caa 100644 --- a/ESP32_AP-Flasher/src/flasher.cpp +++ b/ESP32_AP-Flasher/src/flasher.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include "storage.h" #include // #include @@ -116,7 +116,6 @@ flasher::~flasher() { bool flasher::connectTag(uint8_t port) { bool result; - switch (port) { case 0: result = zbs->begin(FLASHER_AP_SS, FLASHER_AP_CLK, FLASHER_AP_MOSI, FLASHER_AP_MISO, FLASHER_AP_RESET, (uint8_t *)powerPinsAP, sizeof(powerPinsAP), 8000000); @@ -206,7 +205,7 @@ bool flasher::getInfoBlockType() { bool flasher::findTagByMD5() { StaticJsonDocument<3000> doc; DynamicJsonDocument APconfig(600); - fs::File readfile = LittleFS.open("/tag_md5_db.json", "r"); + fs::File readfile = contentFS->open("/tag_md5_db.json", "r"); DeserializationError err = deserializeJson(doc, readfile); if (!err) { for (JsonObject elem : doc.as()) { @@ -236,7 +235,7 @@ bool flasher::findTagByMD5() { bool flasher::findTagByType(uint8_t type) { StaticJsonDocument<3000> doc; DynamicJsonDocument APconfig(600); - fs::File readfile = LittleFS.open("/tag_md5_db.json", "r"); + fs::File readfile = contentFS->open("/tag_md5_db.json", "r"); DeserializationError err = deserializeJson(doc, readfile); if (!err) { for (JsonObject elem : doc.as()) { @@ -300,7 +299,7 @@ bool flasher::backupFlash() { getFirmwareMD5(); if (!zbs->select_flash(0)) return false; md5char[16] = 0x00; - fs::File backup = LittleFS.open("/" + (String)md5char + "_backup.bin", "w", true); + fs::File backup = contentFS->open("/" + (String)md5char + "_backup.bin", "w", true); for (uint32_t c = 0; c < 65535; c++) { backup.write(zbs->read_flash(c)); } @@ -474,7 +473,7 @@ bool flasher::writeFlashFromPackOffset(fs::File *file, uint16_t length) { bool flasher::writeFlashFromPack(String filename, uint8_t type) { StaticJsonDocument<512> doc; DynamicJsonDocument APconfig(512); - fs::File readfile = LittleFS.open(filename, "r"); + fs::File readfile = contentFS->open(filename, "r"); DeserializationError err = deserializeJson(doc, readfile); if (!err) { for (JsonObject elem : doc.as()) { @@ -505,7 +504,7 @@ bool flasher::writeFlashFromPack(String filename, uint8_t type) { uint16_t getAPUpdateVersion(uint8_t type) { StaticJsonDocument<512> doc; DynamicJsonDocument APconfig(512); - fs::File readfile = LittleFS.open("/AP_FW_Pack.bin", "r"); + fs::File readfile = contentFS->open("/AP_FW_Pack.bin", "r"); DeserializationError err = deserializeJson(doc, readfile); if (!err) { for (JsonObject elem : doc.as()) { @@ -529,7 +528,7 @@ uint16_t getAPUpdateVersion(uint8_t type) { } bool checkForcedAPFlash() { - return LittleFS.exists("/AP_force_flash.bin"); + return contentFS->exists("/AP_force_flash.bin"); } bool doForcedAPFlash() { @@ -547,14 +546,14 @@ bool doForcedAPFlash() { f->writeInfoBlock(); } - fs::File readfile = LittleFS.open("/AP_force_flash.bin", "r"); + fs::File readfile = contentFS->open("/AP_force_flash.bin", "r"); bool res = f->writeFlashFromPackOffset(&readfile, readfile.size()); #ifdef HAS_RGB_LED if (res) addFadeColor(CRGB::Green); if (!res) addFadeColor(CRGB::Red); #endif readfile.close(); - if (res) LittleFS.remove("/AP_force_flash.bin"); + if (res) contentFS->remove("/AP_force_flash.bin"); f->zbs->reset(); delete f; return res; diff --git a/ESP32_AP-Flasher/src/main.cpp b/ESP32_AP-Flasher/src/main.cpp index 20c8cc26..9fcd7fe6 100644 --- a/ESP32_AP-Flasher/src/main.cpp +++ b/ESP32_AP-Flasher/src/main.cpp @@ -1,8 +1,10 @@ + #include #include #include #include +#include "storage.h" #include "contentmanager.h" #include "flasher.h" #include "makeimage.h" @@ -84,6 +86,8 @@ void setup() { heap_caps_malloc_extmem_enable(64); #endif + Storage.begin(); + /* Serial.println("\n\n##################################"); Serial.printf("Internal Total heap %d, internal Free Heap %d\n", ESP.getHeapSize(), ESP.getFreeHeap()); diff --git a/ESP32_AP-Flasher/src/makeimage.cpp b/ESP32_AP-Flasher/src/makeimage.cpp index 2cb090d0..1cc77192 100644 --- a/ESP32_AP-Flasher/src/makeimage.cpp +++ b/ESP32_AP-Flasher/src/makeimage.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include "storage.h" #include #include #include @@ -15,7 +15,7 @@ bool spr_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *bitmap) } void jpg2buffer(String filein, String fileout, imgParam &imageParams) { - LittleFS.begin(); + Storage.begin(); TJpgDec.setSwapBytes(true); TJpgDec.setJpgScale(1); TJpgDec.setCallback(spr_output); @@ -23,7 +23,7 @@ void jpg2buffer(String filein, String fileout, imgParam &imageParams) { if (filein.c_str()[0] != '/') { filein = "/" + filein; } - TJpgDec.getFsJpgSize(&w, &h, filein, LittleFS); + TJpgDec.getFsJpgSize(&w, &h, filein, *contentFS); if (w==0 && h==0) { wsErr("invalid jpg"); return; @@ -47,7 +47,7 @@ void jpg2buffer(String filein, String fileout, imgParam &imageParams) { wsErr("Failed to create sprite in jpg2buffer"); } else { spr.fillSprite(TFT_WHITE); - TJpgDec.drawFsJpg(0, 0, filein, LittleFS); + TJpgDec.drawFsJpg(0, 0, filein, *contentFS); spr2buffer(spr, fileout, imageParams); spr.deleteSprite(); @@ -76,9 +76,9 @@ uint32_t colorDistance(const Color &c1, const Color &c2, const Error &e1) { void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) { long t = millis(); - LittleFS.begin(); + Storage.begin(); - fs::File f_out = LittleFS.open(fileout, "w"); + fs::File f_out = contentFS->open(fileout, "w"); bool dither = true; uint8_t rotate = imageParams.rotate; diff --git a/ESP32_AP-Flasher/src/newproto.cpp b/ESP32_AP-Flasher/src/newproto.cpp index 09d9be7f..ad2b130d 100644 --- a/ESP32_AP-Flasher/src/newproto.cpp +++ b/ESP32_AP-Flasher/src/newproto.cpp @@ -3,12 +3,12 @@ #include #include #include -#include +#include "storage.h" #include #include #include -#include "LittleFS.h" +#include "storage.h" #include "commstructs.h" #include "serialap.h" #include "settings.h" @@ -130,14 +130,14 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t } *filename = "/" + *filename; - LittleFS.begin(); + Storage.begin(); - if (!LittleFS.exists(*filename)) { + if (!contentFS->exists(*filename)) { wsErr("File not found. " + *filename); return false; } - fs::File file = LittleFS.open(*filename); + fs::File file = contentFS->open(*filename); uint32_t filesize = file.size(); if (filesize == 0) { file.close(); @@ -161,8 +161,8 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t if (memcmp(md5bytes, taginfo->md5pending, 16) == 0) { wsLog("new image is the same as current or already pending image. not updating tag."); wsSendTaginfo(dst, SYNC_TAGSTATUS); - if (LittleFS.exists(*filename)) { - LittleFS.remove(*filename); + if (contentFS->exists(*filename)) { + contentFS->remove(*filename); } return true; } @@ -182,10 +182,10 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t if (dataType != DATATYPE_FW_UPDATE) { char dst_path[64]; sprintf(dst_path, "/current/%02X%02X%02X%02X%02X%02X%02X%02X.pending\0", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]); - if (LittleFS.exists(dst_path)) { - LittleFS.remove(dst_path); + if (contentFS->exists(dst_path)) { + contentFS->remove(dst_path); } - LittleFS.rename(*filename, dst_path); + contentFS->rename(*filename, dst_path); *filename = String(dst_path); wsLog("new image: " + String(dst_path)); @@ -241,7 +241,7 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) { case DATATYPE_IMG_RAW_1BPP: case DATATYPE_IMG_RAW_2BPP: case DATATYPE_IMG_RAW_1BPP_DIRECT: { - LittleFS.begin(); + Storage.begin(); char hexmac[17]; mac2hex(pending->targetMac, hexmac); @@ -252,13 +252,13 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) { http.begin(imageUrl); int httpCode = http.GET(); if (httpCode == 200) { - File file = LittleFS.open(filename, "w"); + File file = contentFS->open(filename, "w"); http.writeToStream(&file); file.close(); } http.end(); - fs::File file = LittleFS.open(filename); + fs::File file = contentFS->open(filename); uint32_t filesize = file.size(); if (filesize == 0) { file.close(); @@ -338,7 +338,7 @@ void processBlockRequest(struct espBlockRequest* br) { if (taginfo->data == nullptr) { // not cached. open file, cache the data - fs::File file = LittleFS.open(taginfo->filename); + fs::File file = contentFS->open(taginfo->filename); if (!file) { Serial.print("No current file. Canceling request\n"); prepareCancelPending(br->src); @@ -380,14 +380,14 @@ void processXferComplete(struct espXferComplete* xfc, bool local) { char dst_path[64]; sprintf(src_path, "/current/%02X%02X%02X%02X%02X%02X%02X%02X.pending\0", xfc->src[7], xfc->src[6], xfc->src[5], xfc->src[4], xfc->src[3], xfc->src[2], xfc->src[1], xfc->src[0]); sprintf(dst_path, "/current/%02X%02X%02X%02X%02X%02X%02X%02X.raw\0", xfc->src[7], xfc->src[6], xfc->src[5], xfc->src[4], xfc->src[3], xfc->src[2], xfc->src[1], xfc->src[0]); - if (LittleFS.exists(dst_path) && LittleFS.exists(src_path)) { - LittleFS.remove(dst_path); + if (contentFS->exists(dst_path) && contentFS->exists(src_path)) { + contentFS->remove(dst_path); } - if (LittleFS.exists(src_path)) { + if (contentFS->exists(src_path)) { #ifndef REMOVE_RAW - LittleFS.rename(src_path, dst_path); + contentFS->rename(src_path, dst_path); #else - LittleFS.remove(src_path); + contentFS->remove(src_path); #endif } @@ -400,8 +400,8 @@ void processXferComplete(struct espXferComplete* xfc, bool local) { clearPending(taginfo); taginfo->wakeupReason = 0; if (taginfo->contentMode == 12 && local == false) { - if (LittleFS.exists(dst_path)) { - LittleFS.remove(dst_path); + if (contentFS->exists(dst_path)) { + contentFS->remove(dst_path); } } } diff --git a/ESP32_AP-Flasher/src/ota.cpp b/ESP32_AP-Flasher/src/ota.cpp index 9fa8cd0b..9815dd01 100644 --- a/ESP32_AP-Flasher/src/ota.cpp +++ b/ESP32_AP-Flasher/src/ota.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include "storage.h" #include #include @@ -51,7 +51,7 @@ void handleCheckFile(AsyncWebServerRequest* request) { } String filePath = request->getParam("path")->value(); - File file = LittleFS.open(filePath, "r"); + File file = contentFS->open(filePath, "r"); if (!file) { StaticJsonDocument<64> doc; doc["filesize"] = 0; @@ -121,7 +121,7 @@ void handleLittleFSUpload(AsyncWebServerRequest* request, String filename, size_ } else { path = request->getParam("path", true)->value(); Serial.println("update " + path); - request->_tempFile = LittleFS.open(path, "w", true); + request->_tempFile = contentFS->open(path, "w", true); } } if (len) { @@ -268,7 +268,7 @@ void handleRollback(AsyncWebServerRequest* request) { void handleUpdateActions(AsyncWebServerRequest* request) { wsSerial("Performing cleanup"); - File file = LittleFS.open("/update_actions.json", "r"); + File file = contentFS->open("/update_actions.json", "r"); if (!file) { wsSerial("No update_actions.json present"); request->send(200, "No update actions needed"); @@ -278,12 +278,12 @@ void handleUpdateActions(AsyncWebServerRequest* request) { DeserializationError error = deserializeJson(doc, file); JsonArray deleteFiles = doc["deletefile"].as(); for (const auto& filePath : deleteFiles) { - if (LittleFS.remove(filePath.as())) { + if (contentFS->remove(filePath.as())) { wsSerial("deleted file: " + filePath.as()); } } file.close(); wsSerial("Cleanup finished"); request->send(200, "Clean up finished"); - LittleFS.remove("/update_actions.json"); + contentFS->remove("/update_actions.json"); } \ No newline at end of file diff --git a/ESP32_AP-Flasher/src/serialap.cpp b/ESP32_AP-Flasher/src/serialap.cpp index 3cec6467..db249e6a 100644 --- a/ESP32_AP-Flasher/src/serialap.cpp +++ b/ESP32_AP-Flasher/src/serialap.cpp @@ -2,7 +2,6 @@ #include #include -#include #include "commstructs.h" #include "flasher.h" @@ -10,6 +9,7 @@ #include "newproto.h" #include "powermgt.h" #include "settings.h" +#include "storage.h" #include "web.h" #include "zbs_interface.h" @@ -205,6 +205,7 @@ bool sendDataAvail(struct pendingData* pending) { } if (waitCmdReply()) goto sdasend; Serial.printf("SDA send failed in try %d\n", attempt); + delay(200); } Serial.print("SDA failed to send...\n"); txEnd(); diff --git a/ESP32_AP-Flasher/src/storage.cpp b/ESP32_AP-Flasher/src/storage.cpp new file mode 100644 index 00000000..f923ac0e --- /dev/null +++ b/ESP32_AP-Flasher/src/storage.cpp @@ -0,0 +1,189 @@ +#include "storage.h" + +#ifdef HAS_SDCARD +#include "FS.h" +#include "SD.h" +#include "SPI.h" +#endif + +#include "LittleFS.h" + +DynStorage::DynStorage() : isInited(0) {} + +static void initLittleFS() { + LittleFS.begin(); + contentFS = &LittleFS; +} + +#ifdef HAS_SDCARD +static SPIClass* spi; + +static void initSDCard() { + uint8_t spi_bus = VSPI; + + // SD.begin and spi.begin are allocating memory so we dont want to do that + if(!spi) { + spi = new SPIClass(spi_bus); + spi->begin(SD_CARD_CLK, SD_CARD_MISO, SD_CARD_MOSI, SD_CARD_SS); + + bool res = SD.begin(SD_CARD_SS, *spi, 40000000); + if (!res) { + Serial.println("Card Mount Failed"); + return; + } + } + + uint8_t cardType = SD.cardType(); + + if (cardType == CARD_NONE) { + Serial.println("No SD card attached"); + return; + } + + contentFS = &SD; +} +#endif + +void copyToSD(FS &sourceFS, const char* source_path){ + File root = sourceFS.open(source_path); + +void copyFile(File in, File out) { + Serial.print("Copying "); + Serial.print(in.path()); + Serial.print(" to "); + Serial.println(out.path()); + + size_t n; + uint8_t buf[64]; + while ((n = in.read(buf, sizeof(buf))) > 0) { + out.write(buf, n); + } +} + +void copyBetweenFS(FS& sourceFS, const char* source_path, FS& targetFS) { + File root = sourceFS.open(source_path); + char next_path[128]; + + if (root.isDirectory()) { + if (!contentFS->exists(root.path())) { + if (!contentFS->mkdir(root.path())) { + Serial.print("Failed to create directory "); + Serial.println(root.path()); + return; + } + } + File file = root.openNextFile(); + while (file) { + if (file.isDirectory()) { + sprintf(next_path, "%s/%s\0", root.path(), file.path()); + + copyBetweenFS(sourceFS, file.path(), targetFS); + } else { + File target = contentFS->open(file.path(), "w"); + if (target) { + copyFile(file, target); + target.close(); + file.close(); + } else { + Serial.print("Couldn't create high target file"); + Serial.println(file.path()); + return; + } + } + file = root.openNextFile(); + } + } else { + File target = contentFS->open(root.path(), "w"); + if (target) { + copyFile(root, target); + } else { + Serial.print("Couldn't create target file "); + Serial.println(root.path()); + return; + } + } +} + +#ifdef HAS_SDCARD +void copyIfNeeded(const char* path) { + if (!contentFS->exists(path) && LittleFS.exists(path)) { + Serial.printf("SDCard does not contain %s, littleFS does, copying\n", path); + copyBetweenFS(LittleFS, path, *contentFS); + } +} +#endif + +void DynStorage::begin() { + initLittleFS(); + +#ifdef HAS_SDCARD + initSDCard(); + + copyIfNeeded("/index.html"); + copyIfNeeded("/fonts"); + copyIfNeeded("/www"); + copyIfNeeded("/AP_FW_Pack.bin"); + copyIfNeeded("/tag_md5_db.json"); + copyIfNeeded("/update_actions.json"); + copyIfNeeded("/content_template.json"); +#endif + + if (!contentFS->exists("/current")) { + contentFS->mkdir("/current"); + } + if (!contentFS->exists("/temp")) { + contentFS->mkdir("/temp"); + } +} + + +void listDir(fs::FS& fs, const char* dirname, uint8_t levels) { + Storage.begin(); + // Print blank line on screen + Serial.printf(" \n "); + + Serial.printf("Listing directory: %s\n", dirname); + + File root = fs.open(dirname); + if (!root) { + Serial.println("Failed to open directory"); + return; + } + if (!root.isDirectory()) { + Serial.println("Not a directory"); + return; + } + + File file = root.openNextFile(); + while (file) { + if (!strcmp("System Volume Information", file.name())) { + file = root.openNextFile(); + continue; + } + + if (file.isDirectory()) { + Serial.print(" DIR : "); + Serial.println(file.name()); + if (levels) { + listDir(fs, file.path(), levels - 1); + } + Serial.println(); + } else { + Serial.print(" FILE: "); + Serial.print(file.name()); + Serial.print(" SIZE: "); + Serial.println(file.size()); + } + file = root.openNextFile(); + } +} + +void DynStorage::listFiles() { + listDir(LittleFS, "/", 1); +#ifdef HAS_SDCARD + listDir(*contentFS, "/", 1); +#endif +} + +fs::FS* contentFS; +DynStorage Storage; diff --git a/ESP32_AP-Flasher/src/system.cpp b/ESP32_AP-Flasher/src/system.cpp index 07d2bc65..83879d08 100644 --- a/ESP32_AP-Flasher/src/system.cpp +++ b/ESP32_AP-Flasher/src/system.cpp @@ -3,7 +3,7 @@ #include #include -#include "LittleFS.h" +#include "storage.h" void init_time() { struct tm timeinfo; @@ -28,7 +28,7 @@ void logLine(String text) { char timeStr[24]; strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S ", localtime(&now)); - File logFile = LittleFS.open("/log.txt", "a"); + File logFile = contentFS->open("/log.txt", "a"); if (logFile) { logFile.print(timeStr); logFile.println(text); diff --git a/ESP32_AP-Flasher/src/tag_db.cpp b/ESP32_AP-Flasher/src/tag_db.cpp index e0bdf4e6..aea3ee19 100644 --- a/ESP32_AP-Flasher/src/tag_db.cpp +++ b/ESP32_AP-Flasher/src/tag_db.cpp @@ -5,7 +5,7 @@ #include #include -#include "LittleFS.h" +#include "storage.h" #include "language.h" std::vector tagDB; @@ -126,8 +126,8 @@ void saveDB(String filename) { long t = millis(); - LittleFS.begin(); - fs::File file = LittleFS.open(filename, "w"); + Storage.begin(); + fs::File file = contentFS->open(filename, "w"); if (!file) { Serial.println("saveDB: Failed to open file"); return; @@ -161,8 +161,8 @@ void loadDB(String filename) { Serial.println("reading DB from file"); long t = millis(); - LittleFS.begin(); - fs::File readfile = LittleFS.open(filename, "r"); + Storage.begin(); + fs::File readfile = contentFS->open(filename, "r"); if (!readfile) { Serial.println("loadDB: Failed to open file"); return; @@ -264,9 +264,9 @@ void clearPending(tagRecord* taginfo) { } void initAPconfig() { - LittleFS.begin(true); + Storage.begin(); DynamicJsonDocument APconfig(500); - File configFile = LittleFS.open("/current/apconfig.json", "r"); + File configFile = contentFS->open("/current/apconfig.json", "r"); if (configFile) { DeserializationError error = deserializeJson(APconfig, configFile); if (error) { @@ -285,7 +285,7 @@ void initAPconfig() { } void saveAPconfig() { - fs::File configFile = LittleFS.open("/current/apconfig.json", "w"); + fs::File configFile = contentFS->open("/current/apconfig.json", "w"); DynamicJsonDocument APconfig(500); APconfig["channel"] = config.channel; APconfig["alias"] = config.alias; diff --git a/ESP32_AP-Flasher/src/web.cpp b/ESP32_AP-Flasher/src/web.cpp index 6eec2bfb..5432cb5e 100644 --- a/ESP32_AP-Flasher/src/web.cpp +++ b/ESP32_AP-Flasher/src/web.cpp @@ -6,7 +6,8 @@ #include #include #include -#include +#include "storage.h" +#include "LittleFS.h" #include #include #include // https://github.com/tzapu/WiFiManager/tree/feature_asyncwebserver @@ -215,15 +216,7 @@ uint8_t wsClientCount() { } void init_web() { - LittleFS.begin(true); - - if (!LittleFS.exists("/current")) { - LittleFS.mkdir("/current"); - } - if (!LittleFS.exists("/temp")) { - LittleFS.mkdir("/temp"); - } - + Storage.begin(); WiFi.mode(WIFI_STA); WiFiManager wm; @@ -243,8 +236,8 @@ void init_web() { Serial.print("Connected! IP address: "); Serial.println(WiFi.localIP()); - // server.addHandler(new SPIFFSEditor(LittleFS, http_username, http_password)); - server.addHandler(new SPIFFSEditor(LittleFS)); + // server.addHandler(new SPIFFSEditor(*contentFS, http_username, http_password)); + server.addHandler(new SPIFFSEditor(*contentFS)); ws.onEvent(onEvent); server.addHandler(&ws); @@ -260,8 +253,8 @@ void init_web() { ESP.restart(); }); - server.serveStatic("/current", LittleFS, "/current/"); - server.serveStatic("/", LittleFS, "/www/").setDefaultFile("index.html"); + server.serveStatic("/current", *contentFS, "/current/"); + server.serveStatic("/", *contentFS, "/www/").setDefaultFile("index.html"); server.on( "/imgupload", HTTP_POST, [](AsyncWebServerRequest *request) { @@ -371,7 +364,7 @@ void init_web() { server.on("/get_ap_config", HTTP_GET, [](AsyncWebServerRequest *request) { UDPcomm udpsync; udpsync.getAPList(); - File configFile = LittleFS.open("/current/apconfig.json", "r"); + File configFile = contentFS->open("/current/apconfig.json", "r"); if (!configFile) { request->send(500, "text/plain", "Error opening apconfig.json file"); return; @@ -411,7 +404,7 @@ void init_web() { server.on("/backup_db", HTTP_GET, [](AsyncWebServerRequest *request) { saveDB("/current/tagDB.json"); - File file = LittleFS.open("/current/tagDB.json", "r"); + File file = contentFS->open("/current/tagDB.json", "r"); AsyncWebServerResponse *response = request->beginResponse(file, "tagDB.json", String(), true); request->send(response); file.close(); @@ -449,7 +442,7 @@ void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index } else { filename = "unknown.jpg"; } - request->_tempFile = LittleFS.open("/" + filename, "w"); + request->_tempFile = contentFS->open("/" + filename, "w"); } if (len) { // stream the incoming chunk to the opened file