diff --git a/.github/workflows/providers/gitea-release.yml b/.github/workflows/providers/gitea-release.yml index bda0002..667a864 100644 --- a/.github/workflows/providers/gitea-release.yml +++ b/.github/workflows/providers/gitea-release.yml @@ -48,30 +48,28 @@ jobs: run: | cd .pio/build/esp32dev - # Create OTA binary (already has correct magic byte) - cp firmware.bin filaman.bin + # Kopiere die Firmware in den SPIFFS-Build-Ordner + cp firmware.bin ./spiffs/firmware.bin - # Create a magic byte prepended binary for the bootloader - echo -ne '\xE9' > bootloader_with_magic.bin - cat bootloader.bin >> bootloader_with_magic.bin + # Baue das SPIFFS-Image neu mit der eingebetteten Firmware + pio run -t buildfs - echo "Creating full binary with magic byte..." + # Erstelle die Release-Dateien + cp spiffs.bin filaman_spiffs.bin + + # Create full binary with magic byte + echo "Creating full binary..." esptool.py --chip esp32 merge_bin \ --fill-flash-size 4MB \ --flash_mode dio \ --flash_freq 40m \ --flash_size 4MB \ - -o filaman.bin \ - 0x0000 bootloader_with_magic.bin \ + -o filaman_full.bin \ + 0x0000 bootloader.bin \ 0x8000 partitions.bin \ 0x10000 firmware.bin \ 0x390000 spiffs.bin - # Verify magic bytes - echo "Checking magic bytes:" - echo "Full binary first bytes:" - xxd -l 16 filaman.bin - # Verify file sizes echo "File sizes:" ls -lh *.bin @@ -103,7 +101,7 @@ jobs: # Upload binaries cd .pio/build/esp32dev - for file in filaman.bin; do + for file in filaman_ota.bin filaman_spiffs.bin filaman_full.bin; do echo "Uploading $file..." curl -k -s \ -X POST \ diff --git a/.github/workflows/providers/github-release.yml b/.github/workflows/providers/github-release.yml index adbe08f..4a8c626 100644 --- a/.github/workflows/providers/github-release.yml +++ b/.github/workflows/providers/github-release.yml @@ -37,30 +37,28 @@ jobs: run: | cd .pio/build/esp32dev - # Create OTA binary (already has correct magic byte) - cp firmware.bin filaman.bin + # Kopiere die Firmware in den SPIFFS-Build-Ordner + cp firmware.bin ./spiffs/firmware.bin - # Create a magic byte prepended binary for the bootloader - echo -ne '\xE9' > bootloader_with_magic.bin - cat bootloader.bin >> bootloader_with_magic.bin + # Baue das SPIFFS-Image neu mit der eingebetteten Firmware + pio run -t buildfs - echo "Creating full binary with magic byte..." + # Erstelle die Release-Dateien + cp spiffs.bin filaman_spiffs.bin + + # Create full binary with magic byte + echo "Creating full binary..." esptool.py --chip esp32 merge_bin \ --fill-flash-size 4MB \ --flash_mode dio \ --flash_freq 40m \ --flash_size 4MB \ - -o filaman.bin \ - 0x0000 bootloader_with_magic.bin \ + -o filaman_full.bin \ + 0x0000 bootloader.bin \ 0x8000 partitions.bin \ 0x10000 firmware.bin \ 0x390000 spiffs.bin - # Verify magic bytes - echo "Checking magic bytes:" - echo "Full binary first bytes:" - xxd -l 16 filaman.bin - # Verify file sizes echo "File sizes:" ls -lh *.bin @@ -86,4 +84,4 @@ jobs: gh release create "${{ github.ref_name }}" \ --title "Release ${{ steps.get_version.outputs.VERSION }}" \ --notes "${{ steps.changelog.outputs.CHANGES }}" \ - .pio/build/esp32dev/filaman.bin \ No newline at end of file + .pio/build/esp32dev/filaman_full.bin \ No newline at end of file diff --git a/src/ota.cpp b/src/ota.cpp index 24468b5..6a2fa2e 100644 --- a/src/ota.cpp +++ b/src/ota.cpp @@ -20,81 +20,150 @@ void stopAllTasks() { Serial.println("All tasks stopped"); } +void performStageTwo() { + if (!SPIFFS.begin(true)) { + Serial.println("Error: Could not mount SPIFFS for stage 2"); + return; + } + + File firmwareFile = SPIFFS.open("/firmware.bin", "r"); + if (!firmwareFile) { + Serial.println("Error: Could not open firmware.bin from SPIFFS"); + return; + } + + size_t firmwareSize = firmwareFile.size(); + size_t maxAppSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; + + Serial.printf("Stage 2 - Firmware size: %u bytes\n", firmwareSize); + Serial.printf("Available space: %u bytes\n", maxAppSpace); + + if (firmwareSize > maxAppSpace) { + Serial.printf("Error: Not enough space for firmware. Need %u bytes but only have %u bytes\n", + firmwareSize, maxAppSpace); + return; + } + + if (!Update.begin(firmwareSize)) { + Update.printError(Serial); + return; + } + + size_t written = Update.writeStream(firmwareFile); + if (written != firmwareSize) { + Update.printError(Serial); + return; + } + + if (!Update.end(true)) { + Update.printError(Serial); + return; + } + + firmwareFile.close(); + SPIFFS.remove("/firmware.bin"); // Cleanup + Serial.println("Stage 2 update successful, restarting..."); + delay(500); + ESP.restart(); +} + +void checkForStagedUpdate() { + if (!SPIFFS.begin(true)) { + return; + } + + if (SPIFFS.exists("/firmware.bin")) { + Serial.println("Found staged firmware update, initiating stage 2..."); + performStageTwo(); + } +} + void handleOTAUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) { + static File stagingFile; + if (!index) { - bool isFullImage = filename.endsWith(".bin"); - Serial.printf("Update Start: %s (type: %s)\n", filename.c_str(), isFullImage ? "full" : "OTA"); + bool isSpiffsUpdate = filename.endsWith("_spiffs.bin"); + Serial.printf("Update Start: %s (type: %s)\n", filename.c_str(), isSpiffsUpdate ? "SPIFFS" : "OTA"); if (request->contentLength() == 0) { request->send(400, "application/json", "{\"status\":\"error\",\"message\":\"Invalid file size\"}"); return; } - // Berechne verfügbaren Speicherplatz - size_t updateSize = request->contentLength(); - size_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; - - Serial.printf("Update size: %u bytes\n", updateSize); - Serial.printf("Available space: %u bytes\n", maxSketchSpace); - - if (updateSize > maxSketchSpace) { - Serial.printf("Error: Not enough space. Need %u bytes but only have %u bytes available\n", - updateSize, maxSketchSpace); - request->send(400, "application/json", - "{\"status\":\"error\",\"message\":\"Not enough space for update\"}"); - return; - } - + // Stop tasks before update if (!tasksAreStopped && (RfidReaderTask || BambuMqttTask || ScaleTask)) { stopAllTasks(); tasksAreStopped = true; } - // Ensure SPIFFS is ended before update - if (SPIFFS.begin()) { - SPIFFS.end(); - } - - bool success; - if (isFullImage) { - success = Update.begin(updateSize, U_FLASH); - } else { - if (data[0] != 0xE9) { - Serial.printf("Wrong magic byte: 0x%02X (expected 0xE9)\n", data[0]); + size_t updateSize = request->contentLength(); + + if (isSpiffsUpdate) { + if (!SPIFFS.begin(true)) { request->send(400, "application/json", - "{\"status\":\"error\",\"message\":\"Invalid firmware format\"}"); + "{\"status\":\"error\",\"message\":\"Could not mount SPIFFS\"}"); + return; + } + + // Start SPIFFS update + if (!Update.begin(updateSize, U_SPIFFS)) { + Update.printError(Serial); + request->send(400, "application/json", + "{\"status\":\"error\",\"message\":\"SPIFFS update initialization failed\"}"); + return; + } + } else { + // Regular OTA update + stagingFile = SPIFFS.open("/firmware.bin", "w"); + if (!stagingFile) { + request->send(400, "application/json", + "{\"status\":\"error\",\"message\":\"Could not create staging file\"}"); return; } - success = Update.begin(updateSize); - } - - if (!success) { - Update.printError(Serial); - request->send(400, "application/json", - "{\"status\":\"error\",\"message\":\"Update initialization failed\"}"); - return; } } - if (Update.write(data, len) != len) { - Update.printError(Serial); - request->send(400, "application/json", - "{\"status\":\"error\",\"message\":\"Write failed\"}"); - return; + if (stagingFile) { + // Stage 1: Write to SPIFFS + if (stagingFile.write(data, len) != len) { + stagingFile.close(); + SPIFFS.remove("/firmware.bin"); + request->send(400, "application/json", + "{\"status\":\"error\",\"message\":\"Write to SPIFFS failed\"}"); + return; + } + } else { + // Direct SPIFFS update + if (Update.write(data, len) != len) { + Update.printError(Serial); + request->send(400, "application/json", + "{\"status\":\"error\",\"message\":\"Write failed\"}"); + return; + } } if (final) { - if (!Update.end(true)) { - Update.printError(Serial); - request->send(400, "application/json", - "{\"status\":\"error\",\"message\":\"Update failed\"}"); - return; + if (stagingFile) { + // Finish Stage 1 + stagingFile.close(); + Serial.println("Stage 1 complete - firmware staged in SPIFFS"); + request->send(200, "application/json", + "{\"status\":\"success\",\"message\":\"Update staged successfully! Starting stage 2...\"}"); + performStageTwo(); + } else { + // Finish direct SPIFFS update + if (!Update.end(true)) { + Update.printError(Serial); + request->send(400, "application/json", + "{\"status\":\"error\",\"message\":\"Update failed\"}"); + return; + } + Serial.println("SPIFFS update successful, restarting..."); + request->send(200, "application/json", + "{\"status\":\"success\",\"message\":\"SPIFFS update successful! Device will restart...\",\"restart\":true}"); + delay(500); + ESP.restart(); } - Serial.println("Update successful, restarting..."); - request->send(200, "application/json", - "{\"status\":\"success\",\"message\":\"Update successful! Device will restart...\",\"restart\":true}"); - delay(500); - ESP.restart(); } }