mirror of
https://github.com/OpenEPaperLink/OpenEPaperLink.git
synced 2026-03-21 01:04:30 +01:00
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:
@@ -160,7 +160,7 @@ Latency will be around 40 seconds.">
|
||||
<div><span id="runstate"></div>
|
||||
<div><span id="apstatecolor">⬤</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>
|
||||
|
||||
|
||||
7
ESP32_AP-Flasher/esp32_sdcard.csv
Normal file
7
ESP32_AP-Flasher/esp32_sdcard.csv
Normal 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,
|
||||
|
37
ESP32_AP-Flasher/include/storage.h
Normal file
37
ESP32_AP-Flasher/include/storage.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
189
ESP32_AP-Flasher/src/storage.cpp
Normal file
189
ESP32_AP-Flasher/src/storage.cpp
Normal 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;
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user