diff --git a/docs/bootloader-compatibility.md b/docs/bootloader-compatibility.md new file mode 100644 index 000000000..1729817f7 --- /dev/null +++ b/docs/bootloader-compatibility.md @@ -0,0 +1,82 @@ +# Bootloader Compatibility Checking + +As of WLED 0.16, the firmware includes bootloader version checking to prevent incompatible OTA updates that could cause boot loops. + +## Background + +ESP32 devices use different bootloader versions: +- **V2 Bootloaders**: Legacy bootloaders (ESP-IDF < 4.4) +- **V3 Bootloaders**: Intermediate bootloaders (ESP-IDF 4.4+) +- **V4 Bootloaders**: Modern bootloaders (ESP-IDF 5.0+) with rollback support + +WLED 0.16+ requires V4 bootloaders for full compatibility and safety features. + +## Checking Your Bootloader Version + +### Method 1: Web Interface +Visit your WLED device at: `http://your-device-ip/json/bootloader` + +This will return JSON like: +```json +{ + "version": 4, + "rollback_capable": true, + "esp_idf_version": 50002 +} +``` + +### Method 2: Serial Console +Enable debug output and look for bootloader version messages during startup. + +## OTA Update Behavior + +When uploading firmware via OTA: + +1. **Compatible Bootloader**: Update proceeds normally +2. **Incompatible Bootloader**: Update is blocked with error message: + > "Bootloader incompatible! Please update to a newer bootloader first." +3. **No Metadata**: Update proceeds (for backward compatibility with older firmware) + +## Upgrading Your Bootloader + +If you have an incompatible bootloader, you have several options: + +### Option 1: Serial Flash (Recommended) +Use the [WLED web installer](https://install.wled.me) to flash via USB cable. This will install the latest bootloader and firmware. + +### Option 2: Staged Update +1. First update to WLED 0.15.x (which supports your current bootloader) +2. Then update to WLED 0.16+ (0.15.x may include bootloader update) + +### Option 3: ESP Tool +Use esptool.py to manually flash a new bootloader (advanced users only). + +## For Firmware Builders + +When building custom firmware that requires V4 bootloader: + +```bash +# Add bootloader requirement to your binary +python3 tools/add_bootloader_metadata.py firmware.bin 4 +``` + +## Technical Details + +- Metadata format: ASCII string `WLED_BOOTLOADER:X` where X is required version (1-9) +- Checked in first 512 bytes of uploaded firmware +- Uses ESP-IDF version and rollback capability to detect current bootloader +- Backward compatible with firmware without metadata + +## Troubleshooting + +**Error: "Bootloader incompatible!"** +- Use web installer to update via USB +- Or use staged update through 0.15.x + +**How to check if I need an update?** +- Visit `/json/bootloader` endpoint +- If version < 4, you may need to update for future firmware + +**Can I force an update?** +- Not recommended - could brick your device +- Use proper upgrade path instead \ No newline at end of file diff --git a/tools/add_bootloader_metadata.py b/tools/add_bootloader_metadata.py index 808acafdc..623be45cc 100755 --- a/tools/add_bootloader_metadata.py +++ b/tools/add_bootloader_metadata.py @@ -29,6 +29,22 @@ def add_bootloader_metadata(binary_file, required_version): # Create metadata string metadata = f"WLED_BOOTLOADER:{version}" + # Check if metadata already exists + try: + with open(binary_file, 'rb') as f: + content = f.read() + + if metadata.encode('ascii') in content: + print(f"File already contains bootloader v{version} requirement") + return True + + # Check for any bootloader metadata + if b"WLED_BOOTLOADER:" in content: + print("Warning: File already contains bootloader metadata. Adding new requirement.") + except Exception as e: + print(f"Error reading file: {e}") + return False + # Append metadata to file try: with open(binary_file, 'ab') as f: diff --git a/wled00/wled_server.cpp b/wled00/wled_server.cpp index ae09bfd17..fa5699cad 100644 --- a/wled00/wled_server.cpp +++ b/wled00/wled_server.cpp @@ -388,6 +388,26 @@ void initServer() createEditHandler(correctPIN); +#ifndef WLED_DISABLE_OTA + // Bootloader info endpoint for troubleshooting + server.on("/json/bootloader", HTTP_GET, [](AsyncWebServerRequest *request){ + AsyncJsonResponse *response = new AsyncJsonResponse(128); + JsonObject root = response->getRoot(); + + root[F("version")] = getBootloaderVersion(); + #ifdef ESP32 + root[F("rollback_capable")] = Update.canRollBack(); + root[F("esp_idf_version")] = ESP_IDF_VERSION; + #else + root[F("rollback_capable")] = false; + root[F("platform")] = F("ESP8266"); + #endif + + response->setLength(); + request->send(response); + }); +#endif + static const char _update[] PROGMEM = "/update"; #ifndef WLED_DISABLE_OTA //init ota page @@ -449,7 +469,8 @@ void initServer() DEBUG_PRINTF_P(PSTR("Bootloader incompatible! Current: v%d, Required: v%d\n"), getBootloaderVersion(), required_version); request->send(400, FPSTR(CONTENT_TYPE_PLAIN), - F("Bootloader incompatible! Please update to a newer bootloader first.")); + F("Bootloader incompatible! This firmware requires bootloader v4+. " + "Please update via USB using install.wled.me first, or use WLED 0.15.x.")); return; } DEBUG_PRINTLN(F("Bootloader compatibility check passed"));