diff --git a/ESP32_AP-Flasher/data/www/index.html b/ESP32_AP-Flasher/data/www/index.html
index 28f5f025..f7dab1d2 100644
--- a/ESP32_AP-Flasher/data/www/index.html
+++ b/ESP32_AP-Flasher/data/www/index.html
@@ -163,6 +163,7 @@ Latency will be around 40 seconds.">
⬤ loading
AP config
+ ⬤
diff --git a/ESP32_AP-Flasher/data/www/main.js b/ESP32_AP-Flasher/data/www/main.js
index c68c7ecf..a225922e 100644
--- a/ESP32_AP-Flasher/data/www/main.js
+++ b/ESP32_AP-Flasher/data/www/main.js
@@ -101,6 +101,18 @@ function connect() {
$("#runstate").innerHTML = runstate[msg.sys.runstate].state;
$("#temp").innerHTML = msg.sys.temp.toFixed(1) + '°C';
}
+ if (typeof msg.sys.sdcard != "undefined") {
+ $("#sdstatecolor").style.display = '';
+ $("#sdstate").style.display = '';
+ const sdName = ["", "MMC", "SD", "SDHC", "Unknown"];
+ if (msg.sys.sdcard == 0) {
+ $("#sdstatecolor").style.color = "red";
+ $("#sdstate").innerHTML = "SD failed"
+ } else {
+ $("#sdstatecolor").style.color = "green";
+ $("#sdstate").innerHTML = sdName[msg.sys.sdcard];
+ }
+ }
servertimediff = (Date.now() / 1000) - msg.sys.currtime;
}
if (msg.apitem) {
diff --git a/ESP32_AP-Flasher/include/storage.h b/ESP32_AP-Flasher/include/storage.h
index a1732e25..8bd7cd6c 100644
--- a/ESP32_AP-Flasher/include/storage.h
+++ b/ESP32_AP-Flasher/include/storage.h
@@ -28,9 +28,12 @@ class DynStorage {
void end();
void listFiles();
size_t freeSpace();
+ #ifdef HAS_SDCARD
+ uint8_t cardType();
+ #endif
private:
- bool isInited;
+ bool isInited = false;
};
extern DynStorage Storage;
diff --git a/ESP32_AP-Flasher/src/storage.cpp b/ESP32_AP-Flasher/src/storage.cpp
index 2cb5849f..4a8f5a9d 100644
--- a/ESP32_AP-Flasher/src/storage.cpp
+++ b/ESP32_AP-Flasher/src/storage.cpp
@@ -42,6 +42,11 @@ static void initSDCard() {
contentFS = &SD;
}
+
+uint8_t DynStorage::cardType() {
+ return SD.cardType();
+}
+
#endif
size_t DynStorage::freeSpace(){
@@ -116,21 +121,140 @@ void copyIfNeeded(const char* 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
- initSDCard();
+ bool inited = 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");
+ 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")) {
diff --git a/ESP32_AP-Flasher/src/web.cpp b/ESP32_AP-Flasher/src/web.cpp
index 5c0336fb..13ea66bd 100644
--- a/ESP32_AP-Flasher/src/web.cpp
+++ b/ESP32_AP-Flasher/src/web.cpp
@@ -144,6 +144,10 @@ void wsSendSysteminfo() {
sys["wifistatus"] = WiFi.status();
sys["wifissid"] = WiFi.SSID();
+#ifdef HAS_SDCARD
+ sys["sdcard"] = Storage.cardType();
+#endif
+
xSemaphoreTake(wsMutex, portMAX_DELAY);
ws.textAll(doc.as());
xSemaphoreGive(wsMutex);