diff --git a/.github/workflows/providers/gitea-release.yml b/.github/workflows/providers/gitea-release.yml index 7febf6d..b226720 100644 --- a/.github/workflows/providers/gitea-release.yml +++ b/.github/workflows/providers/gitea-release.yml @@ -46,27 +46,56 @@ jobs: - name: Build Firmware run: | source venv/bin/activate + # Ensure clean build + platformio run -t clean + echo "Building SPIFFS..." platformio run -t buildfs echo "Building firmware..." platformio run + + # Get esp32 bootloader + python -c "import pkg_resources; print(pkg_resources.resource_filename('platformio', 'packages/framework-arduinoespressif32/tools/sdk/esp32/bin/bootloader_dio_40m.bin'))" > bootloader_path.txt + cp $(cat bootloader_path.txt) .pio/build/esp32dev/bootloader.bin - name: Create Release Files run: | source venv/bin/activate - echo "Creating release files..." + + # Verify file existence and sizes + echo "Verifying build files..." + ls -lh .pio/build/esp32dev/bootloader.bin + ls -lh .pio/build/esp32dev/partitions.bin + ls -lh .pio/build/esp32dev/firmware.bin + ls -lh .pio/build/esp32dev/spiffs.bin + ls -lh .pio/build/esp32dev/ota_data_initial.bin + + # Create OTA file (firmware only) + echo "Creating OTA update binary..." + cp .pio/build/esp32dev/firmware.bin .pio/build/esp32dev/filaman_ota.bin + + # Create full flash binary + echo "Creating full binary..." esptool.py --chip esp32 merge_bin \ + -o .pio/build/esp32dev/filaman_full.bin \ --flash_mode dio \ --flash_freq 40m \ --flash_size 4MB \ - -o .pio/build/esp32dev/filaman_full.bin \ - 0x1000 .pio/build/esp32dev/bootloader.bin \ + 0x0000 .pio/build/esp32dev/bootloader.bin \ 0x8000 .pio/build/esp32dev/partitions.bin \ + 0xe000 .pio/build/esp32dev/ota_data_initial.bin \ 0x10000 .pio/build/esp32dev/firmware.bin \ 0x3D0000 .pio/build/esp32dev/spiffs.bin - cp .pio/build/esp32dev/firmware.bin .pio/build/esp32dev/filaman_ota.bin + # Verify created files and show sizes + echo "Verifying created files..." + ls -lh .pio/build/esp32dev/filaman_full.bin + ls -lh .pio/build/esp32dev/filaman_ota.bin + + # Show binary info + echo "Binary information:" + esptool.py --chip esp32 image_info .pio/build/esp32dev/filaman_full.bin - name: Read CHANGELOG.md id: changelog diff --git a/platformio.ini b/platformio.ini index fa8666d..65312db 100644 --- a/platformio.ini +++ b/platformio.ini @@ -65,6 +65,7 @@ build_flags = -DCONFIG_BOOTLOADER_RESERVE_RTC_SIZE=0x1000 -DCONFIG_PARTITION_TABLE_OFFSET=0x8000 -DCONFIG_PARTITION_TABLE_MD5=y + -DBOOT_APP_PARTITION_OTA_0=1 extra_scripts = scripts/extra_script.py @@ -72,6 +73,7 @@ extra_scripts = pre:scripts/pre_spiffs.py ; wird als zweites ausgeführt pre:scripts/combine_html.py ; wird als drittes ausgeführt scripts/gzip_files.py + post:post_extra_script.py ; Remove or comment out the targets line ;targets = buildfs, build diff --git a/scripts/post_extra_script.py b/scripts/post_extra_script.py new file mode 100644 index 0000000..27befd0 --- /dev/null +++ b/scripts/post_extra_script.py @@ -0,0 +1,22 @@ +Import("env") +import os + +def create_ota_data_initial(source, target, env): + build_dir = env.subst("$BUILD_DIR") + ota_data_path = os.path.join(build_dir, "ota_data_initial.bin") + + # WLED-style OTA data initialization + # First 32 bytes are for the first OTA slot + # Next 32 bytes are for the second OTA slot + # Pattern: 0x00 for the running partition, 0x55 for others + ota_data = bytearray([0x00] * 32 + [0x55] * 32) + + # Fill the rest with 0xFF + ota_data.extend([0xFF] * (0x2000 - len(ota_data))) + + with open(ota_data_path, 'wb') as f: + f.write(ota_data) + + print(f"Created ota_data_initial.bin ({len(ota_data)} bytes)") + +env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", create_ota_data_initial) \ No newline at end of file diff --git a/src/ota.cpp b/src/ota.cpp index 0c623b4..88abbe8 100644 --- a/src/ota.cpp +++ b/src/ota.cpp @@ -22,23 +22,47 @@ void stopAllTasks() { } void handleOTAUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) { + static size_t contentLength = 0; + static bool isFullImage = false; + if (!index) { - Serial.printf("Update Start: %s\n", filename.c_str()); - if (request->contentLength() == 0) { + contentLength = request->contentLength(); + Serial.printf("Update Start: %s (size: %u bytes)\n", filename.c_str(), contentLength); + + if (contentLength == 0) { request->send(400, "application/json", "{\"status\":\"error\",\"message\":\"Invalid file size\"}"); return; } + // Bei full.bin muss das Magic Byte nicht geprüft werden + isFullImage = (contentLength > 0x300000); + if (!isFullImage && data[0] != ESP_MAGIC) { + Serial.printf("Wrong magic byte: 0x%02X (expected 0x%02X)\n", data[0], ESP_MAGIC); + request->send(400, "application/json", "{\"status\":\"error\",\"message\":\"Invalid firmware format\"}"); + return; + } + if (!tasksAreStopped && (RfidReaderTask || BambuMqttTask || ScaleTask)) { stopAllTasks(); tasksAreStopped = true; } - if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { + if (!Update.begin(contentLength, isFullImage ? U_FLASH : U_FLASH)) { Update.printError(Serial); request->send(400, "application/json", "{\"status\":\"error\",\"message\":\"OTA could not begin\"}"); return; } + + Serial.printf("Starting %s update\n", isFullImage ? "full" : "firmware"); + } + + // Debug output für die ersten paar Bytes + if (index == 0) { + Serial.printf("First bytes: "); + for(size_t i = 0; i < min(16UL, len); i++) { + Serial.printf("%02X ", data[i]); + } + Serial.println(); } if (Update.write(data, len) != len) { @@ -53,6 +77,7 @@ void handleOTAUpload(AsyncWebServerRequest *request, String filename, size_t ind request->send(400, "application/json", "{\"status\":\"error\",\"message\":\"OTA end failed\"}"); return; } + Serial.println("Update successful, restarting..."); request->send(200, "application/json", "{\"status\":\"success\",\"message\":\"Update successful! Device will restart...\",\"restart\":true}"); delay(500); ESP.restart();