Add SDCard support

To be able to store more images and never worry about running
out of storage again we are adding support of SDCards.

We are faced with two options:
1) Remove the LittleFS code
2) Allow for both FSs but increase the app partition as we run
out of space.

We have opted for 2 for two reasons:
1) Unformated SDCards can be initialized from the littleFS
2) If the SDCard and the flasher share the same SPI pins
the SDCard needs to be completely disconnected so there needs
to be a FS in that rare case.

Performance wise the SDCard is not slowing the system down.
Writing the AP db with 70 APs takes 2.5 seconds, on the SD
only 800ms. This however is SD and setup depending.

By default the SDCard is expected on pins 18,19,23 however this
is configurable via preprocessor defines.

Globally we replace LittleFS with a new pointer to the currently
used FS allowing for hotswapping where the FS pointer is not saved.
One case which is therefore not working is the SPIFSEditor which
copies the FS interally.

Signed-off-by: Mimoja <git@mimoja.de>
This commit is contained in:
Mimoja
2023-06-17 16:56:17 +02:00
committed by Mimoja
parent eae7c3a159
commit f487092f7f
14 changed files with 314 additions and 84 deletions

View File

@@ -160,7 +160,7 @@ Latency will be around 40 seconds.">
<div><span id="runstate"></div>
<div><span id="apstatecolor">&#11044;</span> <span id="apstate">loading</span></div>
<div><span id="apconfigbutton">AP config</span></div>
<div><a href="/edit" target="littlefs" class="filebutton">edit littleFS</a></div>
<div><a href="/edit" target="littlefs" class="filebutton">edit contentFS</a></div>
</div>
</div>

View File

@@ -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,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0xe000 0x2000
4 app0 app ota_0 0x10000 0x150000
5 app1 app ota_1 0x160000 0x150000
6 spiffs data spiffs 0x2B0000 0x140000
7 coredump data coredump 0x3F0000 0x10000

View File

@@ -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

View File

@@ -13,7 +13,7 @@
#ifdef CONTENT_RSS
#include <rssClass.h>
#endif
#include <LittleFS.h>
#include "storage.h"
#include <time.h>
#include <map>
@@ -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<int>())) {
cfgobj["#fetched"] = true;
if (cfgobj["delete"].as<String>() == "1") LittleFS.remove("/" + cfgobj["filename"].as<String>());
if (cfgobj["delete"].as<String>() == "1") contentFS->remove("/" + cfgobj["filename"].as<String>());
} 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<String>();
if (count > 999) font = loc["fonts"][2].as<String>();
if (count > 9999) font = loc["fonts"][3].as<String>();
spr.loadFont(font, LittleFS);
spr.loadFont(font, *contentFS);
spr.drawString(String(count), loc["xy"][0].as<uint16_t>(), loc["xy"][1].as<uint16_t>());
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<int>();
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<double>());
uint8_t wind = windSpeedToBeaufort(doc["daily"]["windspeed_10m_max"][dag].as<double>());
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<double>());
@@ -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;

View File

@@ -2,7 +2,7 @@
#include <Arduino.h>
#include <ArduinoJson.h>
#include <LittleFS.h>
#include "storage.h"
#include <MD5Builder.h>
// #include <FS.h>
@@ -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<JsonArray>()) {
@@ -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<JsonArray>()) {
@@ -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<JsonArray>()) {
@@ -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<JsonArray>()) {
@@ -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;

View File

@@ -1,8 +1,10 @@
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiManager.h>
#include <time.h>
#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());

View File

@@ -1,6 +1,6 @@
#include <Arduino.h>
#include <FS.h>
#include <LittleFS.h>
#include "storage.h"
#include <TFT_eSPI.h>
#include <TJpg_Decoder.h>
#include <makeimage.h>
@@ -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;

View File

@@ -3,12 +3,12 @@
#include <Arduino.h>
#include <FS.h>
#include <HTTPClient.h>
#include <LittleFS.h>
#include "storage.h"
#include <MD5Builder.h>
#include <makeimage.h>
#include <time.h>
#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);
}
}
}

View File

@@ -4,7 +4,7 @@
#include <ArduinoJson.h>
#include <FS.h>
#include <HTTPClient.h>
#include <LittleFS.h>
#include "storage.h"
#include <MD5Builder.h>
#include <Update.h>
@@ -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<JsonArray>();
for (const auto& filePath : deleteFiles) {
if (LittleFS.remove(filePath.as<const char*>())) {
if (contentFS->remove(filePath.as<const char*>())) {
wsSerial("deleted file: " + filePath.as<String>());
}
}
file.close();
wsSerial("Cleanup finished");
request->send(200, "Clean up finished");
LittleFS.remove("/update_actions.json");
contentFS->remove("/update_actions.json");
}

View File

@@ -2,7 +2,6 @@
#include <Arduino.h>
#include <HardwareSerial.h>
#include <LittleFS.h>
#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();

View File

@@ -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;

View File

@@ -3,7 +3,7 @@
#include <Arduino.h>
#include <FS.h>
#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);

View File

@@ -5,7 +5,7 @@
#include <FS.h>
#include <vector>
#include "LittleFS.h"
#include "storage.h"
#include "language.h"
std::vector<tagRecord*> 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;

View File

@@ -6,7 +6,8 @@
#include <ESPAsyncWebServer.h>
#include <ESPmDNS.h>
#include <FS.h>
#include <LittleFS.h>
#include "storage.h"
#include "LittleFS.h"
#include <SPIFFSEditor.h>
#include <WiFi.h>
#include <WiFiManager.h> // 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