diff --git a/partitions.csv b/partitions.csv index 4ca13c4..aa1daa1 100644 --- a/partitions.csv +++ b/partitions.csv @@ -1,5 +1,6 @@ -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x5000, -otadata, data, ota, 0xe000, 0x2000, -app0, app, ota_0, 0x10000, 0x280000, -spiffs, data, spiffs, 0x290000, 0x170000, \ No newline at end of file +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, factory, 0x10000, 0x1E0000, +app1, app, ota_0, 0x1F0000, 0x1E0000, +spiffs, data, spiffs, 0x3D0000, 0x30000, \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index dac12fc..df5bfcd 100644 --- a/platformio.ini +++ b/platformio.ini @@ -34,9 +34,8 @@ board_build.filesystem = spiffs ; Update partition settings board_build.partitions = partitions.csv board_upload.flash_size = 4MB -; Remove these as they're now defined in partitions.csv -; board_build.spiffs.partition = 2M -; board_build.spiffs.upload_size = 2M +board_build.flash_mode = dio +board_upload.flash_freq = "40m" build_flags = -Os @@ -45,8 +44,15 @@ build_flags = -DNDEBUG -mtext-section-literals '-D VERSION="${common.version}"' - -DESPASYNCHTTPUPDATESERVER_PRETTY - + -DASYNCWEBSERVER_REGEX + -DCORE_DEBUG_LEVEL=1 + -DCONFIG_ARDUHAL_LOG_COLORS=1 + -DOTA_DEBUG=1 + -DARDUINO_RUNNING_CORE=1 + -DARDUINO_EVENT_RUNNING_CORE=1 + -DCONFIG_OPTIMIZATION_LEVEL_DEBUG=1 + -DCONFIG_ESP32_PANIC_PRINT_REBOOT + extra_scripts = scripts/extra_script.py pre:scripts/pre_build.py ; wird zuerst ausgeführt diff --git a/src/ota.cpp b/src/ota.cpp index b8610b0..309b7ec 100644 --- a/src/ota.cpp +++ b/src/ota.cpp @@ -1,3 +1,214 @@ #include #include "ota.h" +#include +#include +#include "commonFS.h" +#include +#include +#include +#include + +// Reduzierter Puffer für Dateioperationen +const size_t BUFFER_SIZE = 128; +const size_t MIN_FREE_HEAP = 32768; // 32KB minimum free heap + +// Files to backup before update +const char* CONFIG_FILES[] = { + "/bambu_credentials.json", + "/spoolman_url.json" +}; +const int CONFIG_FILES_COUNT = sizeof(CONFIG_FILES) / sizeof(CONFIG_FILES[0]); + +bool backupConfigs() { + if (!SPIFFS.begin(true)) { + Serial.println("Failed to mount SPIFFS"); + return false; + } + + for (int i = 0; i < CONFIG_FILES_COUNT; i++) { + if (SPIFFS.exists(CONFIG_FILES[i])) { + String backupFile = String(CONFIG_FILES[i]) + ".bak"; + + if (SPIFFS.exists(backupFile)) { + SPIFFS.remove(backupFile); + } + + File sourceFile = SPIFFS.open(CONFIG_FILES[i], "r"); + File destFile = SPIFFS.open(backupFile, "w"); + + if (!sourceFile || !destFile) { + Serial.printf("Failed to open files for backup: %s\n", CONFIG_FILES[i]); + return false; + } + + // Verwenden Sie einen kleineren Puffer + uint8_t* buf = (uint8_t*)malloc(BUFFER_SIZE); + if (!buf) { + Serial.println("Failed to allocate buffer"); + sourceFile.close(); + destFile.close(); + return false; + } + + size_t len = 0; + bool success = true; + while ((len = sourceFile.read(buf, BUFFER_SIZE)) > 0) { + if (destFile.write(buf, len) != len) { + Serial.println("Write failed"); + success = false; + break; + } + } + + free(buf); + sourceFile.close(); + destFile.close(); + + if (!success) { + return false; + } + } + } + return true; +} + +bool restoreConfigs() { + if (!SPIFFS.begin(true)) { + Serial.println("Failed to mount SPIFFS"); + return false; + } + + bool success = true; + for (int i = 0; i < CONFIG_FILES_COUNT; i++) { + String backupFile = String(CONFIG_FILES[i]) + ".bak"; + if (SPIFFS.exists(backupFile)) { + if (SPIFFS.exists(CONFIG_FILES[i])) { + SPIFFS.remove(CONFIG_FILES[i]); + } + + File sourceFile = SPIFFS.open(backupFile, "r"); + File destFile = SPIFFS.open(CONFIG_FILES[i], "w"); + + if (!sourceFile || !destFile) { + Serial.printf("Failed to open files for restore: %s\n", CONFIG_FILES[i]); + success = false; + continue; + } + + // Verwenden Sie einen kleineren Puffer + uint8_t* buf = (uint8_t*)malloc(BUFFER_SIZE); + if (!buf) { + Serial.println("Failed to allocate buffer"); + sourceFile.close(); + destFile.close(); + success = false; + continue; + } + + size_t len = 0; + while ((len = sourceFile.read(buf, BUFFER_SIZE)) > 0) { + if (destFile.write(buf, len) != len) { + Serial.println("Write failed"); + success = false; + break; + } + } + + free(buf); + sourceFile.close(); + destFile.close(); + + SPIFFS.remove(backupFile); + } + } + return success; +} + +void handleOTAUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) { + static bool updateStarted = false; + static size_t totalBytes = 0; + + if (!index) { + updateStarted = false; + totalBytes = 0; + + // Check minimum heap size + if (ESP.getFreeHeap() < MIN_FREE_HEAP) { + request->send(500, "text/plain", "Not enough memory available"); + return; + } + + size_t updateSize = request->contentLength(); + if (updateSize == 0) { + request->send(400, "text/plain", "Invalid file size"); + return; + } + + Serial.printf("Update size: %u bytes\n", updateSize); + Serial.printf("Free space: %u bytes\n", ESP.getFreeSketchSpace()); + Serial.printf("Free heap: %u bytes\n", ESP.getFreeHeap()); + + // Backup configs first + if (!backupConfigs()) { + request->send(500, "text/plain", "Failed to backup configuration"); + return; + } + + // Close all files and unmount SPIFFS + SPIFFS.end(); + + // Begin update with minimal configuration + if (!Update.begin(updateSize)) { + Serial.printf("Update.begin failed: %s\n", Update.errorString()); + request->send(500, "text/plain", "Failed to start update"); + return; + } + + updateStarted = true; + Serial.println("Update process started"); + } + + if (!updateStarted) { + request->send(500, "text/plain", "Update not properly started"); + return; + } + + // Write update data + if (Update.write(data, len) != len) { + Serial.printf("Update.write failed: %s\n", Update.errorString()); + Update.abort(); + request->send(500, "text/plain", "Error during update"); + return; + } + + totalBytes += len; + if (totalBytes % (32 * 1024) == 0) { + Serial.printf("Progress: %u bytes\n", totalBytes); + } + + if (final) { + if (!Update.end(true)) { + Serial.printf("Update.end failed: %s\n", Update.errorString()); + request->send(500, "text/plain", "Update failed"); + return; + } + + // Try to restore configs + if (!SPIFFS.begin(true)) { + Serial.println("Failed to mount SPIFFS for restore"); + request->send(200, "text/plain", "Update successful but config restore failed. Device will restart..."); + delay(2000); + ESP.restart(); + return; + } + + if (!restoreConfigs()) { + Serial.println("Failed to restore configs"); + } + + request->send(200, "text/plain", "Update successful. Device will restart..."); + delay(2000); + ESP.restart(); + } +} diff --git a/src/ota.h b/src/ota.h index 5e571e2..c7779e9 100644 --- a/src/ota.h +++ b/src/ota.h @@ -1,6 +1,8 @@ #ifndef OTA_H #define OTA_H +#include +void handleOTAUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final); #endif \ No newline at end of file diff --git a/src/website.cpp b/src/website.cpp index bfc0d8d..d4d762d 100644 --- a/src/website.cpp +++ b/src/website.cpp @@ -337,7 +337,111 @@ void setupWebserver(AsyncWebServer &server) { request->send(response); Serial.println("RFID.js gesendet"); }); - + + // Route für OTA Updates + server.on("/ota", HTTP_GET, [](AsyncWebServerRequest *request) { + Serial.println("Anfrage für /ota erhalten"); + + String html = R"( + + + + Firmware Update + + + + +

Firmware Update

+
+ + +
+
+
0%
+
+
+ + + + )"; + request->send(200, "text/html", html); + }); + + server.on("/update", HTTP_POST, + [](AsyncWebServerRequest *request) { + // The response will be sent from handleOTAUpload when the upload is complete + }, + handleOTAUpload + ); + // Fehlerbehandlung für nicht gefundene Seiten server.onNotFound([](AsyncWebServerRequest *request){ Serial.print("404 - Nicht gefunden: ");