mirror of
https://github.com/OpenEPaperLink/OpenEPaperLink.git
synced 2026-03-21 03:04:25 +01:00
340 lines
8.9 KiB
C++
340 lines
8.9 KiB
C++
#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;
|
|
}
|
|
|
|
uint8_t DynStorage::cardType() {
|
|
return SD.cardType();
|
|
}
|
|
|
|
#endif
|
|
|
|
size_t DynStorage::freeSpace(){
|
|
this->begin();
|
|
#ifdef HAS_SDCARD
|
|
return SD.totalBytes() - SD.usedBytes();
|
|
#endif
|
|
return LittleFS.totalBytes() - LittleFS.usedBytes();
|
|
}
|
|
|
|
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
|
|
|
|
String md5Filepath = "/ota_md5.txt";
|
|
String otaFilepath = "/ota.bin";
|
|
|
|
SemaphoreHandle_t otaMutex;
|
|
|
|
static void fileSystemFirmwareUpdateTask(void* parameter) {
|
|
const char* md5 = nullptr;
|
|
String md5Str = "";
|
|
|
|
xSemaphoreTake(otaMutex, portMAX_DELAY);
|
|
|
|
File updateBin = contentFS->open(otaFilepath);
|
|
if (!updateBin) {
|
|
Serial.println("Failed to load " + otaFilepath);
|
|
wsErr("Failed to load " + otaFilepath);
|
|
xSemaphoreGive(otaMutex);
|
|
return;
|
|
}
|
|
|
|
if(updateBin.isDirectory()){
|
|
Serial.println("Error " + otaFilepath + " is not a file");
|
|
wsErr("Error " + otaFilepath + " is not a file");
|
|
updateBin.close();
|
|
xSemaphoreGive(otaMutex);
|
|
return;
|
|
}
|
|
|
|
// MD5 check
|
|
if (contentFS->exists(md5Filepath)) {
|
|
Serial.println("Reading MD5 file");
|
|
File md5File = contentFS->open(md5Filepath);
|
|
if (!md5File ) {
|
|
Serial.println("Failed to load ota md5 even tho an md5 file exists");
|
|
wsErr("Failed to load ota md5 even tho an md5 file exists");
|
|
contentFS->rename(md5Filepath, md5Filepath + "_failure.txt");
|
|
xSemaphoreGive(otaMutex);
|
|
return;
|
|
}
|
|
|
|
if(md5File.size() < 32){
|
|
Serial.println("md5 sum too short");
|
|
wsErr("md5 sum too short");
|
|
md5File.close();
|
|
contentFS->rename(md5Filepath, md5Filepath + "_failure.txt");
|
|
xSemaphoreGive(otaMutex);
|
|
return;
|
|
}
|
|
|
|
// Only take the md5 from the output of md5sum
|
|
// "471a53ab5e35fa9d3e642a82fa95f3ce .pio/build/Esp32-POE-ISO/firmware.bin"
|
|
md5Str = md5File.readStringUntil(' ');
|
|
md5Str.trim();
|
|
md5Str.toLowerCase();
|
|
Serial.println(String("ota.bin md5:") + md5Str);
|
|
wsLog(String("ota.bin md5:") + md5Str);
|
|
md5 = md5Str.c_str();
|
|
md5File.close();
|
|
}
|
|
|
|
bool success = executeUpdate(md5, updateBin.size(), updateBin, updateBin.size());
|
|
if (!success) {
|
|
Serial.println("Update failed.");
|
|
}
|
|
updateBin.close();
|
|
contentFS->remove(otaFilepath);
|
|
|
|
if (contentFS->exists(md5Filepath))
|
|
contentFS->remove(md5Filepath);
|
|
|
|
if (success) {
|
|
Serial.println("Rebooting now");
|
|
wsLog("Rebooting now!");
|
|
ESP.restart();
|
|
}
|
|
|
|
xSemaphoreGive(otaMutex);
|
|
}
|
|
|
|
void DynStorage::checkForUpdate() {
|
|
Storage.begin();
|
|
|
|
if (!contentFS->exists(otaFilepath)) {
|
|
return;
|
|
}
|
|
|
|
if(!otaMutex)
|
|
otaMutex = xSemaphoreCreateMutex();
|
|
|
|
// An update process is already running
|
|
if (xQueuePeek((xQueueHandle)otaMutex, (void*)NULL, (portTickType)NULL) != pdTRUE)
|
|
return;
|
|
|
|
File updateBin = contentFS->open(otaFilepath);
|
|
if (!updateBin) {
|
|
Serial.println("Failed to load " + otaFilepath);
|
|
wsErr("Failed to load " + otaFilepath);
|
|
}
|
|
|
|
size_t updateSize = updateBin.size();
|
|
if (updateSize <= 0) {
|
|
return;
|
|
}
|
|
updateBin.close();
|
|
Serial.println("Found OTA file on contentFS, updating async");
|
|
wsLog("Found OTA file on contentFS, updating async");
|
|
|
|
xTaskCreate(fileSystemFirmwareUpdateTask, "FSUpdateTask", 6144, NULL, 10, NULL);
|
|
}
|
|
|
|
void DynStorage::begin() {
|
|
initLittleFS();
|
|
|
|
#ifdef HAS_SDCARD
|
|
bool inited = initSDCard();
|
|
|
|
if(inited && inited != this->isInited) {
|
|
Serial.println("SDCard mounted");
|
|
wsLog("SDcard mounted");
|
|
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");
|
|
}
|
|
if (this->isInited && !inited) {
|
|
Serial.println("Lost connection to the SDCard");
|
|
wsErr("Lost connection to the SDCard");
|
|
}
|
|
this->isInited = inited;
|
|
#endif
|
|
|
|
if (!contentFS->exists("/current")) {
|
|
contentFS->mkdir("/current");
|
|
}
|
|
if (!contentFS->exists("/temp")) {
|
|
contentFS->mkdir("/temp");
|
|
}
|
|
}
|
|
|
|
void DynStorage::end() {
|
|
#ifdef HAS_SDCARD
|
|
initLittleFS();
|
|
if (SD_CARD_CLK == FLASHER_AP_CLK ||
|
|
SD_CARD_MISO == FLASHER_AP_MISO ||
|
|
SD_CARD_MOSI == FLASHER_AP_MOSI) {
|
|
Serial.println("Tearing down SD card connection");
|
|
|
|
copyBetweenFS(*contentFS, "/tag_md5_db.json", LittleFS);
|
|
copyBetweenFS(*contentFS, "/AP_FW_Pack.bin", LittleFS);
|
|
if (contentFS->exists("/AP_force_flash.bin")) {
|
|
copyBetweenFS(*contentFS, "/AP_force_flash.bin", LittleFS);
|
|
contentFS->remove("/AP_force_flash.bin");
|
|
}
|
|
Serial.println("Swapping to LittleFS");
|
|
|
|
contentFS = &LittleFS;
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
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;
|