From aed82210842665d925da00aa206f47467b0f260f Mon Sep 17 00:00:00 2001 From: wled-develop Date: Sun, 15 Mar 2026 12:19:11 +0100 Subject: [PATCH 1/4] Feature: Add SafeBoot compatibility --- wled00/cfg.cpp | 4 + wled00/file.cpp | 7 ++ wled00/ota_update.cpp | 4 + wled00/presets.cpp | 4 + wled00/safe_boot_functions.cpp | 149 +++++++++++++++++++++++++++++++++ wled00/safe_boot_functions.h | 17 ++++ 6 files changed, 185 insertions(+) create mode 100644 wled00/safe_boot_functions.cpp create mode 100644 wled00/safe_boot_functions.h diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 97b48c4ab6..1d446c2818 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -1,5 +1,6 @@ #include "wled.h" #include "wled_ethernet.h" +#include "safe_boot_functions.h" /* * Serializes and parses the cfg.json and wsec.json settings files, stored in internal FS. @@ -832,6 +833,9 @@ void serializeConfigToFS() { if (f) serializeJson(root, f); f.close(); releaseJSONBufferLock(); + #ifdef WLED_ENABLE_SAFE_BOOT + update_spiffs_crc(); + #endif configNeedsWrite = false; } diff --git a/wled00/file.cpp b/wled00/file.cpp index dcc2d57c41..568d661a9b 100644 --- a/wled00/file.cpp +++ b/wled00/file.cpp @@ -1,4 +1,5 @@ #include "wled.h" +#include "safe_boot_functions.h" /* * Utility for SPIFFS filesystem @@ -40,6 +41,9 @@ void closeFile() { #endif f.close(); // "if (f)" check is aleady done inside f.close(), and f cannot be nullptr -> no need for double checking before closing the file handle. DEBUGFS_PRINTF("took %lu ms\n", millis() - s); + #ifdef WLED_ENABLE_SAFE_BOOT + update_spiffs_crc(); + #endif doCloseFile = false; } @@ -478,6 +482,9 @@ bool copyFile(const char* src_path, const char* dst_path) { DEBUG_PRINTLN(F("copy failed")); WLED_FS.remove(dst_path); // delete incomplete file } + #ifdef WLED_ENABLE_SAFE_BOOT + else {update_spiffs_crc();} + #endif return success; } diff --git a/wled00/ota_update.cpp b/wled00/ota_update.cpp index 4aa7830bab..ca4f41793a 100644 --- a/wled00/ota_update.cpp +++ b/wled00/ota_update.cpp @@ -1,5 +1,6 @@ #include "ota_update.h" #include "wled.h" +#include "safe_boot_functions.h" #ifdef ESP32 #include @@ -96,6 +97,9 @@ static void endOTA(AsyncWebServerRequest *request) { #ifndef ESP8266 bootloopCheckOTA(); // let the bootloop-checker know there was an OTA update #endif + #ifdef WLED_ENABLE_SAFE_BOOT + update_ota_crc(); + #endif doReboot = true; context->needsRestart = false; } diff --git a/wled00/presets.cpp b/wled00/presets.cpp index 9023baf344..8bb2587e5d 100644 --- a/wled00/presets.cpp +++ b/wled00/presets.cpp @@ -1,4 +1,5 @@ #include "wled.h" +#include "safe_boot_functions.h" /* * Methods to handle saving and loading presets to/from the filesystem @@ -114,6 +115,9 @@ void initPresetsFile() } serializeJson(doc, f); f.close(); + #ifdef WLED_ENABLE_SAFE_BOOT + update_spiffs_crc(); + #endif } bool applyPresetFromPlaylist(byte index) diff --git a/wled00/safe_boot_functions.cpp b/wled00/safe_boot_functions.cpp new file mode 100644 index 0000000000..6fb350ddf9 --- /dev/null +++ b/wled00/safe_boot_functions.cpp @@ -0,0 +1,149 @@ +/* + This functions are used to use WLED together with "Safe Bootbootloader" + https://github.com/wled-install/SafeBootloader +*/ +#ifdef WLED_ENABLE_SAFE_BOOT +#include "safe_boot_functions.h" +#include "wled.h" +#include + +#define SPIFFS_CRC_MAGIC 0x53504653 + +typedef struct { + uint32_t magic; + uint32_t crc_app; // CRC OTA + uint32_t crc_spiffs; // CRC SPIFFS / Backup + uint32_t size_spiffs; // Größe SPIFFS / Backup +} crc_group_t; + +uint32_t calc_crc(const esp_partition_t* part) +{ + const size_t bufsize = 4096; + uint8_t buf[bufsize]; + + uint32_t crc = 0; + size_t offset = 0; + + while (offset < part->size) { + size_t toread = bufsize; + if (offset + toread > part->size) + toread = part->size - offset; + + esp_partition_read(part, offset, buf, toread); + + crc = crc32_le(crc, buf, toread); + offset += toread; + } + + return crc; +} + +void update_ota_crc() +{ + const esp_partition_t* update_part = + esp_ota_get_next_update_partition(NULL); + + const esp_partition_t* crc0part = + esp_partition_find_first( + ESP_PARTITION_TYPE_DATA, + static_cast(0x41), + "crc0"); + + const esp_partition_t* crc1part = + esp_partition_find_first( + ESP_PARTITION_TYPE_DATA, + static_cast(0x42), + "crc1"); + + if (!update_part || !crc0part || !crc1part) { + Serial.println(F("OTA or CRC partition not found!")); + return; + } + + Serial.printf("Updated OTA partition: %s\n", update_part->label); + + const esp_partition_t* crcpart = nullptr; + + if (update_part->subtype == ESP_PARTITION_SUBTYPE_APP_OTA_0) { + crcpart = crc0part; + Serial.println(F("Writing CRC to crc0")); + } + else if (update_part->subtype == ESP_PARTITION_SUBTYPE_APP_OTA_1) { + crcpart = crc1part; + Serial.println(F("Writing CRC to crc1")); + } + else { + Serial.println(F("Unexpected OTA subtype")); + return; + } + + uint32_t crc = calc_crc(update_part); + + Serial.printf("Calculated OTA CRC: 0x%08X\n", crc); + + crc_group_t stored = {}; + esp_partition_read(crcpart, 0, &stored, sizeof(stored)); + + if (stored.magic == SPIFFS_CRC_MAGIC && + stored.crc_app == crc) + { + Serial.println(F("OTA CRC matches stored value")); + return; + } + + stored.magic = SPIFFS_CRC_MAGIC; + stored.crc_app = crc; + + esp_partition_erase_range(crcpart, 0, 4096); + esp_partition_write(crcpart, 0, &stored, sizeof(stored)); + + Serial.println(F("OTA CRC updated.")); +} + +bool update_spiffs_crc() +{ + const esp_partition_t* spiffs = + esp_partition_find_first( + ESP_PARTITION_TYPE_DATA, + ESP_PARTITION_SUBTYPE_DATA_SPIFFS, + "spiffs"); + + const esp_partition_t* crcpart = + esp_partition_find_first( + ESP_PARTITION_TYPE_DATA, + static_cast(0x41), // crc subtype + "crc0"); + + if (!spiffs || !crcpart) { + DEBUG_PRINTLN(F("SPIFFS or CRC partition not found!")); + return false; + } + + + uint32_t crc = calc_crc(spiffs); + + crc_group_t stored = {}; + esp_partition_read(crcpart, 0, &stored, sizeof(stored)); + + // prüfen ob identisch + if (stored.magic == SPIFFS_CRC_MAGIC && + stored.crc_spiffs == crc && + stored.size_spiffs == spiffs->size) + { + // nichts zu tun + DEBUG_PRINTLN(F("SPIFFS CRC matches stored value, no update needed.")); + return true; + } + + stored.magic = SPIFFS_CRC_MAGIC; + stored.crc_spiffs = crc; + stored.size_spiffs = spiffs->size; + DEBUG_PRINTLN(F("Updating SPIFFS CRC partition with new values.")); + esp_partition_erase_range(crcpart, 0, 4096); + esp_partition_write(crcpart, 0, &stored, sizeof(stored)); + DEBUG_PRINTLN(F("SPIFFS CRC partition updated.")); + + return true; +} + +#endif \ No newline at end of file diff --git a/wled00/safe_boot_functions.h b/wled00/safe_boot_functions.h new file mode 100644 index 0000000000..35f065ee4a --- /dev/null +++ b/wled00/safe_boot_functions.h @@ -0,0 +1,17 @@ +/* + This functions are used to use WLED together with "Safe Bootbootloader" + https://github.com/wled-install/SafeBootloader +*/ + +#pragma once + +#ifdef WLED_ENABLE_SAFE_BOOT +#include +#include +#include +#include + +bool update_spiffs_crc(); +void update_ota_crc(); + +#endif \ No newline at end of file From d1045a3a37174ea5a7b4a1c6fe73973fefafce33 Mon Sep 17 00:00:00 2001 From: wled-develop Date: Sun, 15 Mar 2026 12:27:26 +0100 Subject: [PATCH 2/4] Add SafeBoot compatibile partition table --- tools/WLED_ESP32_8MB_SafeBoot.csv | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tools/WLED_ESP32_8MB_SafeBoot.csv diff --git a/tools/WLED_ESP32_8MB_SafeBoot.csv b/tools/WLED_ESP32_8MB_SafeBoot.csv new file mode 100644 index 0000000000..8dc47b8c1c --- /dev/null +++ b/tools/WLED_ESP32_8MB_SafeBoot.csv @@ -0,0 +1,10 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x240000, +app1, app, ota_1, 0x250000, 0x240000, +spiffs, data, spiffs, 0x490000, 0x100000, +spiffs_backup, data, 0x40, 0x590000, 0x100000, +crc0, data, 0x41, 0x690000, 0x1000, +crc1, data, 0x42, 0x691000, 0x1000, +coredump, data, coredump,,64K From 16ac74d3ce7ee867ea3090b579bbaf339bf510a1 Mon Sep 17 00:00:00 2001 From: wled-develop Date: Sun, 15 Mar 2026 19:13:05 +0100 Subject: [PATCH 3/4] Use SHA for OTA instead of CRC --- wled00/ota_update.cpp | 4 --- wled00/safe_boot_functions.cpp | 63 ---------------------------------- wled00/safe_boot_functions.h | 1 - 3 files changed, 68 deletions(-) diff --git a/wled00/ota_update.cpp b/wled00/ota_update.cpp index ca4f41793a..4aa7830bab 100644 --- a/wled00/ota_update.cpp +++ b/wled00/ota_update.cpp @@ -1,6 +1,5 @@ #include "ota_update.h" #include "wled.h" -#include "safe_boot_functions.h" #ifdef ESP32 #include @@ -97,9 +96,6 @@ static void endOTA(AsyncWebServerRequest *request) { #ifndef ESP8266 bootloopCheckOTA(); // let the bootloop-checker know there was an OTA update #endif - #ifdef WLED_ENABLE_SAFE_BOOT - update_ota_crc(); - #endif doReboot = true; context->needsRestart = false; } diff --git a/wled00/safe_boot_functions.cpp b/wled00/safe_boot_functions.cpp index 6fb350ddf9..242b87397d 100644 --- a/wled00/safe_boot_functions.cpp +++ b/wled00/safe_boot_functions.cpp @@ -11,7 +11,6 @@ typedef struct { uint32_t magic; - uint32_t crc_app; // CRC OTA uint32_t crc_spiffs; // CRC SPIFFS / Backup uint32_t size_spiffs; // Größe SPIFFS / Backup } crc_group_t; @@ -38,68 +37,6 @@ uint32_t calc_crc(const esp_partition_t* part) return crc; } -void update_ota_crc() -{ - const esp_partition_t* update_part = - esp_ota_get_next_update_partition(NULL); - - const esp_partition_t* crc0part = - esp_partition_find_first( - ESP_PARTITION_TYPE_DATA, - static_cast(0x41), - "crc0"); - - const esp_partition_t* crc1part = - esp_partition_find_first( - ESP_PARTITION_TYPE_DATA, - static_cast(0x42), - "crc1"); - - if (!update_part || !crc0part || !crc1part) { - Serial.println(F("OTA or CRC partition not found!")); - return; - } - - Serial.printf("Updated OTA partition: %s\n", update_part->label); - - const esp_partition_t* crcpart = nullptr; - - if (update_part->subtype == ESP_PARTITION_SUBTYPE_APP_OTA_0) { - crcpart = crc0part; - Serial.println(F("Writing CRC to crc0")); - } - else if (update_part->subtype == ESP_PARTITION_SUBTYPE_APP_OTA_1) { - crcpart = crc1part; - Serial.println(F("Writing CRC to crc1")); - } - else { - Serial.println(F("Unexpected OTA subtype")); - return; - } - - uint32_t crc = calc_crc(update_part); - - Serial.printf("Calculated OTA CRC: 0x%08X\n", crc); - - crc_group_t stored = {}; - esp_partition_read(crcpart, 0, &stored, sizeof(stored)); - - if (stored.magic == SPIFFS_CRC_MAGIC && - stored.crc_app == crc) - { - Serial.println(F("OTA CRC matches stored value")); - return; - } - - stored.magic = SPIFFS_CRC_MAGIC; - stored.crc_app = crc; - - esp_partition_erase_range(crcpart, 0, 4096); - esp_partition_write(crcpart, 0, &stored, sizeof(stored)); - - Serial.println(F("OTA CRC updated.")); -} - bool update_spiffs_crc() { const esp_partition_t* spiffs = diff --git a/wled00/safe_boot_functions.h b/wled00/safe_boot_functions.h index 35f065ee4a..4166ed6e17 100644 --- a/wled00/safe_boot_functions.h +++ b/wled00/safe_boot_functions.h @@ -12,6 +12,5 @@ #include bool update_spiffs_crc(); -void update_ota_crc(); #endif \ No newline at end of file From f489c1c00b046704c08e1327ba786dea6200e1d8 Mon Sep 17 00:00:00 2001 From: wled-install <96419931+wled-install@users.noreply.github.com> Date: Thu, 26 Mar 2026 20:52:05 +0100 Subject: [PATCH 4/4] Update safe_boot_functions.cpp Correct translations --- wled00/safe_boot_functions.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/wled00/safe_boot_functions.cpp b/wled00/safe_boot_functions.cpp index 242b87397d..2370994788 100644 --- a/wled00/safe_boot_functions.cpp +++ b/wled00/safe_boot_functions.cpp @@ -12,7 +12,7 @@ typedef struct { uint32_t magic; uint32_t crc_spiffs; // CRC SPIFFS / Backup - uint32_t size_spiffs; // Größe SPIFFS / Backup + uint32_t size_spiffs; // Size of SPIFFS / Backup } crc_group_t; uint32_t calc_crc(const esp_partition_t* part) @@ -62,12 +62,12 @@ bool update_spiffs_crc() crc_group_t stored = {}; esp_partition_read(crcpart, 0, &stored, sizeof(stored)); - // prüfen ob identisch + // check if magic/crc/size are equal if (stored.magic == SPIFFS_CRC_MAGIC && stored.crc_spiffs == crc && stored.size_spiffs == spiffs->size) { - // nichts zu tun + // nothing to do DEBUG_PRINTLN(F("SPIFFS CRC matches stored value, no update needed.")); return true; } @@ -83,4 +83,4 @@ bool update_spiffs_crc() return true; } -#endif \ No newline at end of file +#endif