Skip to content

🚀 ESP-IDF V5#4838

Draft
netmindz wants to merge 95 commits intomainfrom
V5
Draft

🚀 ESP-IDF V5#4838
netmindz wants to merge 95 commits intomainfrom
V5

Conversation

@netmindz
Copy link
Member

@netmindz netmindz commented Aug 14, 2025

Update the code to handle at least being able to compile against V5


Edit (softhack007): espressif has "silently" changed a lot of APIs, and - in many cases - replacements are not behaving the same way as before. We are lucky that a few "legacy APIs" are still available, like the "V4" I2S driver. So even when "it compiles", there may be a lot more adaptations needed before "it really works".


Helpful information and migration guidance

coding guides

We should make sure that the "V5" branch will still compile for "V4" as a backup solution.
When adding code that only works in the new framework, it should be conditionally compiled, like in the examples below

  • "V5" code that is not specific to new boards
      #if defined(ESP_IDF_VERSION) && (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
      ledcAttach(pwmPin, 25000, 8);  // New API: ledcAttach(pin, freq, resolution)
      #else
      ledcSetup(pwmChannel, 25000, 8);
      // attach the channel to the GPIO to be controlled
      ledcAttachPin(pwmPin, pwmChannel);
      #endif
  • "V5" code only for esp32-P4 or esp32-C6:
 #if defined(CONFIG_IDF_TARGET_ESP32C6)
    // https://docs.espressif.com/projects/esp-idf/en/latest/esp32c6/api-reference/peripherals/gpio.html
    // strapping pins: 4, 5, 8, 9
    if (gpio > 11 && gpio < 14) return false;     // 12-13 USB-JTAG
    if (gpio > 23 && gpio < 31) return false;     // 24-30 SPI FLASH
#elif defined(CONFIG_IDF_TARGET_ESP32P4)
    // strapping pins: 34,35,36,37,38
    if (             gpio <   2) return false;     // NC
    if (gpio > 13 && gpio <  20) return false;     // ESP-Hosted WiFi pins
    if (gpio > 23 && gpio <  26) return false;     // USB Pins
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
  // .... existing code
#else
  // .... existing code
#endif

related



📋 Major Open Points Checklist for ESP-IDF V5 Port

🔧 Core WLED Code

  • PWM/LEDC (Analog LEDs)

    • V5 (all boards): LEDC API breaking changes (discussion, analysis)
    • Breaking changes on C5/C6/P4
    • Dithering for Analog LEDs is not possible anymore (API removed)
    • Impact: Direct LEDC struct access in bus_manager.cpp (lines 549-556) won't work with V5
    • Action needed: Complete rewrite using HAL API calls (see comment for detailed strategy)
  • NeoEsp32RmtHI

    • 🔧 V5 (all ESP32): Check if still needed (platformio.ini:339, 680, 684)
    • Comment: "ToDO: check if NeoESP32RmtHI is still needed with V5 (see discussion in PR#4838)"
  • MQTT

    • 🔧 V5 (all boards): Disabled pending library updates (platformio.ini:229, 251, 341)
    • Comment: "TODO: remove once we have updated library for V5"
  • Infrared (IR)

    • 🔧 V5 (all boards): Disabled pending library updates
  • Fix device identification functions in util.cpp (ref)

    • computeSHA1() and getDeviceId() currently disabled for ESP-IDF ≥5.0
    • ❌ Missing headers: esp_chip_info.h, esp_efuse.h, esp_mac.h
    • ❌ Breaks device fingerprinting functionality
  • Migrate audioreactive usermod I2S driver (ref)

    • ❌ Currently disabled for V5 builds
    • Might requires migration to new I2S driver API (major changes in ESP-IDF 5) - to be verified
  • Replace deprecated ADC constant (ref)

    • Change ADC_ATTEN_DB_11ADC_ATTEN_DB_12 in util.cpp:1177
    • Also affects audioreactive usermod

🏗️ Build System & Dependencies

  • Analyze WLED_VERSION build warning (ref)

    • 🔧 might cause OTA update failure (invalid metadata)
    • ❌ might lead to wrong version showing in UI - "dev" instead of "16.0.0-alphaV5"
  • Fix PlatformIO dependency references (ref)

    • Replace short SHA hashes (e.g., #bc3af18) with long hashes or .zip URLs
    • Prevents intermittent fatal: couldn't find remote ref errors
  • Decide on platform package strategy (ref)

    • Current: Tasmota platform package
    • Alternative: pioarduino for better V5 support
    • Need maintainer decision
  • Disable Usermod CI for V5 branch (ref)

    • Currently generating ~100 extra builds
    • Adds noise until usermods are updated

🔌 Usermods Compatibility

  • Update pwm_outputs usermod for V5 (ref)

    • Add ESP-IDF version guards for LEDC API
    • Apply same pattern as PWM_fan.cpp (already fixed)
  • Add version guards to deep_sleep usermod (ref)

    • Multiple rtc_gpio_*() calls need ESP-IDF version protection
    • Lines 214-238 in deep_sleep.cpp

✅ Items Already Resolved

  • ✅ Network class rename (Network → WLEDNetwork)
  • ✅ mbedtls SHA1 shim for AsyncWebServer
  • ✅ LEDC API guards in bus_manager.cpp and PWM_fan.cpp
  • ✅ DMX driver V5 API updates
  • ✅ RMT conflict mitigation (-DESP32_ARDUINO_NO_RGB_BUILTIN flag)
  • ✅ Usermod registration hybrid mechanism (section + constructor)

Summary by CodeRabbit

  • New Features

    • Broad ESP‑IDF v5 compatibility and conditional platform gates.
  • Bug Fixes

    • Unified network reporting via a single network layer; safer DMX/RDM handling; PWM/LEDC init/cleanup and WebSocket payload guards improved; multiple usermods/displays migrated to unified network accessor.
  • Chores

    • Large build manifest/environment reorganization and dependency/tool upgrades.
  • Documentation

    • Added BLE requirements guide for a pixels usermod.
  • Refactor

    • Centralized network interface, typed constants, SHA‑1 compatibility shim, and major bootloader OTA validation/size handling overhaul.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 14, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d0abc968-5788-440e-94aa-e00d092645c3

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Refactors build and platform compatibility for ESP32/ESP-IDF v5: gates NeoEsp32RmtHI compilation by WLED_USE_SHARED_RMT, renames Network → WLEDNetwork across the codebase, updates LEDC/ETH/UDP/MDNS/NTP APIs for IDF v5, adds SHA-1 shim, DMX RDM personality changes, and updates platformio/requirements.

Changes

Cohort / File(s) Summary
NeoEsp32RmtHI guard
lib/NeoESP32RmtHI/include/NeoEsp32RmtHIMethod.h, lib/NeoESP32RmtHI/src/NeoEsp32RmtHIMethod.cpp, lib/NeoESP32RmtHI/src/NeoEsp32RmtHI.S
Wrapped RMT driver headers/source/ASM with #if !defined(WLED_USE_SHARED_RMT) to exclude when shared RMT is used.
Network core rename
wled00/src/dependencies/network/Network.h, wled00/src/dependencies/network/Network.cpp
Renamed NetworkClassWLEDNetworkClass and global NetworkWLEDNetwork; adjusted localMAC handling for ESP32 Ethernet.
Network API propagation
wled00/*, wled00/src/dependencies/e131/ESPAsyncE131.cpp, wled00/src/dependencies/espalexa/Espalexa.h, usermods/*, lib/*, wled00/src/dependencies/network/*
Replaced Network.* references with WLEDNetwork.* across many modules (json, udp, improv, e131, espalexa, usermods, wled.cpp/h, xml, bus_manager, etc.) and updated WLED_CONNECTED macro.
Build/Platform manifest
platformio.ini, requirements.txt
Added ESP-IDF v5 manifest/envs, introduced -D WLED_USE_SHARED_RMT and other build flags, updated library versions and Python/dev deps.
LEDC / PWM modernization
wled00/bus_manager.cpp, usermods/PWM_fan/PWM_fan.cpp
Conditionalized LEDC API for ESP-IDF ≥5: switch ledcSetup/ledcAttachPinledcAttach and ledcDetachPinledcDetach; changed semaphore type to SemaphoreHandle_t.
DMX / RDM updates
wled00/dmx_input.h, wled00/dmx_input.cpp
RDM callbacks changed to receive separate request/response headers; added personalities array and createPersonalities(), switched driver install to use personalities count, added null checks and DMX_CONFIG_DEFAULT usage.
SHA-1 shim
wled00/mbedtls_sha1_shim.cpp, wled00/fcn_declare.h, wled00/util.cpp
Added mbedtls SHA-1 wrappers mapping to Arduino SHA1Builder for ESP32 (IDF ≥5.2.0); guarded computeSHA1/getDeviceId/device fingerprint code for IDF v5+.
Ethernet / ETH API changes
wled00/network.cpp
Reordered ETH.begin() parameters to match updated API (PHY type first, then address, mdc/mdio, power, clock mode) and minor formatting fixes.
NeoPixelBus / bus wrapper shim
wled00/bus_wrapper.h
Added macro remapping for NeoPixelBus RMT/I2S method names on ESP32 and defined new 32-bit host pin constants for ESP32.
NTP / UDP flush handling
wled00/ntp.cpp, wled00/src/dependencies/espalexa/Espalexa.h
Use clear() instead of flush() on ESP-IDF ≥5 where appropriate; adjusted UDP handling and multicast IP sourcing to use WLEDNetwork.localIP().
Misc. API/type modernizations & constants
wled00/const.h, wled00/bus_manager.cpp, wled00/wled.h
Converted macro to constexpr for WLED_MAX_ANALOG_CHANNELS; replaced xSemaphoreHandle with SemaphoreHandle_t; updated WLED_CONNECTED to use WLEDNetwork.isConnected().

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • willmmiles
  • netmindz
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title '🚀 ESP-IDF V5' directly and clearly identifies the main objective of the PR: upgrading the codebase to support ESP-IDF v5.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch V5

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@netmindz netmindz changed the title V5 ESP-IDF V5 Aug 14, 2025
@netmindz netmindz mentioned this pull request Aug 15, 2025
@netmindz netmindz requested a review from willmmiles August 16, 2025 11:30
@netmindz
Copy link
Member Author

I'm not sure if renaming Network is the right approach to fix the conflict @willmmiles

@netmindz
Copy link
Member Author

I am also unsure why we are seeing linker errors for things like mbedtls_sha1_starts

@netmindz netmindz added this to the 0.17.0 milestone Aug 16, 2025
@willmmiles
Copy link
Member

I'm not sure if renaming Network is the right approach to fix the conflict @willmmiles

Hm, it looks like it's just a bag of stateless utility functions. Probably it should be a namespace instead of a class. I don't think there's a practical solution to avoid the collision other than renaming, unless the newer core offers these utilities itself. (In fact I'd also suggest pulling them in to wled00/network.cpp , given the use of WLED-specific #defines.)

(Best pratice would've been that the upstream libraries put everything they do in a namespace, eg. Arduino::Network or somesuch, but here we are.)

I am also unsure why we are seeing linker errors for things like mbedtls_sha1_starts

I believe that the Tasmota platform omits mbedtls to save flash space, as they've gone over to BearSSL. Looks like they're still exposing the headers though - I don't know if that's an IDF bug or something wrong with their build process.

AsyncWebServer needs an SHA1 implementation as it is required for WebSockets. It looks like the upstream AWS folks have switched over to IDFv5 functions and vendored in Espressif's implementation when building on IDFv4. I'll see if I can pull those patches.

@sombree

This comment was marked as resolved.

@netmindz

This comment was marked as resolved.

sombree added a commit to sombree/WLED that referenced this pull request Sep 29, 2025
…(ESP-IDF 5.3.4 / arduino-esp32 v3.1.4).

Based on wled#4838.
sombree added a commit to sombree/WLED that referenced this pull request Sep 29, 2025
…(ESP-IDF 5.3.4 / arduino-esp32 v3.1.4).

Based on wled#4838.
@softhack007
Copy link
Member

ToDO: analyze this build warning

Building .pio\build\esp32dev\bootloader.bin
esptool v5.1.2
Creating ESP32 image...
Merged 2 ELF sections.
Successfully created ESP32 image.
wled00/wled_metadata.cpp:6:4: warning: #warning WLED_VERSION was not set - using default value of 'dev' [-Wcpp]
    6 |   #warning WLED_VERSION was not set - using default value of 'dev'
      |    ^~~~~~~

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
wled00/ota_update.cpp (1)

724-727: ⚠️ Potential issue | 🔴 Critical

Write only the validated bootloader length to flash.

verifyBootloaderImage() updates bootloaderSize, but bytesToWrite is initialized from context->bytesBuffered. If trailing bytes exist, extra unvalidated data is flashed.

Suggested fix
         size_t flashOffset = context->bootloaderOffset;
-        const uint8_t* dataToWrite = context->buffer;
-        size_t bytesToWrite = context->bytesBuffered;
+        const uint8_t* dataToWrite = bootloaderData;
+        size_t bytesToWrite = bootloaderSize;

         // If validation adjusted the pointer, it means we have a partition table at the start
         // In this case, we should skip writing the partition table and write bootloader at 0x1000
         if (bootloaderData != context->buffer) {
           // bootloaderData was adjusted - skip partition table in our data
           size_t partitionTableSize = bootloaderData - context->buffer;
-          dataToWrite = bootloaderData;
-          bytesToWrite = bootloaderSize;
           DEBUG_PRINTF_P(PSTR("Skipping %d bytes of partition table data\n"), partitionTableSize);
         }

Also applies to: 730-735, 738-743

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/ota_update.cpp` around lines 724 - 727, The code writes
context->bytesBuffered to flash even after verifyBootloaderImage() may have
reduced the validated bootloaderSize, so trailing unvalidated bytes can be
flashed; change the write logic in ota_update.cpp (around flashOffset /
dataToWrite / bytesToWrite handling) to clamp bytesToWrite to the validated
bootloaderSize (use the updated bootloaderSize instead of
context->bytesBuffered), and adjust flashOffset/dataToWrite for subsequent
writes to ensure only bootloaderSize bytes are written; update the same pattern
in the other occurrences around the noted lines (730-735, 738-743) so every
write uses min(context->bytesBuffered, bootloaderSize - bytesAlreadyWritten).
♻️ Duplicate comments (2)
wled00/util.cpp (1)

1139-1140: ⚠️ Potential issue | 🟡 Minor

Unify the compile guard for computeSHA1() / getDeviceId().

Line 1139 uses a different guard than the declaration/call sites, which risks target drift over time. Use the same guard pattern here as in the related declaration/use locations.

Suggested fix
-#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)    // ToDO: find a solution to make this work in V5
+#if !defined(ARDUINO_ARCH_ESP32) || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0))    // TODO: find a solution to make this work in V5
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/util.cpp` around lines 1139 - 1140, The preprocessor guard around the
block needs to match the same condition used where computeSHA1() and
getDeviceId() are declared/used; update the guard at the shown location
(currently `#if` ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)) to the exact same
macro expression used at the computeSHA1()/getDeviceId() declaration and call
sites so the functions are consistently included/excluded across compilation
units (locate the guard used alongside the computeSHA1 and getDeviceId
declarations and apply that identical guard here).
platformio.ini (1)

531-531: ⚠️ Potential issue | 🟡 Minor

ESP32 V5 envs are still dropping V5-specific build_unflags.

These environments inherit ${common.build_unflags} instead of ${esp32_idf_V5.build_unflags}, so V5-specific unflags are not consistently applied.

Suggested fix pattern
-build_unflags = ${common.build_unflags}
+build_unflags = ${esp32_idf_V5.build_unflags}

Apply the same replacement to each affected ESP32 V5 environment listed above.

Also applies to: 545-546, 556-556, 572-572, 589-589, 607-607, 783-783

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@platformio.ini` at line 531, Several ESP32 V5 environments are inheriting
${common.build_unflags} instead of the V5-specific
${esp32_idf_V5.build_unflags}, so V5-specific unflags are not applied; update
each ESP32 V5 environment entry in platformio.ini to replace build_unflags =
${common.build_unflags} with build_unflags = ${esp32_idf_V5.build_unflags}
(apply the same replacement for every ESP32 V5 env that currently references
${common.build_unflags}); look for occurrences of the build_unflags key and the
symbols common.build_unflags and esp32_idf_V5.build_unflags to ensure all V5
envs use the V5-specific variable.
🧹 Nitpick comments (1)
usermods/pixels_dice_tray/BLE_REQUIREMENT.md (1)

17-20: Clarify the expected working directory for the copy command.
cp platformio_override.ini.sample ../../../platformio_override.ini is context-sensitive; adding “run from usermods/pixels_dice_tray” would prevent miscopies.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/pixels_dice_tray/BLE_REQUIREMENT.md` around lines 17 - 20, Clarify
the working directory for the cp command by updating the instruction around "cp
platformio_override.ini.sample ../../../platformio_override.ini" to explicitly
state it must be run from the usermods/pixels_dice_tray directory (e.g., "From
the usermods/pixels_dice_tray directory, run: cp platformio_override.ini.sample
../../../platformio_override.ini"); reference the exact command string so
maintainers can locate and edit that line in BLE_REQUIREMENT.md.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@wled00/data/update.htm`:
- Around line 47-50: hideforms() currently hides bootupd and toggles upd but
never displays the "Noupd" / updating state, so uploads can leave a blank page;
update hideforms() to also show the Noupd element (e.g., remove "hide" or add a
visible class to gId('Noupd')) so an “Updating...” state is visible, and make
the same change in the other similar submit handlers that mirror the hideforms
logic (the blocks using gId('bootupd'), toggle('upd') around the submit flow) to
ensure consistent behavior across all submit paths.

In `@wled00/ota_update.cpp`:
- Around line 453-462: The bug is signed-byte handling when converting
bootloaderSHA256Cache bytes to hex in the loop using char b1/b2; change to use
an unsigned byte type (e.g., uint8_t or unsigned char) for the temporary value
and for the nibble operations so right-shifts and masks behave correctly: read
the byte as uint8_t val = (uint8_t)bootloaderSHA256Cache[i], compute high = (val
>> 4) and low = (val & 0x0F), convert each nibble to its ASCII hex char (same +
'0' and +39 adjustment as used) and call result.concat on those chars instead of
relying on signed char b1/b2.
- Around line 415-418: The loop that reads the bootloader into the SHA256
context (using esp_flash_read and sizer.feed) currently ignores read failures
and later marks the bootloader SHA cache valid even on partial reads; modify the
logic so you track a boolean success flag (or return early) and only mark the
cached bootloader SHA as valid if every esp_flash_read returned ESP_OK for all
chunks, otherwise abort hashing and ensure the cache remains invalid (so
getBootloaderSHA256Hex() will trigger a recalculation or report failure); update
the code paths around the loop and where the cache validity flag is set to
depend on this all-reads-succeeded condition.

---

Outside diff comments:
In `@wled00/ota_update.cpp`:
- Around line 724-727: The code writes context->bytesBuffered to flash even
after verifyBootloaderImage() may have reduced the validated bootloaderSize, so
trailing unvalidated bytes can be flashed; change the write logic in
ota_update.cpp (around flashOffset / dataToWrite / bytesToWrite handling) to
clamp bytesToWrite to the validated bootloaderSize (use the updated
bootloaderSize instead of context->bytesBuffered), and adjust
flashOffset/dataToWrite for subsequent writes to ensure only bootloaderSize
bytes are written; update the same pattern in the other occurrences around the
noted lines (730-735, 738-743) so every write uses min(context->bytesBuffered,
bootloaderSize - bytesAlreadyWritten).

---

Duplicate comments:
In `@platformio.ini`:
- Line 531: Several ESP32 V5 environments are inheriting ${common.build_unflags}
instead of the V5-specific ${esp32_idf_V5.build_unflags}, so V5-specific unflags
are not applied; update each ESP32 V5 environment entry in platformio.ini to
replace build_unflags = ${common.build_unflags} with build_unflags =
${esp32_idf_V5.build_unflags} (apply the same replacement for every ESP32 V5 env
that currently references ${common.build_unflags}); look for occurrences of the
build_unflags key and the symbols common.build_unflags and
esp32_idf_V5.build_unflags to ensure all V5 envs use the V5-specific variable.

In `@wled00/util.cpp`:
- Around line 1139-1140: The preprocessor guard around the block needs to match
the same condition used where computeSHA1() and getDeviceId() are declared/used;
update the guard at the shown location (currently `#if` ESP_IDF_VERSION <
ESP_IDF_VERSION_VAL(5, 0, 0)) to the exact same macro expression used at the
computeSHA1()/getDeviceId() declaration and call sites so the functions are
consistently included/excluded across compilation units (locate the guard used
alongside the computeSHA1 and getDeviceId declarations and apply that identical
guard here).

---

Nitpick comments:
In `@usermods/pixels_dice_tray/BLE_REQUIREMENT.md`:
- Around line 17-20: Clarify the working directory for the cp command by
updating the instruction around "cp platformio_override.ini.sample
../../../platformio_override.ini" to explicitly state it must be run from the
usermods/pixels_dice_tray directory (e.g., "From the usermods/pixels_dice_tray
directory, run: cp platformio_override.ini.sample
../../../platformio_override.ini"); reference the exact command string so
maintainers can locate and edit that line in BLE_REQUIREMENT.md.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9297775 and 8f44f1b.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (22)
  • package.json
  • platformio.ini
  • usermods/audioreactive/audio_source.h
  • usermods/pixels_dice_tray/BLE_REQUIREMENT.md
  • usermods/pixels_dice_tray/library.json
  • wled00/bus_manager.cpp
  • wled00/data/update.htm
  • wled00/e131.cpp
  • wled00/fcn_declare.h
  • wled00/improv.cpp
  • wled00/json.cpp
  • wled00/network.cpp
  • wled00/ntp.cpp
  • wled00/ota_update.cpp
  • wled00/ota_update.h
  • wled00/src/dependencies/espalexa/Espalexa.h
  • wled00/udp.cpp
  • wled00/util.cpp
  • wled00/wled.cpp
  • wled00/wled.h
  • wled00/wled_server.cpp
  • wled00/xml.cpp
💤 Files with no reviewable changes (2)
  • wled00/ota_update.h
  • usermods/pixels_dice_tray/library.json
🚧 Files skipped from review as they are similar to previous changes (7)
  • wled00/fcn_declare.h
  • wled00/improv.cpp
  • wled00/json.cpp
  • wled00/wled.cpp
  • wled00/udp.cpp
  • wled00/wled_server.cpp
  • wled00/network.cpp

Comment on lines +47 to +50
function hideforms() {
gId('bootupd').classList.add("hide");
toggle('upd');
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Show the update progress state when submitting.

hideforms() currently hides/toggles form containers but never shows Noupd, so upload can leave a blank page instead of an “Updating...” state.

Suggested fix
 function hideforms() {
-  gId('bootupd').classList.add("hide");
-  toggle('upd');			
+  gId('upd').classList.add("hide");
+  gId('bootupd').classList.add("hide");
+  gId('Noupd').classList.remove("hide");
 }

Also applies to: 58-59, 77-77

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/data/update.htm` around lines 47 - 50, hideforms() currently hides
bootupd and toggles upd but never displays the "Noupd" / updating state, so
uploads can leave a blank page; update hideforms() to also show the Noupd
element (e.g., remove "hide" or add a visible class to gId('Noupd')) so an
“Updating...” state is visible, and make the same change in the other similar
submit handlers that mirror the hideforms logic (the blocks using
gId('bootupd'), toggle('upd') around the submit flow) to ensure consistent
behavior across all submit paths.

Comment on lines +415 to +418
for (uint32_t offset = 0; offset < bootloaderSize; offset += chunkSize) {
size_t readSize = min((size_t)(bootloaderSize - offset), chunkSize);
if (esp_flash_read(NULL, buffer, BOOTLOADER_OFFSET + offset, readSize) == ESP_OK) {
mbedtls_sha256_update(&ctx, buffer, readSize);
sizer.feed(buffer, readSize);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the file structure and locate the relevant lines
cd /root/repo && wc -l wled00/ota_update.cpp

Repository: wled/WLED

Length of output: 113


🏁 Script executed:

# Read the relevant section of the file around lines 415-442
sed -n '400,450p' wled00/ota_update.cpp

Repository: wled/WLED

Length of output: 1550


🏁 Script executed:

# Get more context by reading the entire function to understand control flow
# First, let's find what function contains these lines
sed -n '350,500p' wled00/ota_update.cpp | head -200

Repository: wled/WLED

Length of output: 5010


Mark bootloader SHA256 cache valid only if all flash reads succeed.

Read failures are currently silently ignored. If any read fails, the SHA256 context remains uninitialized or partial, but the cache is still marked valid and persists until invalidated, causing getBootloaderSHA256Hex() to return an invalid digest without recalculation.

Suggested fix
+  bool readOk = true;
   for (uint32_t offset = 0; offset < bootloaderSize; offset += chunkSize) {
     size_t readSize = min((size_t)(bootloaderSize - offset), chunkSize);
-    if (esp_flash_read(NULL, buffer, BOOTLOADER_OFFSET + offset, readSize) == ESP_OK) {
-      sizer.feed(buffer, readSize);
+    if (esp_flash_read(NULL, buffer, BOOTLOADER_OFFSET + offset, readSize) != ESP_OK) {
+      readOk = false;
+      break;
+    }
+    sizer.feed(buffer, readSize);

       size_t hashLen = readSize;
       if (sizer.isSizeKnown()) {
         size_t totalSize = sizer.totalSize();
         if (totalSize > 0 && totalSize <= BOOTLOADER_SIZE) {
           bootloaderSize = totalSize;
           if (offset + readSize > totalSize) {
             hashLen = (totalSize > offset) ? (totalSize - offset) : 0;
           }
         }
       }

       if (hashLen > 0) {
         totalHashLen += hashLen;
         mbedtls_sha256_update(&ctx, buffer, hashLen);
       }
-    }
   }

+  if (!readOk) {
+    mbedtls_sha256_free(&ctx);
+    bootloaderSHA256CacheValid = false;
+    return;
+  }
   mbedtls_sha256_finish(&ctx, bootloaderSHA256Cache);
   mbedtls_sha256_free(&ctx);

   bootloaderSHA256CacheValid = true;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/ota_update.cpp` around lines 415 - 418, The loop that reads the
bootloader into the SHA256 context (using esp_flash_read and sizer.feed)
currently ignores read failures and later marks the bootloader SHA cache valid
even on partial reads; modify the logic so you track a boolean success flag (or
return early) and only mark the cached bootloader SHA as valid if every
esp_flash_read returned ESP_OK for all chunks, otherwise abort hashing and
ensure the cache remains invalid (so getBootloaderSHA256Hex() will trigger a
recalculation or report failure); update the code paths around the loop and
where the cache validity flag is set to depend on this all-reads-succeeded
condition.

Fix signed-byte handling in SHA256 hex conversion.

When bootloaderSHA256Cache bytes >= 0x80 are assigned to a signed char, they sign-extend to negative values. Right-shifting negative values produces incorrect results (e.g., 0xFF becomes -1, which shifts incorrectly). This causes wrong hex digit output for non-ASCII hash bytes.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (5)
wled00/data/update.htm (1)

47-50: ⚠️ Potential issue | 🟡 Minor

Show the “Updating...” state explicitly in hideforms().

The helper hides/toggles forms but does not unhide Noupd, so both submit paths can render a blank page during upload.

Suggested fix
 function hideforms() {
-	gId('bootupd').classList.add("hide");
-	toggle('upd');			
+	gId('upd').classList.add("hide");
+	gId('bootupd').classList.add("hide");
+	gId('Noupd').classList.remove("hide");
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/data/update.htm` around lines 47 - 50, hideforms() currently hides the
bootupd form and toggles upd but never shows the "Updating..." indicator, which
can leave the page blank; update hideforms() to explicitly unhide or show the
Noupd element (e.g. remove its "hide" class or set it visible) so the
"Updating..." state is displayed (refer to hideforms(), gId('bootupd'),
toggle('upd') and the Noupd element).
platformio.ini (3)

130-130: ⚠️ Potential issue | 🟡 Minor

Define WLED_USE_SHARED_RMT in one scope only.

Line [130] defines it globally and Line [337] defines it again for V5. Keep one V5-scoped definition to avoid unintended ESP8266 leakage and config drift.

🛠️ Proposed fix
 [common]
 build_flags =
   ...
-  -D WLED_USE_SHARED_RMT ;; Use standard RMT method instead of incompatible NeoEsp32RmtHI - until updated for V5

 [esp32_idf_V5]
 build_flags = -g
   ...
   -D WLED_USE_SHARED_RMT     ;; ToDO: check if NeoESP32RmtHI is still needed with V5 (see discussion in PR#4838)

Also applies to: 337-337

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@platformio.ini` at line 130, The WLED_USE_SHARED_RMT define is declared twice
causing scope leakage; remove the global definition of WLED_USE_SHARED_RMT (the
one outside the V5 environment) and keep only the V5-scoped definition so it
applies exclusively when building for V5; search for the symbol
WLED_USE_SHARED_RMT in platformio.ini and delete the non-V5/global instance,
leaving the V5-specific define in the env:V5 section.

23-23: ⚠️ Potential issue | 🟡 Minor

Keep test-only Ethernet overrides out of the default V4 regression target.

Line [23] keeps esp32dev_V4 in default_envs, while Line [518] still applies temporary Ethernet/pin test overrides. This makes the V4 regression job non-baseline.

🛠️ Proposed fix
 [env:esp32dev_V4]
 ...
-              -D WLED_USE_ETHERNET -D RLYPIN=-1 -D BTNPIN=-1 ;; TODO: this is just for testing - remove before merging to main 

Also applies to: 518-519

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@platformio.ini` at line 23, The default_envs list includes the test-only
target esp32dev_V4 while the temporary Ethernet/pin overrides are applied to
that same V4 env (esp32dev_V4) later, making the regression baseline
non-baseline; remove esp32dev_V4 from the default_envs value and instead create
or use a separate dedicated test env (or move the Ethernet/pin overrides into a
non-default env) so that esp32dev_V4 is not part of the default regression
targets and the overrides at the esp32dev_V4 block are only applied to the test
env.

531-546: ⚠️ Potential issue | 🟡 Minor

esp32dev and esp32dev_debug should inherit V5-specific build_unflags.

Line [531] and Line [545] currently use ${common.build_unflags}. That bypasses ${esp32_idf_V5.build_unflags} and can re-enable flags intentionally removed for V5.

🛠️ Proposed fix
 [env:esp32dev]
 ...
-build_unflags = ${common.build_unflags}
+build_unflags = ${esp32_idf_V5.build_unflags}
 ...

 [env:esp32dev_debug]
 ...
-build_unflags = ${common.build_unflags}
+build_unflags = ${esp32_idf_V5.build_unflags}
               -D WLED_RELEASE_NAME=\"ESP32\"

Based on learnings: "In PlatformIO projects, prefer using build_unflags in the PlatformIO configuration to remove inherited -D defines rather than using GCC's -U option."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@platformio.ini` around lines 531 - 546, The env sections esp32dev and
esp32dev_debug are inheriting ${common.build_unflags} which skips the
V5-specific unflags; replace those occurrences with
${esp32_idf_V5.build_unflags} so both env:esp32dev and env:esp32dev_debug
inherit the V5-specific build_unflags (keep other build_unflags entries as-is,
e.g., additional -D WLED_RELEASE_NAME=\"ESP32\" in esp32dev_debug).
wled00/ota_update.cpp (1)

415-442: ⚠️ Potential issue | 🔴 Critical

Only mark bootloader SHA cache valid when all flash reads succeed.

Line [417] currently skips failed reads, but Line [441] still sets the cache valid. That can return an incorrect digest from partial data.

🛠️ Proposed fix
 static void calculateBootloaderSHA256() {
+  bool readOk = true;
   // Calculate SHA256
   mbedtls_sha256_context ctx;
   mbedtls_sha256_init(&ctx);
   mbedtls_sha256_starts(&ctx, 0); // 0 = SHA256 (not SHA224)
 ...
   for (uint32_t offset = 0; offset < bootloaderSize; offset += chunkSize) {
     size_t readSize = min((size_t)(bootloaderSize - offset), chunkSize);
-    if (esp_flash_read(NULL, buffer, BOOTLOADER_OFFSET + offset, readSize) == ESP_OK) {
-      sizer.feed(buffer, readSize);
+    if (esp_flash_read(NULL, buffer, BOOTLOADER_OFFSET + offset, readSize) != ESP_OK) {
+      readOk = false;
+      break;
+    }
+    sizer.feed(buffer, readSize);
 ...
-      if (hashLen > 0) {
-        totalHashLen += hashLen;
-        mbedtls_sha256_update(&ctx, buffer, hashLen);
-      }
-    }
+    if (hashLen > 0) {
+      totalHashLen += hashLen;
+      mbedtls_sha256_update(&ctx, buffer, hashLen);
+    }
   }
 
+  if (!readOk) {
+    mbedtls_sha256_free(&ctx);
+    bootloaderSHA256CacheValid = false;
+    return;
+  }
   mbedtls_sha256_finish(&ctx, bootloaderSHA256Cache);
   mbedtls_sha256_free(&ctx);
 
   bootloaderSHA256CacheValid = true;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/ota_update.cpp` around lines 415 - 442, The loop may skip failed
esp_flash_read calls but the code always calls mbedtls_sha256_finish and sets
bootloaderSHA256CacheValid; change this so the SHA finish and
bootloaderSHA256CacheValid = true only occur when all flash reads succeeded. Add
a boolean (e.g., allReadsOk) initialized true, set false if esp_flash_read(...)
!= ESP_OK, and after the loop call mbedtls_sha256_finish(&ctx,
bootloaderSHA256Cache) and set bootloaderSHA256CacheValid = true only if
allReadsOk; always mbedtls_sha256_free(&ctx) to avoid leaks even when reads
failed. Ensure you reference bootloaderSize, chunkSize, sizer, totalHashLen,
bootloaderSHA256Cache, and bootloaderSHA256CacheValid when implementing the
change.
🧹 Nitpick comments (1)
wled00/ota_update.cpp (1)

567-568: Use the target-specific offset constant in bootloader context.

Line [567] hardcodes 0x1000 even though BOOTLOADER_OFFSET is already target-aware.

♻️ Proposed refactor
-  const uint32_t bootloaderOffset = 0x1000;
+  const uint32_t bootloaderOffset = static_cast<uint32_t>(BOOTLOADER_OFFSET);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/ota_update.cpp` around lines 567 - 568, Replace the hardcoded
bootloader offset with the target-aware constant: change the local variable
bootloaderOffset (currently set to 0x1000) to use BOOTLOADER_OFFSET so the code
respects platform-specific offsets; keep maxBootloaderSize as-is and ensure any
subsequent uses/reference of bootloaderOffset in ota_update.cpp continue to
compile against the renamed/updated symbol.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@platformio.ini`:
- Line 130: The WLED_USE_SHARED_RMT define is declared twice causing scope
leakage; remove the global definition of WLED_USE_SHARED_RMT (the one outside
the V5 environment) and keep only the V5-scoped definition so it applies
exclusively when building for V5; search for the symbol WLED_USE_SHARED_RMT in
platformio.ini and delete the non-V5/global instance, leaving the V5-specific
define in the env:V5 section.
- Line 23: The default_envs list includes the test-only target esp32dev_V4 while
the temporary Ethernet/pin overrides are applied to that same V4 env
(esp32dev_V4) later, making the regression baseline non-baseline; remove
esp32dev_V4 from the default_envs value and instead create or use a separate
dedicated test env (or move the Ethernet/pin overrides into a non-default env)
so that esp32dev_V4 is not part of the default regression targets and the
overrides at the esp32dev_V4 block are only applied to the test env.
- Around line 531-546: The env sections esp32dev and esp32dev_debug are
inheriting ${common.build_unflags} which skips the V5-specific unflags; replace
those occurrences with ${esp32_idf_V5.build_unflags} so both env:esp32dev and
env:esp32dev_debug inherit the V5-specific build_unflags (keep other
build_unflags entries as-is, e.g., additional -D WLED_RELEASE_NAME=\"ESP32\" in
esp32dev_debug).

In `@wled00/data/update.htm`:
- Around line 47-50: hideforms() currently hides the bootupd form and toggles
upd but never shows the "Updating..." indicator, which can leave the page blank;
update hideforms() to explicitly unhide or show the Noupd element (e.g. remove
its "hide" class or set it visible) so the "Updating..." state is displayed
(refer to hideforms(), gId('bootupd'), toggle('upd') and the Noupd element).

In `@wled00/ota_update.cpp`:
- Around line 415-442: The loop may skip failed esp_flash_read calls but the
code always calls mbedtls_sha256_finish and sets bootloaderSHA256CacheValid;
change this so the SHA finish and bootloaderSHA256CacheValid = true only occur
when all flash reads succeeded. Add a boolean (e.g., allReadsOk) initialized
true, set false if esp_flash_read(...) != ESP_OK, and after the loop call
mbedtls_sha256_finish(&ctx, bootloaderSHA256Cache) and set
bootloaderSHA256CacheValid = true only if allReadsOk; always
mbedtls_sha256_free(&ctx) to avoid leaks even when reads failed. Ensure you
reference bootloaderSize, chunkSize, sizer, totalHashLen, bootloaderSHA256Cache,
and bootloaderSHA256CacheValid when implementing the change.

---

Nitpick comments:
In `@wled00/ota_update.cpp`:
- Around line 567-568: Replace the hardcoded bootloader offset with the
target-aware constant: change the local variable bootloaderOffset (currently set
to 0x1000) to use BOOTLOADER_OFFSET so the code respects platform-specific
offsets; keep maxBootloaderSize as-is and ensure any subsequent uses/reference
of bootloaderOffset in ota_update.cpp continue to compile against the
renamed/updated symbol.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9297775 and 80e81ee.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (22)
  • package.json
  • platformio.ini
  • usermods/audioreactive/audio_source.h
  • usermods/pixels_dice_tray/BLE_REQUIREMENT.md
  • usermods/pixels_dice_tray/library.json
  • wled00/bus_manager.cpp
  • wled00/data/update.htm
  • wled00/e131.cpp
  • wled00/fcn_declare.h
  • wled00/improv.cpp
  • wled00/json.cpp
  • wled00/network.cpp
  • wled00/ntp.cpp
  • wled00/ota_update.cpp
  • wled00/ota_update.h
  • wled00/src/dependencies/espalexa/Espalexa.h
  • wled00/udp.cpp
  • wled00/util.cpp
  • wled00/wled.cpp
  • wled00/wled.h
  • wled00/wled_server.cpp
  • wled00/xml.cpp
💤 Files with no reviewable changes (2)
  • usermods/pixels_dice_tray/library.json
  • wled00/ota_update.h
🚧 Files skipped from review as they are similar to previous changes (5)
  • wled00/json.cpp
  • wled00/improv.cpp
  • wled00/e131.cpp
  • wled00/wled_server.cpp
  • wled00/fcn_declare.h

Guard against zero-length binary payloads before dereferencing data[0] or data[1]
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
wled00/ws.cpp (1)

87-94: ⚠️ Potential issue | 🟠 Major

Add minimum packet size validation for E1.31 and ArtNet before casting and dereferencing struct fields.

Line 87's guard (len < offset+1) ensures only 2 bytes minimum, but E1.31/ArtNet packet structures require significantly larger buffers to safely access their fields:

  • Art-Net: minimum 18 bytes (accesses art_opcode at offset 8-9, art_length at offset 16-17 in handleE131Packet)
  • E1.31: minimum 125 bytes (accesses property_values[0] at offset 125 in handleE131Packet)

Currently, lines 90 and 93 forward undersized payloads as raw pointers with no length information passed to handleE131Packet(). This mirrors the vulnerability already handled properly by the DDP branch (lines 96-100), which validates minimum size before calling the same handler.

Add explicit size checks before the E1.31 and ArtNet cases (similar to DDP at line 96):

case BINARY_PROTOCOL_E131:
  if (len < 125 + offset) return;
  handleE131Packet((e131_packet_t*)&data[offset], client->remoteIP(), P_E131);
  break;
case BINARY_PROTOCOL_ARTNET:
  if (len < 18 + offset) return;
  handleE131Packet((e131_packet_t*)&data[offset], client->remoteIP(), P_ARTNET);
  break;

Malformed short binary frames will otherwise cause out-of-bounds reads when struct fields are accessed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/ws.cpp` around lines 87 - 94, The switch handling BINARY_PROTOCOL_E131
and BINARY_PROTOCOL_ARTNET casts data+offset to e131_packet_t and can read far
beyond the current buffer; add explicit minimum-size guards using len, offset
and the protocol constants before calling handleE131Packet (mirror the DDP
branch): check that len >= offset + 125 for BINARY_PROTOCOL_E131 and len >=
offset + 18 for BINARY_PROTOCOL_ARTNET, return early on failure, then call
handleE131Packet((e131_packet_t*)&data[offset], client->remoteIP(),
P_E131/P_ARTNET) as before.
♻️ Duplicate comments (6)
wled00/ota_update.cpp (1)

415-442: ⚠️ Potential issue | 🔴 Critical

Prevent caching an invalid bootloader SHA when flash reads/parsing fail.

Line 417 ignores read failures, and Line 441 still marks the cache as valid. This can cache a digest from partial/invalid input.

💡 Proposed fix
 static void calculateBootloaderSHA256() {
   // Calculate SHA256
   mbedtls_sha256_context ctx;
   mbedtls_sha256_init(&ctx);
   mbedtls_sha256_starts(&ctx, 0); // 0 = SHA256 (not SHA224)
@@
   size_t bootloaderSize = BOOTLOADER_SIZE;
   BootloaderImageSizer sizer;
-  size_t totalHashLen = 0;
+  bool readOk = true;
 
   for (uint32_t offset = 0; offset < bootloaderSize; offset += chunkSize) {
     size_t readSize = min((size_t)(bootloaderSize - offset), chunkSize);
-    if (esp_flash_read(NULL, buffer, BOOTLOADER_OFFSET + offset, readSize) == ESP_OK) {
-      sizer.feed(buffer, readSize);
+    if (esp_flash_read(NULL, buffer, BOOTLOADER_OFFSET + offset, readSize) != ESP_OK) {
+      readOk = false;
+      break;
+    }
+    if (!sizer.feed(buffer, readSize) && sizer.hasError()) {
+      readOk = false;
+      break;
+    }
 
       size_t hashLen = readSize;
       if (sizer.isSizeKnown()) {
@@
       if (hashLen > 0) {
-        totalHashLen += hashLen;
         mbedtls_sha256_update(&ctx, buffer, hashLen);
       }
-    }
   }
 
+  if (!readOk) {
+    mbedtls_sha256_free(&ctx);
+    bootloaderSHA256CacheValid = false;
+    return;
+  }
+
   mbedtls_sha256_finish(&ctx, bootloaderSHA256Cache);
   mbedtls_sha256_free(&ctx);
 
   bootloaderSHA256CacheValid = true;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/ota_update.cpp` around lines 415 - 442, The loop currently ignores
esp_flash_read failures and always finalizes and marks
bootloaderSHA256CacheValid true; change the logic in the function containing the
loop so you track overall success (e.g., a bool like bootloaderHashOk)
initialized true, set it false if any esp_flash_read call returns non-ESP_OK or
if sizer/parsing indicates an error/unexpected size, only call
mbedtls_sha256_finish(&ctx, bootloaderSHA256Cache) and set
bootloaderSHA256CacheValid = true when bootloaderHashOk is still true; on
failure, free the mbedtls context and leave bootloaderSHA256CacheValid false (or
clear the cache) so you never cache a digest derived from partial/invalid reads
(referencing bootloaderSize, chunkSize, esp_flash_read, sizer,
mbedtls_sha256_finish, bootloaderSHA256Cache and bootloaderSHA256CacheValid).
wled00/data/update.htm (1)

47-50: ⚠️ Potential issue | 🟡 Minor

Show the explicit “Updating…” state on submit.

On Line 47-50, hideforms() still never reveals Noupd, so submit can leave the page with hidden forms but no progress message.

Suggested fix
 function hideforms() {
-	gId('bootupd').classList.add("hide");
-	toggle('upd');			
+	gId('upd').classList.add("hide");
+	gId('bootupd').classList.add("hide");
+	gId('Noupd').classList.remove("hide");
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/data/update.htm` around lines 47 - 50, The hideforms() function hides
the boot update form (gId('bootupd')) but does not reveal the explicit
"Updating…" progress element, so submissions can leave the UI blank; update
hideforms() to also show the progress/message element (e.g., remove the "hide"
class from the element with id 'Noupd' or otherwise ensure the "Updating…"
indicator is visible) while keeping the existing toggle('upd') behavior so the
user sees the explicit updating state during submit.
platformio.ini (2)

531-531: ⚠️ Potential issue | 🟡 Minor

V5 environments should inherit V5-specific build_unflags.

Line 531 uses ${common.build_unflags} in a V5 environment. This drops ${esp32_idf_V5.build_unflags} and can reintroduce flags that V5 config explicitly removes.

Suggested fix
 [env:esp32dev]
-build_unflags = ${common.build_unflags}
+build_unflags = ${esp32_idf_V5.build_unflags}

Apply the same pattern to other V5 envs currently overriding with ${common.build_unflags}.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@platformio.ini` at line 531, In V5 environments that set build_unflags to
${common.build_unflags}, change them to include the V5-specific override (e.g.,
use ${esp32_idf_V5.build_unflags} instead of ${common.build_unflags}) so the
V5-specific flags are preserved; search for occurrences of build_unflags =
${common.build_unflags} in V5 env blocks and replace them with build_unflags =
${esp32_idf_V5.build_unflags} (or merge both if needed) to avoid reintroducing
flags V5 intends to remove.

518-519: ⚠️ Potential issue | 🟡 Minor

Keep V4 regression env free of test-only Ethernet overrides.

Line 518 adds test-only Ethernet + pin overrides in env:esp32dev_V4, and that env is part of default_envs (Line 23). This skews regression coverage away from a baseline V4 build.

Suggested fix
 [env:esp32dev_V4]
-              -D WLED_USE_ETHERNET -D RLYPIN=-1 -D BTNPIN=-1 ;; TODO: this is just for testing - remove before merging to main 

Move these flags to a dedicated test-only env if needed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@platformio.ini` around lines 518 - 519, Remove the test-only defines "-D
WLED_USE_ETHERNET -D RLYPIN=-1 -D BTNPIN=-1" from the env named env:esp32dev_V4
(which is currently included in default_envs) and instead add a new dedicated
test-only environment (e.g., env:esp32dev_V4_test) that contains those defines;
update default_envs to keep only baseline env:esp32dev_V4 so regression runs use
the clean V4 baseline, and ensure the new test env is not part of default_envs
so it only runs when explicitly requested.
wled00/util.cpp (1)

1139-1140: ⚠️ Potential issue | 🟠 Major

Use a platform-aware ESP-IDF guard here.

Line 1139 uses a raw ESP_IDF_VERSION comparison. In mixed ESP8266/ESP32 builds, this should be guarded by platform first to avoid preprocessor portability issues and keep declaration/definition guards consistent.

Suggested fix
-#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)    // ToDO: find a solution to make this work in V5
+#if !defined(ARDUINO_ARCH_ESP32) || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0))    // TODO: find a solution to make this work in V5
#!/bin/bash
set -euo pipefail

echo "Inspect current util.cpp guard:"
sed -n '1134,1146p' wled00/util.cpp

echo
echo "Inspect related declarations in fcn_declare.h:"
rg -n -C2 'computeSHA1|getDeviceId|ESP_IDF_VERSION' wled00/fcn_declare.h

echo
echo "Find ESP_IDF_VERSION guard styles in codebase:"
rg -n '#if.*ESP_IDF_VERSION' wled00/ -g '*.{h,cpp}'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/util.cpp` around lines 1139 - 1140, Replace the raw ESP_IDF_VERSION
comparison with a platform-aware guard: wrap the existing conditional in a check
that the build is for ESP32 and ESP_IDF_VERSION is defined, e.g. use something
like "#if defined(ARDUINO_ARCH_ESP32) && defined(ESP_IDF_VERSION) &&
(ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5,0,0))" (or the project’s ESP32 macro if
different) so the block around the computeSHA1/getDeviceId-related code in
util.cpp only applies on ESP32 and only when ESP_IDF_VERSION is available; this
avoids preprocessor errors in mixed ESP8266/ESP32 builds.
wled00/json.cpp (1)

703-705: ⚠️ Potential issue | 🟡 Minor

Guard ESP_IDF_VERSION before numeric comparison in the preprocessor condition.

Line 703 currently compares ESP_IDF_VERSION without a defined() guard. If the macro is absent in a build context, it is treated as 0, which can accidentally enable deviceId emission on targets you intended to exclude.

🔧 Proposed fix
-#if !defined(ARDUINO_ARCH_ESP32) || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)) // ToDO: find a solution to make this work in V5
+#if !defined(ARDUINO_ARCH_ESP32) || (defined(ESP_IDF_VERSION) && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0))) // TODO: find a solution to make this work in V5
   root[F("deviceId")] = getDeviceId();
 `#endif`

Run this to verify macro availability and all related guards:

#!/bin/bash
set -euo pipefail

echo "=== json.cpp guard ==="
sed -n '699,707p' wled00/json.cpp

echo
echo "=== ESP_IDF_VERSION usages in preprocessor conditions ==="
rg -n '#\s*(if|elif).*(ESP_IDF_VERSION|ESP_IDF_VERSION_VAL)' --type=cpp --type=h

echo
echo "=== Definitions/fallbacks for ESP_IDF_VERSION and ESP_IDF_VERSION_VAL ==="
rg -n '#\s*define\s+ESP_IDF_VERSION(_VAL)?\b' --type=cpp --type=h
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/json.cpp` around lines 703 - 705, The preprocessor currently compares
ESP_IDF_VERSION without checking if it is defined, which can evaluate to 0
unexpectedly; update the condition that controls emitting
root[F("deviceId")]/getDeviceId() to explicitly test defined(ESP_IDF_VERSION)
before the numeric comparison (e.g. use !defined(ARDUINO_ARCH_ESP32) ||
!defined(ESP_IDF_VERSION) || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5,0,0))) so
the (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(...)) term is only evaluated when
ESP_IDF_VERSION is defined.
🧹 Nitpick comments (1)
wled00/ota_update.cpp (1)

567-568: Use target-aware constant instead of hardcoded bootloader offset.

Line 567 hardcodes 0x1000 even though BOOTLOADER_OFFSET is already defined per target. Reusing the existing constant avoids drift when support for additional targets is enabled.

💡 Proposed refactor
 struct BootloaderUpdateContext {
@@
-  const uint32_t bootloaderOffset = 0x1000;
+  const uint32_t bootloaderOffset = BOOTLOADER_OFFSET;
   const uint32_t maxBootloaderSize = 0x10000; // 64KB buffer size
 };

Based on learnings: In WLED code reviews, when code is modified or added, look for "magic numbers" (hardcoded numeric literals) and suggest replacing them with defined constants when meaningful constants exist.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/ota_update.cpp` around lines 567 - 568, Replace the hardcoded
bootloaderOffset value (currently set to 0x1000) with the target-aware
BOOTLOADER_OFFSET constant so the build uses the per-target definition; update
the declaration that uses bootloaderOffset (and keep maxBootloaderSize as-is) to
reference BOOTLOADER_OFFSET (ensuring the type remains uint32_t) so the code no
longer contains the magic number and follows target-specific configuration.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@usermods/pixels_dice_tray/BLE_REQUIREMENT.md`:
- Around line 17-20: Clarify the working directory for the cp command in
BLE_REQUIREMENT.md: either state the user must run the command from the usermod
directory (usermods/pixels_dice_tray) before running "cp
platformio_override.ini.sample ../../../platformio_override.ini", or replace it
with a repo-root-safe alternative such as copying with explicit paths ("cp
usermods/pixels_dice_tray/platformio_override.ini.sample
platformio_override.ini") so the command works regardless of current directory;
update the text to include one of these options and show the adjusted command.
- Around line 9-12: Update the wording in BLE_REQUIREMENT.md to clarify that
renaming library.json to library.json.disabled only prevents auto-inclusion of
this usermod via in-tree mechanisms controlled by custom_usermods (e.g., the
WLED in-tree usermods list), and does not disable out-of-tree usermods brought
in via library dependencies; explicitly mention custom_usermods affects only
in-tree usermods while lib_deps-supplied/out-of-tree usermods (and thus the
Arduino BLE library when provided as a dependency) can still be active and cause
compilation to pull in BLE symbols on Tasmota Arduino ESP32 builds.

---

Outside diff comments:
In `@wled00/ws.cpp`:
- Around line 87-94: The switch handling BINARY_PROTOCOL_E131 and
BINARY_PROTOCOL_ARTNET casts data+offset to e131_packet_t and can read far
beyond the current buffer; add explicit minimum-size guards using len, offset
and the protocol constants before calling handleE131Packet (mirror the DDP
branch): check that len >= offset + 125 for BINARY_PROTOCOL_E131 and len >=
offset + 18 for BINARY_PROTOCOL_ARTNET, return early on failure, then call
handleE131Packet((e131_packet_t*)&data[offset], client->remoteIP(),
P_E131/P_ARTNET) as before.

---

Duplicate comments:
In `@platformio.ini`:
- Line 531: In V5 environments that set build_unflags to
${common.build_unflags}, change them to include the V5-specific override (e.g.,
use ${esp32_idf_V5.build_unflags} instead of ${common.build_unflags}) so the
V5-specific flags are preserved; search for occurrences of build_unflags =
${common.build_unflags} in V5 env blocks and replace them with build_unflags =
${esp32_idf_V5.build_unflags} (or merge both if needed) to avoid reintroducing
flags V5 intends to remove.
- Around line 518-519: Remove the test-only defines "-D WLED_USE_ETHERNET -D
RLYPIN=-1 -D BTNPIN=-1" from the env named env:esp32dev_V4 (which is currently
included in default_envs) and instead add a new dedicated test-only environment
(e.g., env:esp32dev_V4_test) that contains those defines; update default_envs to
keep only baseline env:esp32dev_V4 so regression runs use the clean V4 baseline,
and ensure the new test env is not part of default_envs so it only runs when
explicitly requested.

In `@wled00/data/update.htm`:
- Around line 47-50: The hideforms() function hides the boot update form
(gId('bootupd')) but does not reveal the explicit "Updating…" progress element,
so submissions can leave the UI blank; update hideforms() to also show the
progress/message element (e.g., remove the "hide" class from the element with id
'Noupd' or otherwise ensure the "Updating…" indicator is visible) while keeping
the existing toggle('upd') behavior so the user sees the explicit updating state
during submit.

In `@wled00/json.cpp`:
- Around line 703-705: The preprocessor currently compares ESP_IDF_VERSION
without checking if it is defined, which can evaluate to 0 unexpectedly; update
the condition that controls emitting root[F("deviceId")]/getDeviceId() to
explicitly test defined(ESP_IDF_VERSION) before the numeric comparison (e.g. use
!defined(ARDUINO_ARCH_ESP32) || !defined(ESP_IDF_VERSION) || (ESP_IDF_VERSION <
ESP_IDF_VERSION_VAL(5,0,0))) so the (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(...))
term is only evaluated when ESP_IDF_VERSION is defined.

In `@wled00/ota_update.cpp`:
- Around line 415-442: The loop currently ignores esp_flash_read failures and
always finalizes and marks bootloaderSHA256CacheValid true; change the logic in
the function containing the loop so you track overall success (e.g., a bool like
bootloaderHashOk) initialized true, set it false if any esp_flash_read call
returns non-ESP_OK or if sizer/parsing indicates an error/unexpected size, only
call mbedtls_sha256_finish(&ctx, bootloaderSHA256Cache) and set
bootloaderSHA256CacheValid = true when bootloaderHashOk is still true; on
failure, free the mbedtls context and leave bootloaderSHA256CacheValid false (or
clear the cache) so you never cache a digest derived from partial/invalid reads
(referencing bootloaderSize, chunkSize, esp_flash_read, sizer,
mbedtls_sha256_finish, bootloaderSHA256Cache and bootloaderSHA256CacheValid).

In `@wled00/util.cpp`:
- Around line 1139-1140: Replace the raw ESP_IDF_VERSION comparison with a
platform-aware guard: wrap the existing conditional in a check that the build is
for ESP32 and ESP_IDF_VERSION is defined, e.g. use something like "#if
defined(ARDUINO_ARCH_ESP32) && defined(ESP_IDF_VERSION) && (ESP_IDF_VERSION <
ESP_IDF_VERSION_VAL(5,0,0))" (or the project’s ESP32 macro if different) so the
block around the computeSHA1/getDeviceId-related code in util.cpp only applies
on ESP32 and only when ESP_IDF_VERSION is available; this avoids preprocessor
errors in mixed ESP8266/ESP32 builds.

---

Nitpick comments:
In `@wled00/ota_update.cpp`:
- Around line 567-568: Replace the hardcoded bootloaderOffset value (currently
set to 0x1000) with the target-aware BOOTLOADER_OFFSET constant so the build
uses the per-target definition; update the declaration that uses
bootloaderOffset (and keep maxBootloaderSize as-is) to reference
BOOTLOADER_OFFSET (ensuring the type remains uint32_t) so the code no longer
contains the magic number and follows target-specific configuration.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9297775 and 1a91db1.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (23)
  • package.json
  • platformio.ini
  • usermods/audioreactive/audio_source.h
  • usermods/pixels_dice_tray/BLE_REQUIREMENT.md
  • usermods/pixels_dice_tray/library.json
  • wled00/bus_manager.cpp
  • wled00/data/update.htm
  • wled00/e131.cpp
  • wled00/fcn_declare.h
  • wled00/improv.cpp
  • wled00/json.cpp
  • wled00/network.cpp
  • wled00/ntp.cpp
  • wled00/ota_update.cpp
  • wled00/ota_update.h
  • wled00/src/dependencies/espalexa/Espalexa.h
  • wled00/udp.cpp
  • wled00/util.cpp
  • wled00/wled.cpp
  • wled00/wled.h
  • wled00/wled_server.cpp
  • wled00/ws.cpp
  • wled00/xml.cpp
💤 Files with no reviewable changes (2)
  • usermods/pixels_dice_tray/library.json
  • wled00/ota_update.h
🚧 Files skipped from review as they are similar to previous changes (5)
  • wled00/e131.cpp
  • wled00/improv.cpp
  • wled00/bus_manager.cpp
  • wled00/wled_server.cpp
  • wled00/fcn_declare.h

Comment on lines +9 to +12
The `library.json` file has been renamed to `library.json.disabled` to prevent this usermod from being automatically included in builds that use `custom_usermods = *` (like the `usermods` environment in platformio.ini).

The Tasmota Arduino ESP32 platform used by WLED does not include Arduino BLE library by default, which causes compilation failures when this usermod is auto-included.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clarify custom_usermods scope to avoid misleading inclusion behavior.

Line 9 currently reads as if this prevents all auto-inclusion paths. In WLED, this only affects in-tree usermods; out-of-tree usermods from lib_deps can still be active. Please add that distinction explicitly.

✏️ Proposed wording tweak
-The `library.json` file has been renamed to `library.json.disabled` to prevent this usermod from being automatically included in builds that use `custom_usermods = *` (like the `usermods` environment in platformio.ini).
+The `library.json` file has been renamed to `library.json.disabled` to prevent this in-tree usermod from being automatically included in builds that use `custom_usermods = *` (like the `usermods` environment in platformio.ini).
+Note: this does not disable out-of-tree usermods pulled via `lib_deps`.

Based on learnings: In WLED, custom_usermods controls only in-tree usermods, while out-of-tree usermods from library dependencies remain active.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/pixels_dice_tray/BLE_REQUIREMENT.md` around lines 9 - 12, Update the
wording in BLE_REQUIREMENT.md to clarify that renaming library.json to
library.json.disabled only prevents auto-inclusion of this usermod via in-tree
mechanisms controlled by custom_usermods (e.g., the WLED in-tree usermods list),
and does not disable out-of-tree usermods brought in via library dependencies;
explicitly mention custom_usermods affects only in-tree usermods while
lib_deps-supplied/out-of-tree usermods (and thus the Arduino BLE library when
provided as a dependency) can still be active and cause compilation to pull in
BLE symbols on Tasmota Arduino ESP32 builds.

Comment on lines +17 to +20
1. **Copy the sample configuration:**
```bash
cp platformio_override.ini.sample ../../../platformio_override.ini
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Specify the command’s expected working directory.

The copy command in Line 19 is path-sensitive and may fail unless users run it from the usermod directory. Please state the required working directory (or provide a repo-root-safe alternative).

✏️ Proposed wording tweak
-1. **Copy the sample configuration:**
+1. **Copy the sample configuration** (run from `usermods/pixels_dice_tray`):
    ```bash
    cp platformio_override.ini.sample ../../../platformio_override.ini
    ```
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
1. **Copy the sample configuration:**
```bash
cp platformio_override.ini.sample ../../../platformio_override.ini
```
1. **Copy the sample configuration** (run from `usermods/pixels_dice_tray`):
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/pixels_dice_tray/BLE_REQUIREMENT.md` around lines 17 - 20, Clarify
the working directory for the cp command in BLE_REQUIREMENT.md: either state the
user must run the command from the usermod directory (usermods/pixels_dice_tray)
before running "cp platformio_override.ini.sample
../../../platformio_override.ini", or replace it with a repo-root-safe
alternative such as copying with explicit paths ("cp
usermods/pixels_dice_tray/platformio_override.ini.sample
platformio_override.ini") so the command works regardless of current directory;
update the text to include one of these options and show the adjusted command.

@softhack007

This comment was marked as duplicate.

@coderabbitai

This comment was marked as duplicate.

version tag renaming
coderabbitai[bot]

This comment was marked as duplicate.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
wled00/ws.cpp (1)

89-94: ⚠️ Potential issue | 🔴 Critical

Add minimum-length validation for E1.31/Art-Net before field access.

Lines 90 and 93 cast and pass packet pointers to handleE131Packet() with insufficient validation. Line 87 only guarantees 2 bytes total, but handleE131Packet() immediately dereferences packet structure fields without checking buffer size:

  • Art-Net: accesses art_opcode (offset 8), art_universe (offset 14), art_length (offset 16), art_data without validating minimum ~18+ bytes
  • E1.31: accesses options, property_value_count, property_values[...] without validating minimum ~40+ bytes

Unlike the DDP case (line 96-100), which validates len < 10 + offset before calling handleE131Packet(), E1.31 and Art-Net have no pre-flight checks. Add protocol-specific minimum-length checks in wsEvent() before line 90/93:

  • E1.31: require minimum packet size before accessing property fields
  • Art-Net: require minimum ~18 bytes before accessing art_* fields

This prevents out-of-bounds memory access on malformed packets.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/ws.cpp` around lines 89 - 94, In wsEvent(), before casting data+offset
and calling handleE131Packet() for BINARY_PROTOCOL_E131 and
BINARY_PROTOCOL_ARTNET, add protocol-specific minimum-length checks: for
BINARY_PROTOCOL_ARTNET ensure remaining bytes (len - offset) are at least ~18
bytes to cover art_opcode, art_universe, art_length and art_data header; for
BINARY_PROTOCOL_E131 ensure remaining bytes meet the E1.31 minimum (~40 bytes)
required to safely read options, property_value_count and property_values.
Perform these checks using the existing variables (data, len, offset, client)
and only call handleE131Packet((e131_packet_t*)&data[offset],
client->remoteIP(), P_E131/P_ARTNET) when the validation passes; otherwise
drop/log the packet and return. Ensure you reference BINARY_PROTOCOL_E131,
BINARY_PROTOCOL_ARTNET, wsEvent, and handleE131Packet when applying the guards.
wled00/ota_update.cpp (1)

724-735: ⚠️ Potential issue | 🔴 Critical

Write only the validated bootloader span returned by verification.

At Line 726, bytesToWrite starts from context->bytesBuffered and only switches to validated size when pointer was shifted. If the pointer is unchanged, trailing uploaded bytes can still be flashed past the verified image boundary.

Suggested fix
-        size_t flashOffset = context->bootloaderOffset;
-        const uint8_t* dataToWrite = context->buffer;
-        size_t bytesToWrite = context->bytesBuffered;
+        size_t flashOffset = context->bootloaderOffset;
+        const uint8_t* dataToWrite = bootloaderData;
+        size_t bytesToWrite = bootloaderSize;

         // If validation adjusted the pointer, it means we have a partition table at the start
         // In this case, we should skip writing the partition table and write bootloader at 0x1000
         if (bootloaderData != context->buffer) {
           // bootloaderData was adjusted - skip partition table in our data
           size_t partitionTableSize = bootloaderData - context->buffer;
-          dataToWrite = bootloaderData;
-          bytesToWrite = bootloaderSize;
           DEBUG_PRINTF_P(PSTR("Skipping %d bytes of partition table data\n"), partitionTableSize);
         }

Also applies to: 738-745

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/ota_update.cpp` around lines 724 - 735, The code uses
context->bytesBuffered for bytesToWrite even when bootloaderData wasn't
adjusted, risking flashing extra trailing data; change bytesToWrite to the
validated bootloader length returned by verification (use bootloaderSize)
whenever writing bootloader spans so that bytesToWrite is always the verified
span size; update the logic around flashOffset, dataToWrite, bytesToWrite, and
bootloaderData in the write path (and the similar block at the later section
lines ~738-745) to ensure you only write the validated bootloaderSize instead of
context->bytesBuffered when writing the bootloader region.
♻️ Duplicate comments (5)
wled00/data/update.htm (1)

47-50: ⚠️ Potential issue | 🟡 Minor

Use a deterministic submit state and explicitly show “Updating…”

At Line 49, toggle('upd') can produce inconsistent visibility, and Noupd is never shown. This can still leave a blank screen while upload is running.

Proposed fix
 function hideforms() {
-	gId('bootupd').classList.add("hide");
-	toggle('upd');			
+	gId('upd').classList.add("hide");
+	gId('bootupd').classList.add("hide");
+	gId('Noupd').classList.remove("hide");
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/data/update.htm` around lines 47 - 50, hideforms currently calls
toggle('upd') which yields inconsistent visibility and never shows the Noupd
state; change hideforms to set a deterministic submit state by explicitly
showing the uploading indicator and hiding the Noupd message instead of
toggling: in function hideforms() (and any caller that expects upload-start
behavior) remove toggle('upd') and instead call the DOM helpers to
gId('upd').classList.remove('hide') and gId('Noupd').classList.add('hide'), set
the submit control to disabled and change its label/text to "Updating…" (so
users see progress), and ensure the code path that handles upload failure or
completion re-enables the submit button and restores visibility (e.g., by
re-showing Noupd when appropriate).
usermods/pixels_dice_tray/BLE_REQUIREMENT.md (2)

9-12: ⚠️ Potential issue | 🟡 Minor

Clarify that custom_usermods only controls in-tree usermods.

Current wording can be read as globally preventing auto-inclusion, but out-of-tree usermods from lib_deps can still be active.

Based on learnings: In WLED, custom_usermods controls only in-tree usermods, while out-of-tree usermods pulled via lib_deps remain active.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/pixels_dice_tray/BLE_REQUIREMENT.md` around lines 9 - 12, Clarify
that disabling library.json (renamed to library.json.disabled) only prevents
this in-tree usermod from being auto-included via the custom_usermods mechanism,
and does not affect out-of-tree usermods pulled via lib_deps; update the
BLE_REQUIREMENT.md text to explicitly mention custom_usermods controls only
in-tree usermods and that out-of-tree usermods added through lib_deps (or other
external dependency mechanisms) can still be included and cause the same Arduino
BLE dependency to be required.

17-20: ⚠️ Potential issue | 🟡 Minor

Make the copy command location-safe (or state required working directory).

cp platformio_override.ini.sample ../../../platformio_override.ini is cwd-dependent; please either specify it must be run from usermods/pixels_dice_tray or provide a repo-root-safe command.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/pixels_dice_tray/BLE_REQUIREMENT.md` around lines 17 - 20, The copy
command in BLE_REQUIREMENT.md is cwd-dependent; update the instruction to either
state explicitly that the command must be run from the usermods/pixels_dice_tray
directory or replace the example with a repo-root-safe variant that references
the sample file location (the current `cp platformio_override.ini.sample
../../../platformio_override.ini` line) so the copy works regardless of working
directory; edit the BLE_REQUIREMENT.md entry containing that cp command to
include the chosen clarification or safe path form.
platformio.ini (1)

130-130: ⚠️ Potential issue | 🟡 Minor

WLED_USE_SHARED_RMT is still defined twice and in broader scope than needed.

Defining it in both [common] and [esp32_idf_V5] is redundant and leaks an ESP32-specific switch into non-ESP32 environments.

Also applies to: 337-337

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@platformio.ini` at line 130, WLED_USE_SHARED_RMT is defined twice and exposed
in the global [common] scope; remove the duplicate from the [common] section and
keep the define only inside the ESP32-specific profile ([esp32_idf_V5]) so the
flag is scoped to ESP32 builds; update any accompanying comment to reference
that this define is ESP32-only and verify no other sections rely on the common
definition before removing it.
wled00/ota_update.cpp (1)

415-442: ⚠️ Potential issue | 🟠 Major

Only set bootloader SHA cache valid when all flash reads succeed.

Line 417 ignores read failures by skipping the chunk, but Line 441 still marks the cache valid. That can publish an invalid digest.

Suggested fix
 static void calculateBootloaderSHA256() {
   // Calculate SHA256
   mbedtls_sha256_context ctx;
   mbedtls_sha256_init(&ctx);
   mbedtls_sha256_starts(&ctx, 0); // 0 = SHA256 (not SHA224)

   const size_t chunkSize = 256;
   alignas(esp_image_header_t) uint8_t buffer[chunkSize];
   size_t bootloaderSize = BOOTLOADER_SIZE;
   BootloaderImageSizer sizer;
   size_t totalHashLen = 0;
+  bool readOk = true;

   for (uint32_t offset = 0; offset < bootloaderSize; offset += chunkSize) {
     size_t readSize = min((size_t)(bootloaderSize - offset), chunkSize);
-    if (esp_flash_read(NULL, buffer, BOOTLOADER_OFFSET + offset, readSize) == ESP_OK) {
-      sizer.feed(buffer, readSize);
+    if (esp_flash_read(NULL, buffer, BOOTLOADER_OFFSET + offset, readSize) != ESP_OK) {
+      readOk = false;
+      break;
+    }
+    sizer.feed(buffer, readSize);

       size_t hashLen = readSize;
       if (sizer.isSizeKnown()) {
         size_t totalSize = sizer.totalSize();
         if (totalSize > 0 && totalSize <= BOOTLOADER_SIZE) {
           bootloaderSize = totalSize;
           if (offset + readSize > totalSize) {
             hashLen = (totalSize > offset) ? (totalSize - offset) : 0;
           }
         }
       }

       if (hashLen > 0) {
         totalHashLen += hashLen;
         mbedtls_sha256_update(&ctx, buffer, hashLen);
       }
-    }
   }

+  if (!readOk || totalHashLen == 0) {
+    mbedtls_sha256_free(&ctx);
+    bootloaderSHA256CacheValid = false;
+    return;
+  }
   mbedtls_sha256_finish(&ctx, bootloaderSHA256Cache);
   mbedtls_sha256_free(&ctx);

   bootloaderSHA256CacheValid = true;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/ota_update.cpp` around lines 415 - 442, The loop that reads flash and
updates the SHA256 (for .. offset .. bootloaderSize with esp_flash_read and
mbedtls_sha256_update) must detect any esp_flash_read failures and abort
computing/marking the cache; add a success flag (e.g., readOk) or break on first
esp_flash_read != ESP_OK, call mbedtls_sha256_free and avoid
mbedtls_sha256_finish/setting bootloaderSHA256CacheValid unless all chunks read
successfully (optionally verify totalHashLen matches expected bootloaderSize via
sizer.totalSize()), and ensure bootloaderSHA256CacheValid is set to true only
when the full read+finish completed without errors.
🧹 Nitpick comments (1)
platformio.ini (1)

172-172: Consider pinning library refs to commit hashes for build reproducibility.

Branch-based refs like #ESP32-C6 and #esp-idf-v5-fixes can drift and make builds non-reproducible across time and machines. While the comments indicate these are intentional patches for active workarounds (RMT driver conflicts, ESP-IDF v5 compatibility), consider pinning to specific commit hashes once patches stabilize or are merged upstream. If frequent updates are expected, document the rationale for using branch refs alongside the inline comments already present.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@platformio.ini` at line 172, The platformio.ini dependency uses branch refs
(e.g., the FastLED ref "#ESP32-C6" and other branch refs like
"#esp-idf-v5-fixes") which can drift; update these entries to pin to specific
commit hashes (replace "#ESP32-C6" and similar branch tags with the
corresponding commit SHA once the patch is stable) or, if you must keep branch
refs, add an inline comment documenting the reason and expected update cadence
for reproducibility; locate and change the dependency lines referencing FastLED
and any esp-idf-related library refs to use commit SHAs or add the documentation
note.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@wled00/bus_manager.cpp`:
- Around line 417-423: In the ESP-IDF >=5 branch where
ledcAttachChannel(_pins[i], _frequency, _depth - (dithering*4), channel) is
called, check its boolean return value and handle failure: if
ledcAttachChannel(...) returns false, mark the bus/channel as not initialized
(set _valid = false), emit an error/log message indicating which channel/pin
failed (use _pins[i] and channel), and abort further initialization for this
instance (break/return) so we don't leave _valid true when PWM wasn't
configured; ensure any cleanup needed for previously attached channels is
performed before exiting initialization.

---

Outside diff comments:
In `@wled00/ota_update.cpp`:
- Around line 724-735: The code uses context->bytesBuffered for bytesToWrite
even when bootloaderData wasn't adjusted, risking flashing extra trailing data;
change bytesToWrite to the validated bootloader length returned by verification
(use bootloaderSize) whenever writing bootloader spans so that bytesToWrite is
always the verified span size; update the logic around flashOffset, dataToWrite,
bytesToWrite, and bootloaderData in the write path (and the similar block at the
later section lines ~738-745) to ensure you only write the validated
bootloaderSize instead of context->bytesBuffered when writing the bootloader
region.

In `@wled00/ws.cpp`:
- Around line 89-94: In wsEvent(), before casting data+offset and calling
handleE131Packet() for BINARY_PROTOCOL_E131 and BINARY_PROTOCOL_ARTNET, add
protocol-specific minimum-length checks: for BINARY_PROTOCOL_ARTNET ensure
remaining bytes (len - offset) are at least ~18 bytes to cover art_opcode,
art_universe, art_length and art_data header; for BINARY_PROTOCOL_E131 ensure
remaining bytes meet the E1.31 minimum (~40 bytes) required to safely read
options, property_value_count and property_values. Perform these checks using
the existing variables (data, len, offset, client) and only call
handleE131Packet((e131_packet_t*)&data[offset], client->remoteIP(),
P_E131/P_ARTNET) when the validation passes; otherwise drop/log the packet and
return. Ensure you reference BINARY_PROTOCOL_E131, BINARY_PROTOCOL_ARTNET,
wsEvent, and handleE131Packet when applying the guards.

---

Duplicate comments:
In `@platformio.ini`:
- Line 130: WLED_USE_SHARED_RMT is defined twice and exposed in the global
[common] scope; remove the duplicate from the [common] section and keep the
define only inside the ESP32-specific profile ([esp32_idf_V5]) so the flag is
scoped to ESP32 builds; update any accompanying comment to reference that this
define is ESP32-only and verify no other sections rely on the common definition
before removing it.

In `@usermods/pixels_dice_tray/BLE_REQUIREMENT.md`:
- Around line 9-12: Clarify that disabling library.json (renamed to
library.json.disabled) only prevents this in-tree usermod from being
auto-included via the custom_usermods mechanism, and does not affect out-of-tree
usermods pulled via lib_deps; update the BLE_REQUIREMENT.md text to explicitly
mention custom_usermods controls only in-tree usermods and that out-of-tree
usermods added through lib_deps (or other external dependency mechanisms) can
still be included and cause the same Arduino BLE dependency to be required.
- Around line 17-20: The copy command in BLE_REQUIREMENT.md is cwd-dependent;
update the instruction to either state explicitly that the command must be run
from the usermods/pixels_dice_tray directory or replace the example with a
repo-root-safe variant that references the sample file location (the current `cp
platformio_override.ini.sample ../../../platformio_override.ini` line) so the
copy works regardless of working directory; edit the BLE_REQUIREMENT.md entry
containing that cp command to include the chosen clarification or safe path
form.

In `@wled00/data/update.htm`:
- Around line 47-50: hideforms currently calls toggle('upd') which yields
inconsistent visibility and never shows the Noupd state; change hideforms to set
a deterministic submit state by explicitly showing the uploading indicator and
hiding the Noupd message instead of toggling: in function hideforms() (and any
caller that expects upload-start behavior) remove toggle('upd') and instead call
the DOM helpers to gId('upd').classList.remove('hide') and
gId('Noupd').classList.add('hide'), set the submit control to disabled and
change its label/text to "Updating…" (so users see progress), and ensure the
code path that handles upload failure or completion re-enables the submit button
and restores visibility (e.g., by re-showing Noupd when appropriate).

In `@wled00/ota_update.cpp`:
- Around line 415-442: The loop that reads flash and updates the SHA256 (for ..
offset .. bootloaderSize with esp_flash_read and mbedtls_sha256_update) must
detect any esp_flash_read failures and abort computing/marking the cache; add a
success flag (e.g., readOk) or break on first esp_flash_read != ESP_OK, call
mbedtls_sha256_free and avoid mbedtls_sha256_finish/setting
bootloaderSHA256CacheValid unless all chunks read successfully (optionally
verify totalHashLen matches expected bootloaderSize via sizer.totalSize()), and
ensure bootloaderSHA256CacheValid is set to true only when the full read+finish
completed without errors.

---

Nitpick comments:
In `@platformio.ini`:
- Line 172: The platformio.ini dependency uses branch refs (e.g., the FastLED
ref "#ESP32-C6" and other branch refs like "#esp-idf-v5-fixes") which can drift;
update these entries to pin to specific commit hashes (replace "#ESP32-C6" and
similar branch tags with the corresponding commit SHA once the patch is stable)
or, if you must keep branch refs, add an inline comment documenting the reason
and expected update cadence for reproducibility; locate and change the
dependency lines referencing FastLED and any esp-idf-related library refs to use
commit SHAs or add the documentation note.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9297775 and e3bebc0.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (23)
  • package.json
  • platformio.ini
  • usermods/audioreactive/audio_source.h
  • usermods/pixels_dice_tray/BLE_REQUIREMENT.md
  • usermods/pixels_dice_tray/library.json
  • wled00/bus_manager.cpp
  • wled00/data/update.htm
  • wled00/e131.cpp
  • wled00/fcn_declare.h
  • wled00/improv.cpp
  • wled00/json.cpp
  • wled00/network.cpp
  • wled00/ntp.cpp
  • wled00/ota_update.cpp
  • wled00/ota_update.h
  • wled00/src/dependencies/espalexa/Espalexa.h
  • wled00/udp.cpp
  • wled00/util.cpp
  • wled00/wled.cpp
  • wled00/wled.h
  • wled00/wled_server.cpp
  • wled00/ws.cpp
  • wled00/xml.cpp
💤 Files with no reviewable changes (2)
  • usermods/pixels_dice_tray/library.json
  • wled00/ota_update.h
🚧 Files skipped from review as they are similar to previous changes (3)
  • wled00/improv.cpp
  • wled00/udp.cpp
  • wled00/src/dependencies/espalexa/Espalexa.h

Comment on lines +417 to +423
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
ledcSetup(channel, _frequency, _depth - (dithering*4)); // with dithering _frequency doesn't really matter as resolution is 8 bit
ledcAttachPin(_pins[i], channel);
#else
ledcAttachChannel(_pins[i], _frequency, _depth - (dithering*4), channel);
// LEDC timer reset credit @dedehai
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, verify the current state of ledcAttachChannel calls
echo "=== All ledcAttachChannel calls in bus_manager.cpp ==="
rg -nP --type=cpp -C3 'ledcAttachChannel' wled00/bus_manager.cpp

echo -e "\n=== Checking for return value checks on ledcAttachChannel ==="
rg -nP --type=cpp 'if\s*\(\s*!?\s*ledcAttachChannel' wled00/bus_manager.cpp

echo -e "\n=== Full BusPwm::begin method context ==="
sed -n '390,450p' wled00/bus_manager.cpp

Repository: wled/WLED

Length of output: 3598


🏁 Script executed:

#!/bin/bash
# Check ESP-IDF documentation and other LEDC function behavior
echo "=== Check ledcSetup and ledcAttachPin return handling ==="
rg -nP --type=cpp -C2 'ledcSetup|ledcAttachPin' wled00/bus_manager.cpp | head -50

echo -e "\n=== Check if there are any comments or patterns for LEDC error handling ==="
rg -nP --type=cpp -i 'ledc.*fail|ledc.*error' wled00/bus_manager.cpp

Repository: wled/WLED

Length of output: 898


🏁 Script executed:

#!/bin/bash
# Check the broader context of pin allocation and cleanup patterns
echo "=== Search for PinManager::deallocateLedc and deallocateMultiplePins patterns ==="
rg -nP --type=cpp 'deallocateLedc|deallocateMultiplePins' wled00/bus_manager.cpp

echo -e "\n=== Check existing error handling patterns in BusPwm ==="
rg -nP --type=cpp -B2 'return false|return;' wled00/bus_manager.cpp | head -80

Repository: wled/WLED

Length of output: 3190


🌐 Web query:

ESP-IDF v5 ledcAttachChannel return value documentation

💡 Result:

ledcAttachChannel() is not an ESP-IDF API in v5.x; it’s an Arduino-ESP32 helper function. In Arduino-ESP32, its signature is:

bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, int8_t channel);

and the return value is:

  • true: configuration successful
  • false: an error occurred and the LEDC channel was not configured [1]

In ESP-IDF v5, the LEDC driver APIs instead typically return esp_err_t (e.g., ESP_OK, ESP_ERR_INVALID_ARG, etc.) rather than bool, as shown in the ESP-IDF LEDC documentation (for functions like ledc_set_duty, ledc_set_duty_and_update, etc.) [2].

Sources: Arduino-ESP32 LEDC API reference [1], ESP-IDF v5.0 LEDC API reference [2].

[1] https://docs.espressif.com/projects/arduino-esp32/en/latest/api/ledc.html
[2] https://docs.espressif.com/projects/esp-idf/en/v5.0/esp32/api-reference/peripherals/ledc.html


Check return value of ledcAttachChannel() and handle initialization failure.

Line 421 ignores the boolean return from ledcAttachChannel(). If the attach fails, initialization silently continues and _valid is set to true despite the LEDC channel not being configured, resulting in no PWM output at runtime.

Proposed fix
       `#if` ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
       ledcSetup(channel, _frequency, _depth - (dithering*4)); // with dithering _frequency doesn't really matter as resolution is 8 bit
       ledcAttachPin(_pins[i], channel);
       `#else`
-      ledcAttachChannel(_pins[i], _frequency,  _depth - (dithering*4), channel);
+      if (!ledcAttachChannel(_pins[i], _frequency, _depth - (dithering*4), channel)) {
+        DEBUGBUS_PRINTF_P(PSTR("LEDC attach failed (pin:%u ch:%u)\n"), _pins[i], channel);
+        for (unsigned j = 0; j < i; j++) ledcDetach(_pins[j]);
+        PinManager::deallocateLedc(_ledcStart, numPins);
+        PinManager::deallocateMultiplePins(pins, numPins, PinOwner::BusPwm);
+        return;
+      }
       // LEDC timer reset credit `@dedehai`
       `#endif`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/bus_manager.cpp` around lines 417 - 423, In the ESP-IDF >=5 branch
where ledcAttachChannel(_pins[i], _frequency, _depth - (dithering*4), channel)
is called, check its boolean return value and handle failure: if
ledcAttachChannel(...) returns false, mark the bus/channel as not initialized
(set _valid = false), emit an error/log message indicating which channel/pin
failed (use _pins[i] and channel), and abort further initialization for this
instance (break/return) so we don't leave _valid true when PWM wasn't
configured; ensure any cleanup needed for previously attached channels is
performed before exiting initialization.

@netmindz
Copy link
Member Author

netmindz commented Mar 7, 2026

With regards to your own V5 branch @willmmiles , if you have a neater one then we should decide if we replace the current version in the repo with yours before we get too far down the rabbit hole. I'm not precious about the work I did so far, it was more of a discovery of what was broken

@willmmiles
Copy link
Member

With regards to your own V5 branch @willmmiles , if you have a neater one then we should decide if we replace the current version in the repo with yours before we get too far down the rabbit hole. I'm not precious about the work I did so far, it was more of a discovery of what was broken

I'm reviewing it in more depth thismorning and I think some kind of combination might be the way. I took very different approaches on fixing a few things like third-party library integration (the FastLED RMT driver conflict should never have happened because we never call that code -- it's actually caused by current versions of pioarduino/tasmota playing games with lib_archive globally and some linker command line ordering issues). Unfortunately I also have some quick hacks in there where the approach in this branch is better. I'll see if I can recompose a more coherent commit stream from the two today. My original plan from last year was to PR various changes piecemeal but I don't think it's worth it at this point -- I think it's getting to be shallow enough that we should merge the v5 env soon so we can get on with a better HAL and bus drivers.

@netmindz
Copy link
Member Author

netmindz commented Mar 8, 2026

I don't want to delay version 16 further by trying to swap to V5

I think we need to get 16 released, create 16_x branch probably at the point of beta

We can then merge in V5 into main. I'm keen to avoid different platform versions in the same release, so I would prefer we didn't have the C5, C6 and P4 on V5 and the rest still on V4

This would potentially make main and right nightly builds way less stable than they have been if we merge V5 "too soon", but maybe that if something we just message as best we can to the users?

It would certainly be good to have discussion as to what the criteria are for when V5 is our main development version rather than an experimental branch

@softhack007
Copy link
Member

softhack007 commented Mar 8, 2026

I'm keen to avoid different platform versions in the same release, so I would prefer we didn't have the C5, C6 and P4 on V5 and the rest still on V4

I think this we will have to use two platform versions for some time, at least in wled release 17 - so the mainline of MCUs (esp32, s3,s2,c3) will be on V4, while the newer C5, C6 and P4 will be marked "experimental" on V5.

If you look at the long list of open points in the V5 and C6-V5 branch, my feeling is we should not expose our users to a new major framework release unless strictly necessary. We did the same with the V4 framework (S3,S2,C3) in wled releases 0.14 and 0.15, and in retrospect I think it was the right decision. Even if it lead to classic esp32 running an "ancient" framework, I think users had almost zero problems and they never really complained.

Ok just two issues I can recall - wifi WPA3 support and the "FritzBox" bug, plus limited support for newer ethernet chips. For the FritzBox problem, we pointed people to the never "esp32 V4" builds.

@netmindz
Copy link
Member Author

netmindz commented Mar 8, 2026

I do really worry with this approach @softhack007 we underestimate the dev effort that is spent trying to work out of an issues we face relate to the new hardware of the new platform. If we wrongly attribute to the hardware, then when we come to update esp32 classic then we are going to hit all those issues all over again.

We really have very little development resource and with no automated testing, any code that is dependent on chipset or platform variation is a very significant development and testing cost, which if we are honest I would say is not always tested for the current set of variants, let alone when we add 3 more

The ESP-IDF v4.4 release branch reached End-of-Life (EOL) in July 2024 - so we desperately need to catch up! Pushing back to most likely 2027 until we get the vast majority of our users onto a supported platform if we go with a dual platform approach for v16 doesn't seem great

@softhack007
Copy link
Member

@netmindz yes I agree with your point about development effort and resources. Maybe we need to think about it a bit more.

The current V5 branch would also allow for this: make V5 the official platform in 17.0, and keep compiling with V4 as a special developers options mainly for back-to-back testing and to help with porting.

My main concern with V5 is really the sheer number of "opps" and small open ends that I found. We can "embrace" V5, but it will force us to fix all these open ends before we can even think about releasing wled 17.0.

For sure it's possible to go the V5 way, and it's a good investment of our time because we have to do that any way, so the only question is "when", not "if".

@softhack007
Copy link
Member

C3 builds are failing after sync/merge with main

/home/runner/.platformio/packages/toolchain-riscv32-esp/bin/../lib/gcc/riscv32-esp-elf/15.1.0/../../../../riscv32-esp-elf/bin/ld: The gap between .flash.rodata and .eh_frame_hdr must not exist to produce the final bin image.
collect2: error: ld returned 1 exit status

@willmmiles any idea?

@willmmiles
Copy link
Member

C3 builds are failing after sync/merge with main

/home/runner/.platformio/packages/toolchain-riscv32-esp/bin/../lib/gcc/riscv32-esp-elf/15.1.0/../../../../riscv32-esp-elf/bin/ld: The gap between .flash.rodata and .eh_frame_hdr must not exist to produce the final bin image.
collect2: error: ld returned 1 exit status

@willmmiles any idea?

Newer IDF v5s have added some extra validations to the linker scripts on RISCV platforms. In some sense, the challenge here is platform=arduino only: ESP-IDF itself offers an interface for adding linker fragments, but as long as we're using pre-compiled IDF builds, it's a bit trickier to put the data in the right place.

I should hopefully have an improved version ready tonight.

@willmmiles
Copy link
Member

I should hopefully have an improved version ready tonight.

Ready was a stretch - I'm out of time and it's not fully tested. The build time validations pass but I haven't had a chance to upload it to real devices; I'll resume tomorrow. willmmiles@149acc9 is the key commit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement keep This issue will never become stale/closed automatically magic major This is a non-trivial major feature and will take some time to implement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants