Skip to content

Conversation

DedeHai
Copy link
Collaborator

@DedeHai DedeHai commented Sep 14, 2025

  • cherry picked from @blazoncek fork and fixed merge conflicts.
  • added the two mendatory buffers (global and segment, each 32bit) to memory requirement calculation in UI

fixes #4614 "Hardware Setup Length can't be saved after 659"

this fix also nees to be back-ported to 0.15

@willmmiles can you please check this is correct? I am not 100% sure I fixed the conflicts correctly, it did work when testing on ESP32 and ESP8266.

Summary by CodeRabbit

  • New Features

    • LED settings UI now shows updated hardware-aware memory estimates and clearer warning wording; pin validation logic improved.
  • Bug Fixes

    • More comprehensive memory checks now account for shared I2S/double-buffer usage to avoid creating outputs that would exceed available memory.
    • Improved compatibility and sizing across ESP8266/ESP32 variants to reduce init failures.
  • Refactor

    • Default LED bus setup simplified for more consistent configuration creation and memory reporting.

@DedeHai DedeHai requested a review from willmmiles September 14, 2025 07:06
Copy link
Contributor

coderabbitai bot commented Sep 14, 2025

Walkthrough

Adds I2S-aware LED buffer accounting and shared I2S DMA estimation, adjusts per-target bus data-size logic, removes default no-FS memory checks during automatic bus creation, and updates the web UI memory estimator with pixel-buffer overhead and parallel-I2S hardware logic.

Changes

Cohort / File(s) Summary
I2S-aware memory accounting (core)
wled00/FX_fcn.cpp, wled00/bus_manager.cpp
Introduce per-bus mem (excluding DMA/RMT), compute/aggregate a shared maxI2S based on architecture, cadence (3/4 step), channels and bit-width, and require mem + maxI2S <= MAX_LED_MEMORY to create buses. BusManager::memUsage now returns size + maxI2S and logging updated.
Bus sizing / target-specific data-size
wled00/bus_wrapper.h
Simplify and rework getDataSize for ESP8266/ESP32 variants: remove many prior doubling multipliers, add default two-buffer fallback, and add architecture- and parallel-I2S-aware branches for I2S/channel handling. Public signatures unchanged.
Default no-FS bus creation
wled00/cfg.cpp
Replace local BusConfig + mem accounting + overflow check with in-place busConfigs.emplace_back(...) for default bus setup; remove mem/digitalCount tracking and default LED-buffer logging.
Web UI memory estimator & pin validation
wled00/data/settings_leds.htm
Replace DOM reads with d.Sf[...], add toNum bus-id helper, rework pin-conflict checks, compute per-bus memory including pixel-buffer (pbfr) and optional DMA-like dbl, detect parallelI2S per hardware, update multipliers/thresholds and memory warning text/logic.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • willmmiles
  • blazoncek
  • softhack007

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning While many edits align with correcting buffer-size accounting, the PR also removes memory accounting/overflow checks from cfg.cpp's default-bus generation and applies broad getDataSize/I2S-related rework across many bus types; these behavioral and wide-ranging refactors are not documented in the PR objectives and risk introducing regressions beyond the stated "buffer size calculation" fix. The cfg.cpp change in particular eliminates a runtime guard that previously prevented creating buses when memory would be exceeded, which appears out-of-scope for a focused fix and requires separate review or stronger justification. Revert or isolate the cfg.cpp default-bus memory-check removal and split the large bus_wrapper/getDataSize refactor into a separate PR so reviewers can evaluate the buffer-size fix in isolation; add explicit tests showing saved LED counts >659 persist on ESP8266 with accompanying stability logs, and document the reason for any changed memory-guard behavior before merging.
Linked Issues Check ❓ Inconclusive The PR targets linked issue #4614 by updating core and UI memory accounting and by adding two mandatory UI buffers and the author reports being able to configure ~800 LEDs on test hardware, but the diff and notes show the transition buffer was not included, the author observed instability on ESP8266 at higher counts, and cfg.cpp removes default-bus memory checks which could affect behavior. Because the transition buffer is omitted, the author-reported instability exists, and no reproducible ESP8266 save/persistence artifacts are provided in the PR, I cannot conclusively confirm the linked-issue objective (stable persistent saving of >659 LEDs on the target ESP8266 environment) is fully met from the information given. Please reproduce the linked-issue steps on an ESP8266 and attach logs/screenshots showing a saved value >659 that persists after reboot, include the transition buffer in the UI memory calculation or justify its exclusion, and run focused stability tests at the target LED counts; once those artifacts and either the transition-buffer change or a rationale are provided, re-evaluate the PR against issue #4614.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Fix LED buffer size calculation" is concise, developer-focused, and accurately reflects the primary intent of this changeset, which centers on correcting LED buffer/memory accounting across UI and core bus code paths. It is specific enough for a teammate scanning PR history to understand the main change and contains no extraneous noise.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

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/bus_manager.cpp (1)

906-907: Build breaker: missing semicolon

u++ is missing a semicolon; this won’t compile.

Apply this diff:

-    u++
+    u++;
🧹 Nitpick comments (5)
wled00/bus_wrapper.h (2)

1363-1363: Duplicate assignment to offset

offset is set twice for num < 8 on ESP32. Remove the duplicate.

-        if (num < 8) offset = 1;  // 8 I2S followed by 8 RMT
-        if (num < 8) offset = 1;  // 8 I2S followed by 8 RMT
+        if (num < 8) offset = 1;  // 8 I2S followed by 8 RMT

554-567: Spelling nit: “parallel”

Several comments say “paralell/parellel”. Consider fixing for readability in the touched blocks.

Also applies to: 652-667, 748-762, 870-884, 1083-1096

wled00/data/settings_leds.htm (1)

203-206: 8266 DMA detection should use numeric compare

value is a string; use strict compare on a parsed int to avoid edge cases.

-        if (is8266() && d.Sf["L0"+n].value == 3) { //8266 DMA uses 5x the mem
+        if (is8266() && parseInt(d.Sf["L0"+n].value, 10) === 3) { // 8266 DMA (GPIO3) uses 5x the mem
wled00/FX_fcn.cpp (1)

1203-1206: Minor: add newline to OOM debug

Helps readability in logs when a bus is skipped.

-      DEBUG_PRINTF_P(PSTR("Out of LED memory! Bus %d (%d) #%u not created."), (int)bus.type, (int)bus.count, digitalCount);
+      DEBUG_PRINTF_P(PSTR("Out of LED memory! Bus %d (%d) #%u not created.\n"), (int)bus.type, (int)bus.count, digitalCount);
wled00/bus_manager.cpp (1)

788-805: I2S common buffer calc: add 4‑byte alignment and small robustness tweaks

  • DMA is “aligned to 4 bytes” (per comments in bus_wrapper.h). Align i2sCommonSize up to 4 to avoid minor undercount.
  • Optional: hoist stepFactor outside the loop and consider size_t for sizes (non-blocking).

Suggested minimal fix within this block:

-        unsigned i2sCommonSize = stepFactor * bus->getLength() * bus->getNumberOfChannels() * (bus->is16bit()+1);
+        unsigned i2sCommonSize = stepFactor * bus->getLength() * bus->getNumberOfChannels() * (bus->is16bit() ? 2u : 1u);
+        // 4-byte alignment for DMA
+        i2sCommonSize = (i2sCommonSize + 3u) & ~3u;
         if (i2sCommonSize > maxI2S) maxI2S = i2sCommonSize;

Non-blocking nits:

  • Define stepFactor once above the loop to avoid multiple constexprs.
  • Consider size_t for size/maxI2S to future-proof larger configurations.

Please confirm that, in parallel I2S mode, the set of buses contributing to the shared DMA indeed corresponds to the first up-to-8 one‑wire digital buses as iterated here (ordering vs. RMT/I2S mapping). If ordering differs, we may need to adjust which buses are considered.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 75f6de9 and 857a041.

📒 Files selected for processing (5)
  • wled00/FX_fcn.cpp (1 hunks)
  • wled00/bus_manager.cpp (3 hunks)
  • wled00/bus_wrapper.h (3 hunks)
  • wled00/cfg.cpp (1 hunks)
  • wled00/data/settings_leds.htm (3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
wled00/**/*.{cpp,h}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use spaces (2 per level) for C++ firmware files

Files:

  • wled00/cfg.cpp
  • wled00/FX_fcn.cpp
  • wled00/bus_manager.cpp
  • wled00/bus_wrapper.h
wled00/data/**/*.{htm,html,css,js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use tabs for indentation in web UI files (.htm/.html/.css/.js) under wled00/data

Files:

  • wled00/data/settings_leds.htm
🧠 Learnings (5)
📓 Common learnings
Learnt from: mval-sg
PR: wled/WLED#4876
File: wled00/xml.cpp:0-0
Timestamp: 2025-08-28T08:09:20.630Z
Learning: The WLED codebase has opportunities for refactoring hardcoded array bounds (like the "15" used for DMX channels) to use sizeof(array)/sizeof(array[0]) for more maintainable code, but such changes should be done consistently across the entire codebase in a dedicated refactoring effort.
📚 Learning: 2025-08-26T11:51:21.817Z
Learnt from: DedeHai
PR: wled/WLED#4798
File: wled00/FX.cpp:7531-7533
Timestamp: 2025-08-26T11:51:21.817Z
Learning: In WLED PR #4798, DedeHai confirmed that certain gamma-related calls in FX.cpp/FX_fcn.cpp/particle systems are intentional for effect-level shaping (e.g., brightness curves, TV sim, Pride 2015 pre-mix), distinct from final output gamma. Do not flag or remove these in future reviews; add comments when feasible to clarify intent.

Applied to files:

  • wled00/FX_fcn.cpp
📚 Learning: 2025-08-28T08:09:20.630Z
Learnt from: mval-sg
PR: wled/WLED#4876
File: wled00/xml.cpp:0-0
Timestamp: 2025-08-28T08:09:20.630Z
Learning: The WLED codebase has opportunities for refactoring hardcoded array bounds (like the "15" used for DMX channels) to use sizeof(array)/sizeof(array[0]) for more maintainable code, but such changes should be done consistently across the entire codebase in a dedicated refactoring effort.

Applied to files:

  • wled00/data/settings_leds.htm
📚 Learning: 2025-09-01T10:26:17.959Z
Learnt from: mval-sg
PR: wled/WLED#4876
File: wled00/wled_eeprom.cpp:0-0
Timestamp: 2025-09-01T10:26:17.959Z
Learning: In WLED PR #4876, the DMXStartLED EEPROM backward compatibility issue was partially addressed by keeping it at address 2550 and reading it as a 16-bit value, with DMXChannelsValue array moved to addresses 2552-2566. This maintains compatibility with pre-0.11 EEPROM layouts for DMXStartLED, though legacy "Set to 255" (code 6) configurations may still need migration logic.

Applied to files:

  • wled00/data/settings_leds.htm
📚 Learning: 2025-09-02T01:45:58.047Z
Learnt from: willmmiles
PR: wled/WLED#4890
File: lib/NeoESP32RmtHI/include/NeoEsp32RmtHIMethod.h:31-36
Timestamp: 2025-09-02T01:45:58.047Z
Learning: For the NeoEsp32RmtHI driver, RISC-V ESP32-C3 support is currently disabled via bus_wrapper.h rather than compile-time guards, as the maintainer willmmiles is working on resolving underlying nested interrupt issues and prefers to centralize the workaround in one location.

Applied to files:

  • wled00/bus_wrapper.h
🧬 Code graph analysis (2)
wled00/FX_fcn.cpp (3)
wled00/bus_manager.h (7)
  • isDigital (141-142)
  • isDigital (180-180)
  • is2Pin (181-181)
  • hasRGB (163-165)
  • hasWhite (166-172)
  • hasCCT (173-178)
  • is16bit (185-185)
wled00/bus_manager.cpp (8)
  • add (82-87)
  • add (82-82)
  • add (807-828)
  • add (807-807)
  • memUsage (765-776)
  • memUsage (765-765)
  • memUsage (779-805)
  • memUsage (779-779)
wled00/bus_wrapper.h (1)
  • memUsage (1209-1276)
wled00/bus_manager.cpp (2)
wled00/bus_manager.h (1)
  • BusDigital (234-285)
wled00/bus_wrapper.h (3)
  • getDataSize (1111-1207)
  • memUsage (1209-1276)
  • isParallelI2S1Output (338-338)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (16)
  • GitHub Check: wled_build / Build Environments (esp32_wrover)
  • GitHub Check: wled_build / Build Environments (esp32s3_4M_qspi)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_8MB_opi)
  • GitHub Check: wled_build / Build Environments (lolin_s2_mini)
  • GitHub Check: wled_build / Build Environments (usermods)
  • GitHub Check: wled_build / Build Environments (esp32dev)
  • GitHub Check: wled_build / Build Environments (esp32c3dev)
  • GitHub Check: wled_build / Build Environments (esp32_eth)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_16MB_opi)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_compat)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_compat)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_160)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_160)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_compat)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_160)
  • GitHub Check: wled_build / Build Environments (esp8266_2m)
🔇 Additional comments (10)
wled00/bus_wrapper.h (2)

472-485: ESP32 channel remap logic — verify off-by-one for parallel I2S

Looks sane, but please confirm channel mapping when parallel I2S is enabled on classic ESP32: subtracting 1 and then skipping 7 (when channel > 6) matches intended RMT/I2S ordering after 0.15.0-b3. A quick bench test with 9+ outputs would be good.


1215-1273: memUsage split (RMT vs I2S) aligns with FX/bus_manager

The exclusion of I2S DMA here (handled separately as maxI2S) and double-buffering for RMT look consistent with BusManager::memUsage() and WS2812FX::finalizeInit().

Please double-check 16‑bit and 5‑channel multipliers against NeoPixelBus behavior for UCS/SM16825 on ESP32.

wled00/data/settings_leds.htm (4)

196-198: Include skipped LEDs in len — good catch

Accounting for SLn in memory calc is correct.


215-217: Pixel buffer overhead (global + segment) — OK

len*8 per bus matches 4B global + 4B segment buffer per LED under typical segment layouts.

If segments won’t cover the full LED range, consider noting this is a conservative estimate.


269-270: Numeric n in getMem call — OK

Avoids implicit string math.


307-308: ParseInt for bus index — OK

Prevents accidental string concatenation issues.

wled00/cfg.cpp (1)

323-323: Use of emplace_back for default bus config — OK

Direct in-place construction with doInitBusses=true fits the new finalizeInit gating.

Confirm no other caller relies on the removed interim mem accounting in this path.

wled00/FX_fcn.cpp (2)

1177-1207: I2S-aware memory gating — looks correct

Accumulating mem (front/back) and maxI2S (DMA cadence) then gating on mem+maxI2S <= MAX_LED_MEMORY matches BusManager::memUsage().

Please sanity-check on S2/S3 with and without parallel I2S (8+ outputs) to ensure usesI2S selection is correct per target.


1209-1209: Debug print consistency

You print mem+maxI2S vs BusManager::memUsage(); they should match post‑creation. Good for cross‑check.

wled00/bus_manager.cpp (1)

769-771: Comment alignment with implementation looks good

BusConfig::memUsage() delegates to PolyBus::memUsage() and notes the shared I2S DMA isn’t included. This matches the intended split between per-bus and common buffers.

@DedeHai
Copy link
Collaborator Author

DedeHai commented Sep 14, 2025

@blazoncek I see you pushed an update to your branch. Do I need to cherry pick that as well or is it cosmetic?

@DedeHai
Copy link
Collaborator Author

DedeHai commented Sep 14, 2025

@willmmiles I pulled these changes into my "heap handlint tests" branch. I can get ESP8266 to work with 800 LEDs still but it is very(!) unstable, frequently refusing to fully load the UI (info->reboot still works), at lucky moments there is enough heap to run a PS effect, but its not really useable. On the positive side: at least it does not lock-up.
Wit 400 LEDs it works quite well, 500 is still ok-ish, 600 is pushing it but the UI stays responsive most of the time.
What we should consider adding is a reboot after adding new buses if calculated mem-usage is more than like 50%. MM has something like that where a window pops up to recommend a reboot. (CC @netmindz )

without a reboot, heap is fragmented and nothing works right.

@blazoncek
Copy link
Collaborator

@blazoncek I see you pushed an update to your branch. Do I need to cherry pick that as well or is it cosmetic?

Hit 2 flies with a single strike. 😉

BTW add transition buffer to calculation as transitions are enabled by default.

@DedeHai
Copy link
Collaborator Author

DedeHai commented Sep 14, 2025

Hit 2 flies with a single strike. 😉

lucky strike ;)

BTW add transition buffer to calculation as transitions are enabled by default.

I would argue that those are not strictly required, allocation will fail and transition will not happen. If we want the value to represent "will work without memory issues" I agree. ESP8266 will show the warning at around 220LEDs if transition buffer is added to the calculation, stability issues start appearing above 400 but out of memory for FX buffer in transitions can happen at 200 already, depending on heap fragmentation.

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

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

207-214: Call isS3() and fix the TODO typos; else branch also misses ()

Using isS3 instead of isS3() makes parallelI2S true on non‑ESP32 targets and skews mem calc; also repeat in Line 210. Fix typos in the TODO.

Apply:

-				let parallelI2S = d.Sf.PR.checked && (is32() || isS2() || isS3) && !isD2P(t);
+				let parallelI2S = d.Sf.PR.checked && (is32() || isS2() || isS3()) && !isD2P(t);
@@
-				} else if ((is32() || isS2() || isS3) && n > (parallelI2S ? 7 : 0)) {
+				} else if ((is32() || isS2() || isS3()) && n > (parallelI2S ? 7 : 0)) {
@@
-					dbl = len * ch * 3; // DMA buffer for parallel I2S (TODO: ony the bus with largst LED count should be used)
+					dbl = len * ch * 3; // DMA buffer for parallel I2S (TODO: only the bus with the largest LED count should be used)
🧹 Nitpick comments (3)
wled00/data/settings_leds.htm (3)

199-200: Align pbfr with PR intent: 2 mandatory buffers; add transition buffer only if enabled

Current pbfr = len*12 always counts a second segment buffer for transitions. PR states two mandatory buffers (global + segment). Make the extra buffer conditional on transitions (TD > 0).

Apply:

-			let pbfr = len * 12; // pixel buffers: global buffer + 2*segment buffer (one is required, one for transitions)
+			const transEnabled = parseInt(d.Sf.TD.value, 10) > 0; // 0 ms disables transitions
+			let pbfr = len * (8 + (transEnabled ? 4 : 0)); // 2 mandatory 32-bit buffers (+1 for transitions)

Also applies to: 216-216


196-197: parseInt radix and NaN guard

Be explicit with base 10 and guard SL against NaN to avoid propagating NaN into mem calc.

Apply:

-			let len = parseInt(d.Sf["LC"+n].value);
-			len += parseInt(d.Sf["SL"+n].value); // skipped LEDs are allocated too
+			let len = parseInt(d.Sf["LC"+n].value, 10);
+			len += parseInt(d.Sf["SL"+n].value, 10) || 0; // skipped LEDs are allocated too

204-206: Avoid loose equality for GPIO pin check

Coerce to number and use strict equality.

Apply:

-				if (is8266() && d.Sf["L0"+n].value == 3) { //8266 DMA uses 5x the mem
+				if (is8266() && parseInt(d.Sf["L0"+n].value, 10) === 3) { // 8266 DMA uses 5x the mem
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 857a041 and 8fc1db0.

📒 Files selected for processing (1)
  • wled00/data/settings_leds.htm (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
wled00/data/**/*.{htm,html,css,js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use tabs for indentation in web UI files (.htm/.html/.css/.js) under wled00/data

Files:

  • wled00/data/settings_leds.htm
🧠 Learnings (3)
📓 Common learnings
Learnt from: DedeHai
PR: wled/WLED#4798
File: wled00/FX.cpp:7531-7533
Timestamp: 2025-08-26T11:51:21.817Z
Learning: In WLED PR #4798, DedeHai confirmed that certain gamma-related calls in FX.cpp/FX_fcn.cpp/particle systems are intentional for effect-level shaping (e.g., brightness curves, TV sim, Pride 2015 pre-mix), distinct from final output gamma. Do not flag or remove these in future reviews; add comments when feasible to clarify intent.
Learnt from: mval-sg
PR: wled/WLED#4876
File: wled00/xml.cpp:0-0
Timestamp: 2025-08-28T08:09:20.630Z
Learning: The WLED codebase has opportunities for refactoring hardcoded array bounds (like the "15" used for DMX channels) to use sizeof(array)/sizeof(array[0]) for more maintainable code, but such changes should be done consistently across the entire codebase in a dedicated refactoring effort.
📚 Learning: 2025-08-28T08:09:20.630Z
Learnt from: mval-sg
PR: wled/WLED#4876
File: wled00/xml.cpp:0-0
Timestamp: 2025-08-28T08:09:20.630Z
Learning: The WLED codebase has opportunities for refactoring hardcoded array bounds (like the "15" used for DMX channels) to use sizeof(array)/sizeof(array[0]) for more maintainable code, but such changes should be done consistently across the entire codebase in a dedicated refactoring effort.

Applied to files:

  • wled00/data/settings_leds.htm
📚 Learning: 2025-08-26T11:51:21.817Z
Learnt from: DedeHai
PR: wled/WLED#4798
File: wled00/FX.cpp:7531-7533
Timestamp: 2025-08-26T11:51:21.817Z
Learning: In WLED PR #4798, DedeHai confirmed that certain gamma-related calls in FX.cpp/FX_fcn.cpp/particle systems are intentional for effect-level shaping (e.g., brightness curves, TV sim, Pride 2015 pre-mix), distinct from final output gamma. Do not flag or remove these in future reviews; add comments when feasible to clarify intent.

Applied to files:

  • wled00/data/settings_leds.htm

@netmindz
Copy link
Member

Yeah adding a recommendation to reboot sounds like a good addition @DedeHai

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: 0

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

207-214: Fix bus-id comparisons and avoid double-counting I2S DMA buffer

  • n is a chrID ('0'..'9','A'..), so numeric comparisons like n > 7 and n < 8 are wrong for 'A'.. (NaN), undercounting memory on buses ≥10.
  • The parallel I2S DMA buffer is counted for every n<8 bus, overcounting by up to 8x. Count it once.
-				let parallelI2S = d.Sf.PR.checked && (is32() || isS2() || isS3()) && !isD2P(t);
-				if (isC3() || (isS3() && !parallelI2S)) {
+				let parallelI2S = d.Sf.PR.checked && (is32() || isS2() || isS3()) && !isD2P(t);
+				const ni = parseInt(n, 36) || 0; // '0'..'9','A'.. -> 0..35
+				if (isC3() || (isS3() && !parallelI2S)) {
 					mul = 2; // ESP32 RMT uses double buffer
-				} else if ((is32() || isS2() || isS3()) && n > (parallelI2S ? 7 : 0)) {
+				} else if ((is32() || isS2() || isS3()) && ni > (parallelI2S ? 7 : 0)) {
 					mul = 2; // ESP32 RMT uses double buffer
-				} else if ((parallelI2S && n < 8) || (n == 0 && is32())) { // I2S uses extra DMA buffer
-					dbl = len * ch * 3; // DMA buffer for parallel I2S (TODO: only the bus with largest LED count should be used)
+				} else if ((parallelI2S && ni === 0) || (ni === 0 && is32())) { // I2S uses extra DMA buffer (count once)
+					dbl = len * ch * 3; // TODO: size by the largest parallel bus, not bus 0
 				}

Notes:

  • Using ni fixes the numeric thresholds for 'A'.. buses.
  • Counting DMA once avoids severe overestimation. For full accuracy, compute the largest (len*ch) across the 8 parallel buses once in UI() and remove per-bus dbl from getMem. I can provide that refactor if desired.
    Also: nice catch on calling isS3() here—previous review concern resolved.
🧹 Nitpick comments (3)
wled00/data/settings_leds.htm (3)

196-200: Harden numeric parsing and clarify buffer comment

Guard against NaN when fields are empty and clarify that 3x 32‑bit pixel buffers are included.

-			let len = parseInt(d.Sf["LC"+n].value);
-			len += parseInt(d.Sf["SL"+n].value); // skipped LEDs are allocated too
+			let len  = parseInt(d.Sf["LC"+n].value, 10) || 0;
+			let skip = parseInt(d.Sf["SL"+n].value, 10) || 0; // skipped LEDs are allocated too
+			len += skip;
-			let pbfr = len * 12; // pixel buffers: global buffer + 2*segment buffer (one is required, one for transitions)
+			let pbfr = len * 12; // 3x 32-bit pixel buffers: 1x global + 2x segment (transition default)

204-206: Use strict numeric comparison for 8266 DMA pin check

Avoid implicit coercion on the pin value.

-				if (is8266() && d.Sf["L0"+n].value == 3) { //8266 DMA uses 5x the mem
+				if (is8266() && parseInt(d.Sf["L0"+n].value, 10) === 3) { // 8266 DMA uses 5x the mem
 					mul = 5;
 				}

384-391: Optional: add reboot hint when memory usage is high

Per discussion, show a gentle reboot recommendation when usage is high to mitigate heap fragmentation issues on ESP8266.

 			gId('dbar').style.background = `linear-gradient(90deg, ${bquot > 60 ? (bquot > 90 ? "red":"orange"):"#ccc"} 0 ${bquot}%, #444 ${bquot}% 100%)`;
 			gId('ledwarning').style.display = (maxLC > Math.min(maxPB,800) || bquot > 80) ? 'inline':'none';
 			gId('ledwarning').style.color = (maxLC > Math.max(maxPB,800) || bquot > 100) ? 'red':'orange';
-			gId('wreason').innerHTML = (bquot > 80) ? "80% of max LED memory" +(bquot>100 ? ` (<b>WARNING: using over ${maxM}B!</b>)` : "") : "800 LEDs per output";
+			const rebootHint = (bquot > 50) ? " Consider rebooting after adding outputs to reduce heap fragmentation." : "";
+			gId('wreason').innerHTML = (bquot > 80)
+				? "80% of max LED memory" + (bquot>100 ? ` (<b>WARNING: using over ${maxM}B!</b>)` : "") + rebootHint
+				: "800 LEDs per output";
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8fc1db0 and 287d3d4.

📒 Files selected for processing (1)
  • wled00/data/settings_leds.htm (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
wled00/data/**/*.{htm,html,css,js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use tabs for indentation in web UI files (.htm/.html/.css/.js) under wled00/data

Files:

  • wled00/data/settings_leds.htm
🧠 Learnings (5)
📓 Common learnings
Learnt from: DedeHai
PR: wled/WLED#4798
File: wled00/FX.cpp:7531-7533
Timestamp: 2025-08-26T11:51:21.817Z
Learning: In WLED PR #4798, DedeHai confirmed that certain gamma-related calls in FX.cpp/FX_fcn.cpp/particle systems are intentional for effect-level shaping (e.g., brightness curves, TV sim, Pride 2015 pre-mix), distinct from final output gamma. Do not flag or remove these in future reviews; add comments when feasible to clarify intent.
Learnt from: mval-sg
PR: wled/WLED#4876
File: wled00/xml.cpp:0-0
Timestamp: 2025-08-28T08:09:20.630Z
Learning: The WLED codebase has opportunities for refactoring hardcoded array bounds (like the "15" used for DMX channels) to use sizeof(array)/sizeof(array[0]) for more maintainable code, but such changes should be done consistently across the entire codebase in a dedicated refactoring effort.
📚 Learning: 2025-08-28T08:09:20.630Z
Learnt from: mval-sg
PR: wled/WLED#4876
File: wled00/xml.cpp:0-0
Timestamp: 2025-08-28T08:09:20.630Z
Learning: The WLED codebase has opportunities for refactoring hardcoded array bounds (like the "15" used for DMX channels) to use sizeof(array)/sizeof(array[0]) for more maintainable code, but such changes should be done consistently across the entire codebase in a dedicated refactoring effort.

Applied to files:

  • wled00/data/settings_leds.htm
📚 Learning: 2025-09-01T10:26:17.959Z
Learnt from: mval-sg
PR: wled/WLED#4876
File: wled00/wled_eeprom.cpp:0-0
Timestamp: 2025-09-01T10:26:17.959Z
Learning: In WLED PR #4876, the DMXStartLED EEPROM backward compatibility issue was partially addressed by keeping it at address 2550 and reading it as a 16-bit value, with DMXChannelsValue array moved to addresses 2552-2566. This maintains compatibility with pre-0.11 EEPROM layouts for DMXStartLED, though legacy "Set to 255" (code 6) configurations may still need migration logic.

Applied to files:

  • wled00/data/settings_leds.htm
📚 Learning: 2025-09-04T06:34:21.724Z
Learnt from: CR
PR: wled/WLED#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-04T06:34:21.724Z
Learning: Applies to wled00/html_*.h : DO NOT edit generated embedded web headers (wled00/html_*.h)

Applied to files:

  • wled00/data/settings_leds.htm
📚 Learning: 2025-08-26T11:51:21.817Z
Learnt from: DedeHai
PR: wled/WLED#4798
File: wled00/FX.cpp:7531-7533
Timestamp: 2025-08-26T11:51:21.817Z
Learning: In WLED PR #4798, DedeHai confirmed that certain gamma-related calls in FX.cpp/FX_fcn.cpp/particle systems are intentional for effect-level shaping (e.g., brightness curves, TV sim, Pride 2015 pre-mix), distinct from final output gamma. Do not flag or remove these in future reviews; add comments when feasible to clarify intent.

Applied to files:

  • wled00/data/settings_leds.htm

var n = s.name.substring(2);
var t = parseInt(s.value);
memu += getMem(t, parseInt(n)); // calc memory
memu += getMem(t, n); // calc memory
Copy link
Collaborator

Choose a reason for hiding this comment

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

n is used both as character and a number in getMem().
I prefer to have number instead of character.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

but isnt n passed as 0-9 and A-Z ? can 'A' be parsed into int?

Copy link
Collaborator

Choose a reason for hiding this comment

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

You are correct, but the solution presented here is not correct. n has to be converted.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I see you added a fix to your fork, do you mind adding that to this PR too? I am struggling a bit with JS.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I will once I get back from my trip.

Copy link
Collaborator Author

@DedeHai DedeHai Sep 19, 2025

Choose a reason for hiding this comment

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

I think I managed to extract it. At least it worked in tests but please check.

var nList = d.Sf.querySelectorAll("#mLC input[name^=L]");
nList.forEach((LC,i)=>{
let nm = LC.name.substring(0,2); // field name
let n = parseInt(LC.name.substring(2)); // bus number
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same here.

@DedeHai
Copy link
Collaborator Author

DedeHai commented Sep 16, 2025

I am no longer too suer about this PR being a good idea "as is" for 0.16. At bootup, free heap is plenty, LED size is accepted and buffers are created, after wifi connection heap runs out and UI completely breaks down on ESP8266. This may need some adustment. The calculation is fine however for 0.15.2 porting.

@blazoncek
Copy link
Collaborator

At bootup

It means somebody modified cfg.json by uploading it or editing it using /edit endpoint.
If it is changed via UI, WiFi is initialised and most buffer (although smaller) are allocated. When saving such configuration it has to succeed before it is saved, meaning all buffers are allocated the same size as during bootup.

@DedeHai
Copy link
Collaborator Author

DedeHai commented Sep 17, 2025

It means somebody modified cfg.json by uploading it or editing it using /edit endpoint.

EDIT: or if someone updates from 0.15

If it is changed via UI, WiFi is initialised and most buffer (although smaller) are allocated. When saving such configuration it has to succeed before it is saved, meaning all buffers are allocated the same size as during bootup.

curently, calculation for UI and bus creation differ. What I do not like is to limit it to the 4k including all buffers, that will just bring back #4614. ESP8266 can handle up to about 800 LEDs, maybe a bit less if there are no additional usermods etc. with my current test code. I may need to reduce that number to fully stabilize it.

@DedeHai
Copy link
Collaborator Author

DedeHai commented Sep 17, 2025

btw. what is the reason there are two functions to calculate bus memory? in polybus there is memUsage() and there is getDataSize() both doing the same thing. Any good reason not to get rid of getDataSize()?

@blazoncek
Copy link
Collaborator

both doing the same thing

Are they? I don't think so.
One estimates the other returns actual memory use.

Unfortunately NPB does not provide memory estimation/requirement routines. And you can only get single buffer size out of it once bus is initialised (RMT may use 2 buffers).

@DedeHai
Copy link
Collaborator Author

DedeHai commented Sep 17, 2025

Are they? I don't think so.
One estimates the other returns actual memory use.

ah, got it. do we need both? decision to allocate a bus is made on the estimation, what is the "actual use" used for? also: you fixed only one of them :)

ESP8266 does not use double buffering.
- new warnings for LED buffer use, deny adding a bus if it exceeds limits
- adds recommendations for users (reboot, disable transitions)
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: 0

🧹 Nitpick comments (2)
wled00/bus_wrapper.h (1)

1363-1363: Remove duplicate line 1363

Line 1363 duplicates the assignment from line 1362.

       if (_useParallelI2S) {
         if (num > 15) return I_NONE;
         if (num < 8) offset = 1;  // 8 I2S followed by 8 RMT
-        if (num < 8) offset = 1;  // 8 I2S followed by 8 RMT
       } else {
wled00/data/settings_leds.htm (1)

208-214: Fix typos in comment

-					dbl = len * ch * 3; // DMA buffer for parallel I2S (TODO: ony the bus with largst LED count should be used)
+					dbl = len * ch * 3; // DMA buffer for parallel I2S (TODO: only the bus with the largest LED count should be used)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 287d3d4 and e711b74.

📒 Files selected for processing (5)
  • wled00/FX_fcn.cpp (1 hunks)
  • wled00/bus_manager.cpp (3 hunks)
  • wled00/bus_wrapper.h (4 hunks)
  • wled00/cfg.cpp (1 hunks)
  • wled00/data/settings_leds.htm (10 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • wled00/FX_fcn.cpp
  • wled00/cfg.cpp
🧰 Additional context used
📓 Path-based instructions (2)
wled00/**/*.{cpp,h}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use spaces (2 per level) for C++ firmware files

Files:

  • wled00/bus_wrapper.h
  • wled00/bus_manager.cpp
wled00/data/**/*.{htm,html,css,js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use tabs for indentation in web UI files (.htm/.html/.css/.js) under wled00/data

Files:

  • wled00/data/settings_leds.htm
🧠 Learnings (7)
📓 Common learnings
Learnt from: DedeHai
PR: wled/WLED#4939
File: wled00/FX_fcn.cpp:1176-1187
Timestamp: 2025-09-16T18:08:42.848Z
Learning: In WLED finalizeInit() bus creation (wled00/FX_fcn.cpp), intentionally allowing memory overruns when bus configurations exceed MAX_LED_MEMORY is a deliberate design choice. The trade-off prioritizes creating buses with reduced LED counts over completely failing to create buses, which would cause no LED output and UI failures. This approach forces users to update configurations after migrating to version 0.16 while maintaining basic functionality.
Learnt from: DedeHai
PR: wled/WLED#4798
File: wled00/FX.cpp:7531-7533
Timestamp: 2025-08-26T11:51:21.817Z
Learning: In WLED PR #4798, DedeHai confirmed that certain gamma-related calls in FX.cpp/FX_fcn.cpp/particle systems are intentional for effect-level shaping (e.g., brightness curves, TV sim, Pride 2015 pre-mix), distinct from final output gamma. Do not flag or remove these in future reviews; add comments when feasible to clarify intent.
Learnt from: mval-sg
PR: wled/WLED#4876
File: wled00/xml.cpp:0-0
Timestamp: 2025-08-28T08:09:20.630Z
Learning: The WLED codebase has opportunities for refactoring hardcoded array bounds (like the "15" used for DMX channels) to use sizeof(array)/sizeof(array[0]) for more maintainable code, but such changes should be done consistently across the entire codebase in a dedicated refactoring effort.
Learnt from: BobLoeffler68
PR: wled/WLED#4891
File: wled00/FX.cpp:3333-3349
Timestamp: 2025-08-31T03:38:14.114Z
Learning: WLED PacMan effect (wled00/FX.cpp): Keep pacmancharacters_t position fields as signed int (not int16_t). Maintainer preference (blazoncek) prioritizes avoiding potential overhead/regressions over minor RAM savings. Avoid type shrinking here unless memory pressure is demonstrated.
📚 Learning: 2025-09-02T01:45:58.047Z
Learnt from: willmmiles
PR: wled/WLED#4890
File: lib/NeoESP32RmtHI/include/NeoEsp32RmtHIMethod.h:31-36
Timestamp: 2025-09-02T01:45:58.047Z
Learning: For the NeoEsp32RmtHI driver, RISC-V ESP32-C3 support is currently disabled via bus_wrapper.h rather than compile-time guards, as the maintainer willmmiles is working on resolving underlying nested interrupt issues and prefers to centralize the workaround in one location.

Applied to files:

  • wled00/bus_wrapper.h
📚 Learning: 2025-09-16T18:08:42.848Z
Learnt from: DedeHai
PR: wled/WLED#4939
File: wled00/FX_fcn.cpp:1176-1187
Timestamp: 2025-09-16T18:08:42.848Z
Learning: In WLED finalizeInit() bus creation (wled00/FX_fcn.cpp), intentionally allowing memory overruns when bus configurations exceed MAX_LED_MEMORY is a deliberate design choice. The trade-off prioritizes creating buses with reduced LED counts over completely failing to create buses, which would cause no LED output and UI failures. This approach forces users to update configurations after migrating to version 0.16 while maintaining basic functionality.

Applied to files:

  • wled00/bus_wrapper.h
  • wled00/bus_manager.cpp
  • wled00/data/settings_leds.htm
📚 Learning: 2025-09-18T03:17:30.081Z
Learnt from: willmmiles
PR: wled/WLED#4939
File: wled00/FX_fcn.cpp:1183-1194
Timestamp: 2025-09-18T03:17:30.081Z
Learning: In WLED finalizeInit() bus creation (wled00/FX_fcn.cpp), digitalCount is intentionally incremented even for placeholder buses to maintain consistent resource mapping across all bus configurations.

Applied to files:

  • wled00/bus_wrapper.h
  • wled00/bus_manager.cpp
  • wled00/data/settings_leds.htm
📚 Learning: 2025-08-28T08:09:20.630Z
Learnt from: mval-sg
PR: wled/WLED#4876
File: wled00/xml.cpp:0-0
Timestamp: 2025-08-28T08:09:20.630Z
Learning: The WLED codebase has opportunities for refactoring hardcoded array bounds (like the "15" used for DMX channels) to use sizeof(array)/sizeof(array[0]) for more maintainable code, but such changes should be done consistently across the entire codebase in a dedicated refactoring effort.

Applied to files:

  • wled00/data/settings_leds.htm
📚 Learning: 2025-08-26T11:51:21.817Z
Learnt from: DedeHai
PR: wled/WLED#4798
File: wled00/FX.cpp:7531-7533
Timestamp: 2025-08-26T11:51:21.817Z
Learning: In WLED PR #4798, DedeHai confirmed that certain gamma-related calls in FX.cpp/FX_fcn.cpp/particle systems are intentional for effect-level shaping (e.g., brightness curves, TV sim, Pride 2015 pre-mix), distinct from final output gamma. Do not flag or remove these in future reviews; add comments when feasible to clarify intent.

Applied to files:

  • wled00/data/settings_leds.htm
📚 Learning: 2025-09-01T10:26:17.959Z
Learnt from: mval-sg
PR: wled/WLED#4876
File: wled00/wled_eeprom.cpp:0-0
Timestamp: 2025-09-01T10:26:17.959Z
Learning: In WLED PR #4876, the DMXStartLED EEPROM backward compatibility issue was partially addressed by keeping it at address 2550 and reading it as a 16-bit value, with DMXChannelsValue array moved to addresses 2552-2566. This maintains compatibility with pre-0.11 EEPROM layouts for DMXStartLED, though legacy "Set to 255" (code 6) configurations may still need migration logic.

Applied to files:

  • wled00/data/settings_leds.htm
🧬 Code graph analysis (1)
wled00/bus_manager.cpp (2)
wled00/bus_manager.h (1)
  • BusDigital (234-285)
wled00/bus_wrapper.h (3)
  • getDataSize (1111-1207)
  • memUsage (1209-1276)
  • isParallelI2S1Output (338-338)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_160)
  • GitHub Check: wled_build / Build Environments (esp32s3_4M_qspi)
  • GitHub Check: wled_build / Build Environments (lolin_s2_mini)
  • GitHub Check: wled_build / Build Environments (usermods)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_16MB_opi)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_8MB_opi)
  • GitHub Check: wled_build / Build Environments (esp32_eth)
  • GitHub Check: wled_build / Build Environments (esp32dev)
🔇 Additional comments (9)
wled00/bus_wrapper.h (2)

472-485: Good work on the ESP32 RMT channel allocation fix!

The channel adjustment logic correctly accounts for parallel I2S usage across different ESP32 variants. This addresses the issue where I2S bus at position 0 would conflict with RMT channels.


1116-1275: Ignore the 4× change—the existing multiplier is deliberate
getDataSize()’s 4× factor covers both the I2S front buffer and its DMA pipeline, whereas memUsage() reports only the per-bus pixel buffer (excluding the shared DMA buffer managed later in BusManager). Removing the 4× would under-allocate the DMA backing required by NeoPixelBus’s I2S methods.

Likely an incorrect or invalid review comment.

wled00/bus_manager.cpp (3)

327-328: LGTM - Proper I2S DMA buffer separation

The comment clearly indicates that the common I2S DMA buffer is accounted for separately in BusManager::memUsage(), avoiding double-counting.


773-799: Excellent I2S memory accounting implementation!

The refactored memory calculation properly:

  1. Separates per-bus memory from shared I2S DMA buffers
  2. Uses the correct cadence factor (3 or 4 step) for DMA buffer sizing
  3. Only allocates I2S memory for the appropriate buses (first 8 for parallel, first 1 for single)
  4. Returns the total as size + maxI2S preventing double-counting

787-794: Validate I2S common buffer size calculation

Verify that i2sCommonSize = stepFactor * bus->getLength() * bus->getNumberOfChannels() * (bus->is16bit()+1) matches NeoPixelBus's actual DMA buffer allocation — confirm the correct stepFactor (3 vs 4) for cadence and that bus->is16bit() should add one byte per channel.

Location: wled00/bus_manager.cpp (lines 787-794).

wled00/data/settings_leds.htm (4)

27-27: Good addition of the toNum helper function

The toNum() function correctly converts bus identifiers (0-9, A-Z) to numeric values (0-35), properly handling the expanded bus addressing.


195-217: Memory calculation properly accounts for pixel buffers and I2S DMA

The updated getMem() function correctly:

  1. Adds 8-byte pixel buffers per LED (global + segment buffers)
  2. Handles parallel I2S DMA buffer allocation for the first 8 buses
  3. Uses the proper 3x cadence multiplier for I2S DMA
  4. Applies the correct multipliers based on bus type and position

389-389: UI memory warning text updated appropriately

The warning message correctly shows when memory usage exceeds 80% and includes an additional alert when over 100% of available memory.


60-105: Well-structured pin conflict validation

The refactored pinsOK() function properly:

  1. Uses the new toNum() for bus identification
  2. Builds a comprehensive used-pin list from reserved and UI pins
  3. Validates against both reserved and read-only GPIO pins
  4. Provides clear error messages with the conflicting pin lists

Copy link
Member

@willmmiles willmmiles left a comment

Choose a reason for hiding this comment

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

Some small things. Math looks right to me.

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

🧹 Nitpick comments (2)
wled00/bus_wrapper.h (1)

1272-1273: Default case doubles buffer size for unknown bus types

Line 1272 adds a fallback that doubles the memory size for any unrecognized bus type. This could mask configuration errors or new bus types that haven't been properly added to the switch statement.

Consider logging a warning or using a more conservative default (e.g., no multiplication) to avoid silent memory overallocation.

-      default               : size *= 2;                   break; // everything else uses 2 buffers
+      default               : /* size unchanged */         break; // unknown bus types use single buffer
wled00/FX_fcn.cpp (1)

1189-1196: Consider extracting I2S detection logic for maintainability

The platform-specific I2S detection logic is complex and duplicated. Consider extracting it into a helper function for better maintainability.

+  #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(ESP8266)
+  static bool wouldUseI2S(unsigned digitalCount) {
+    #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S3)
+    return (useParallelI2S && digitalCount <= 8) || (!useParallelI2S && digitalCount == 1);
+    #elif defined(CONFIG_IDF_TARGET_ESP32S2)
+    return useParallelI2S && digitalCount <= 8;
+    #else
+    return false;
+    #endif
+  }
+  #endif
+
   // estimate maximum I2S memory usage (only relevant for digital non-2pin busses)
   #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(ESP8266)
-    #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S3)
-  const bool usesI2S = ((useParallelI2S && digitalCount <= 8) || (!useParallelI2S && digitalCount == 1));
-    #elif defined(CONFIG_IDF_TARGET_ESP32S2)
-  const bool usesI2S = (useParallelI2S && digitalCount <= 8);
-    #else
-  const bool usesI2S = false;
-    #endif
+  const bool usesI2S = wouldUseI2S(digitalCount);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e711b74 and 2f98c48.

📒 Files selected for processing (2)
  • wled00/FX_fcn.cpp (1 hunks)
  • wled00/bus_wrapper.h (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
wled00/**/*.{cpp,h}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use spaces (2 per level) for all C++ source and header files

Files:

  • wled00/FX_fcn.cpp
  • wled00/bus_wrapper.h
🧠 Learnings (13)
📓 Common learnings
Learnt from: DedeHai
PR: wled/WLED#4939
File: wled00/FX_fcn.cpp:1176-1187
Timestamp: 2025-09-16T18:08:42.848Z
Learning: In WLED finalizeInit() bus creation (wled00/FX_fcn.cpp), intentionally allowing memory overruns when bus configurations exceed MAX_LED_MEMORY is a deliberate design choice. The trade-off prioritizes creating buses with reduced LED counts over completely failing to create buses, which would cause no LED output and UI failures. This approach forces users to update configurations after migrating to version 0.16 while maintaining basic functionality.
Learnt from: DedeHai
PR: wled/WLED#4798
File: wled00/FX.cpp:7531-7533
Timestamp: 2025-08-26T11:51:21.817Z
Learning: In WLED PR #4798, DedeHai confirmed that certain gamma-related calls in FX.cpp/FX_fcn.cpp/particle systems are intentional for effect-level shaping (e.g., brightness curves, TV sim, Pride 2015 pre-mix), distinct from final output gamma. Do not flag or remove these in future reviews; add comments when feasible to clarify intent.
Learnt from: mval-sg
PR: wled/WLED#4876
File: wled00/xml.cpp:0-0
Timestamp: 2025-08-28T08:09:20.630Z
Learning: The WLED codebase has opportunities for refactoring hardcoded array bounds (like the "15" used for DMX channels) to use sizeof(array)/sizeof(array[0]) for more maintainable code, but such changes should be done consistently across the entire codebase in a dedicated refactoring effort.
📚 Learning: 2025-09-16T18:08:42.848Z
Learnt from: DedeHai
PR: wled/WLED#4939
File: wled00/FX_fcn.cpp:1176-1187
Timestamp: 2025-09-16T18:08:42.848Z
Learning: In WLED finalizeInit() bus creation (wled00/FX_fcn.cpp), intentionally allowing memory overruns when bus configurations exceed MAX_LED_MEMORY is a deliberate design choice. The trade-off prioritizes creating buses with reduced LED counts over completely failing to create buses, which would cause no LED output and UI failures. This approach forces users to update configurations after migrating to version 0.16 while maintaining basic functionality.

Applied to files:

  • wled00/FX_fcn.cpp
  • wled00/bus_wrapper.h
📚 Learning: 2025-09-18T03:17:30.081Z
Learnt from: willmmiles
PR: wled/WLED#4939
File: wled00/FX_fcn.cpp:1183-1194
Timestamp: 2025-09-18T03:17:30.081Z
Learning: In WLED finalizeInit() bus creation (wled00/FX_fcn.cpp), digitalCount is intentionally incremented even for placeholder buses to maintain consistent resource mapping across all bus configurations.

Applied to files:

  • wled00/FX_fcn.cpp
  • wled00/bus_wrapper.h
📚 Learning: 2025-09-18T03:17:30.081Z
Learnt from: willmmiles
PR: wled/WLED#4939
File: wled00/FX_fcn.cpp:1183-1194
Timestamp: 2025-09-18T03:17:30.081Z
Learning: In WLED BusManager (wled00/FX_fcn.cpp), direct access to BusManager::busses is part of the intended public API design, not an encapsulation violation. The wrapper accessor methods are considered unnecessary obfuscation that should be removed in future refactoring.

Applied to files:

  • wled00/FX_fcn.cpp
📚 Learning: 2025-08-26T11:51:21.817Z
Learnt from: DedeHai
PR: wled/WLED#4798
File: wled00/FX.cpp:7531-7533
Timestamp: 2025-08-26T11:51:21.817Z
Learning: In WLED PR #4798, DedeHai confirmed that certain gamma-related calls in FX.cpp/FX_fcn.cpp/particle systems are intentional for effect-level shaping (e.g., brightness curves, TV sim, Pride 2015 pre-mix), distinct from final output gamma. Do not flag or remove these in future reviews; add comments when feasible to clarify intent.

Applied to files:

  • wled00/FX_fcn.cpp
📚 Learning: 2025-09-18T02:59:33.600Z
Learnt from: willmmiles
PR: wled/WLED#4928
File: wled00/FX_fcn.cpp:1181-1181
Timestamp: 2025-09-18T02:59:33.600Z
Learning: In WLED, getFreeHeapSize() is a platform-agnostic function defined in fcn_declare.h that provides accurate free heap reporting across ESP32 and ESP8266 platforms. On ESP32, it uses heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) instead of ESP.getFreeHeap() because the latter can include other memory types. On ESP8266, it uses ESP.getFreeHeap().

Applied to files:

  • wled00/FX_fcn.cpp
📚 Learning: 2025-08-29T15:38:46.208Z
Learnt from: DedeHai
PR: wled/WLED#4791
File: wled00/FX_fcn.cpp:1187-1191
Timestamp: 2025-08-29T15:38:46.208Z
Learning: In WLED's allocate_buffer() function, BFRALLOC_ENFORCE_PSRAM already includes fallback logic to DRAM if PSRAM is not available, as documented in the comment "use PSRAM if available, otherwise fall back to DRAM". The function also uses validateFreeHeap() for additional safety checks. During setup() when finalizeInit() runs, PSRAM has vast available memory making failures unlikely.

Applied to files:

  • wled00/FX_fcn.cpp
📚 Learning: 2025-08-28T08:09:20.630Z
Learnt from: mval-sg
PR: wled/WLED#4876
File: wled00/xml.cpp:0-0
Timestamp: 2025-08-28T08:09:20.630Z
Learning: The WLED codebase has opportunities for refactoring hardcoded array bounds (like the "15" used for DMX channels) to use sizeof(array)/sizeof(array[0]) for more maintainable code, but such changes should be done consistently across the entire codebase in a dedicated refactoring effort.

Applied to files:

  • wled00/FX_fcn.cpp
📚 Learning: 2025-09-15T19:13:56.469Z
Learnt from: DedeHai
PR: wled/WLED#4791
File: wled00/util.cpp:737-743
Timestamp: 2025-09-15T19:13:56.469Z
Learning: In WLED's util.cpp, the *_realloc_malloc functions (p_realloc_malloc and d_realloc_malloc) are intentionally designed to free the original buffer on realloc failure and allocate a new buffer, implementing a "replace buffer" semantic rather than traditional realloc behavior. This is documented in the function comments and is the intended design by the author DedeHai.

Applied to files:

  • wled00/FX_fcn.cpp
📚 Learning: 2025-04-30T05:41:03.633Z
Learnt from: blazoncek
PR: wled/WLED#4667
File: usermods/user_fx/user_fx.cpp:27-30
Timestamp: 2025-04-30T05:41:03.633Z
Learning: In WLED, the Segment::allocateData() method already includes optimization to check if data is allocated and sufficiently sized, handling buffer reuse to reduce memory fragmentation. Adding an external check like `if (SEGENV.data == nullptr && !SEGENV.allocateData(dataSize))` is unnecessary and could be problematic, as it bypasses proper size verification.

Applied to files:

  • wled00/FX_fcn.cpp
📚 Learning: 2025-09-18T03:17:30.081Z
Learnt from: willmmiles
PR: wled/WLED#4939
File: wled00/FX_fcn.cpp:1183-1194
Timestamp: 2025-09-18T03:17:30.081Z
Learning: In WLED bus management code, dead code branches in error handling (like unreachable else breaks) are intentionally preserved to accommodate potential future API changes, even when currently unreachable.

Applied to files:

  • wled00/FX_fcn.cpp
📚 Learning: 2025-08-29T01:34:34.358Z
Learnt from: willmmiles
PR: wled/WLED#4853
File: wled00/util.cpp:779-781
Timestamp: 2025-08-29T01:34:34.358Z
Learning: On ESP8266 systems, avoid adding no-op stub functions across translation units due to limited code memory constraints, as the compiler cannot inline away the function calls, resulting in wasteful memory usage.

Applied to files:

  • wled00/FX_fcn.cpp
📚 Learning: 2025-08-31T03:38:14.114Z
Learnt from: BobLoeffler68
PR: wled/WLED#4891
File: wled00/FX.cpp:3333-3349
Timestamp: 2025-08-31T03:38:14.114Z
Learning: WLED PacMan effect (wled00/FX.cpp): Keep pacmancharacters_t position fields as signed int (not int16_t). Maintainer preference (blazoncek) prioritizes avoiding potential overhead/regressions over minor RAM savings. Avoid type shrinking here unless memory pressure is demonstrated.

Applied to files:

  • wled00/FX_fcn.cpp
🧬 Code graph analysis (1)
wled00/FX_fcn.cpp (4)
wled00/fcn_declare.h (2)
  • getFreeHeapSize (462-462)
  • getFreeHeapSize (465-465)
wled00/bus_manager.h (7)
  • isDigital (141-142)
  • isDigital (180-180)
  • is2Pin (181-181)
  • hasRGB (163-165)
  • hasWhite (166-172)
  • hasCCT (173-178)
  • is16bit (185-185)
wled00/bus_manager.cpp (8)
  • add (76-81)
  • add (76-76)
  • add (801-822)
  • add (801-801)
  • memUsage (759-770)
  • memUsage (759-759)
  • memUsage (773-799)
  • memUsage (773-773)
wled00/bus_wrapper.h (1)
  • memUsage (1209-1276)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: wled_build / Build Environments (usermods)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_16MB_opi)
🔇 Additional comments (6)
wled00/bus_wrapper.h (1)

1116-1163: ESP8266 memory calculation refactoring looks good

The removal of doubling (*2) factors for ESP8266 UART and BitBang bus types aligns with the new I2S-aware memory accounting approach. These bus types no longer artificially inflate their memory requirements, which should provide more accurate memory usage reporting.

wled00/FX_fcn.cpp (5)

1181-1181: Good addition of heap size diagnostic

Adding heap size printout before bus creation helps with debugging memory issues, especially relevant for the ESP8266 stability concerns mentioned in the PR comments.


1183-1217: I2S memory accounting implementation addresses the core issue

This implementation correctly accounts for I2S DMA buffer memory that was previously excluded from memory calculations. The architecture-specific handling for ESP32 variants (S2, S3, C3) and the cadence step factor configuration align with the NeoPixelBus I2S implementation details.

The combined check mem + maxI2S <= MAX_LED_MEMORY ensures that both regular buffers and the shared I2S DMA buffer fit within the memory limit, directly addressing the issue where configurations with >659 LEDs were failing to save.


1203-1204: I2S common buffer calculation is correct

The calculation properly accounts for:

  • Cadence step factor (3 or 4 bits per pixel bit)
  • Number of channels (RGB/White/CCT)
  • 16-bit bus types

This matches the actual memory requirements of the NeoPixelBus I2S implementation.


1212-1212: ErrorFlag setting was removed

Based on past review comments, the errorFlag = ERR_NORAM_PX; assignment was removed here during merge conflict resolution but should be restored to properly indicate memory allocation failures to the UI.


1207-1210: Early termination on memory overflow prevents partial bus creation

Breaking the loop when memory limit is exceeded ensures that buses are created in order until memory is exhausted, rather than skipping individual buses. This maintains a predictable initialization sequence.

Comment on lines 1248 to 1275
case I_8266_DM_SM16825_5: size = (size + 2*count)*2*5; break;
#endif
#ifdef ARDUINO_ARCH_ESP32
// RMT buses (1x front and 1x back buffer)
case I_32_RN_NEO_4: size = (size + count)*2; break;
case I_32_RN_TM1_4: size = (size + count)*2; break;
case I_32_RN_UCS_3: size *= 2*2; break;
case I_32_RN_UCS_4: size = (size + count)*2*2; break;
case I_32_RN_FW6_5: size = (size + 2*count)*2; break;
case I_32_RN_2805_5: size = (size + 2*count)*2; break;
case I_32_RN_SM16825_5: size = (size + 2*count)*2*2; break;
// I2S1 bus or paralell buses (individual 1x front and 1 DMA (3x or 4x pixel count) or common back DMA buffers)
#else
// RMT buses (1x front and 1x back buffer, does not include small RMT buffer)
case I_32_RN_NEO_4 : // fallthrough
case I_32_RN_TM1_4 : size = (size + count)*2; break; // 4 channels
case I_32_RN_UCS_3 : size *= 2*2; break; // 16bit
case I_32_RN_UCS_4 : size = (size + count)*2*2; break; // 16bit, 4 channels
case I_32_RN_FW6_5 : // fallthrough
case I_32_RN_2805_5 : size = (size + 2*count)*2; break; // 5 channels
case I_32_RN_SM16825_5: size = (size + 2*count)*2*2; break; // 16bit, 5 channels
// I2S1 bus or paralell I2S1 buses (1x front, does not include DMA buffer which is front*cadence, a bit(?) more for LCD)
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I2_NEO_3: size *= 4; break;
case I_32_I2_NEO_4: size = (size + count)*4; break;
case I_32_I2_400_3: size *= 4; break;
case I_32_I2_TM1_4: size = (size + count)*4; break;
case I_32_I2_TM2_3: size *= 4; break;
case I_32_I2_UCS_3: size *= 2*4; break;
case I_32_I2_UCS_4: size = (size + count)*2*4; break;
case I_32_I2_APA106_3: size *= 4; break;
case I_32_I2_FW6_5: size = (size + 2*count)*4; break;
case I_32_I2_2805_5: size = (size + 2*count)*4; break;
case I_32_I2_TM1914_3: size *= 4; break;
case I_32_I2_SM16825_5: size = (size + 2*count)*2*4; break;
case I_32_I2_NEO_3 : // fallthrough
case I_32_I2_400_3 : // fallthrough
case I_32_I2_TM2_3 : // fallthrough
case I_32_I2_APA106_3 : break; // do nothing, I2S uses single buffer + DMA buffer
case I_32_I2_NEO_4 : // fallthrough
case I_32_I2_TM1_4 : size = (size + count); break; // 4 channels
case I_32_I2_UCS_3 : size *= 2; break; // 16 bit
case I_32_I2_UCS_4 : size = (size + count)*2; break; // 16 bit, 4 channels
case I_32_I2_FW6_5 : // fallthrough
case I_32_I2_2805_5 : size = (size + 2*count); break; // 5 channels
case I_32_I2_SM16825_5: size = (size + 2*count)*2; break; // 16 bit, 5 channels
#endif
default : size *= 2; break; // everything else uses 2 buffers
#endif
// everything else uses 2 buffers
default: size *= 2; break;
}
return size;
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 21, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

Missing parallel I2S bus memory accounting in memUsage()

The comment on line 1258 mentions "I2S1 bus or paralell I2S1 buses" but the memory calculation doesn't account for parallel I2S when _useParallelI2S is true. This inconsistency with the getDataSize() function (lines 1181-1192) could lead to incorrect memory estimates.

Consider aligning this with the actual memory requirements when parallel I2S is in use.


🏁 Script executed:

#!/bin/bash
# Verify if parallel I2S flag is used in memUsage calculation elsewhere
rg -n "_useParallelI2S" wled00/bus_wrapper.h -C3

Length of output: 22713


memUsage(): account for parallel I2S (_useParallelI2S) for I2S cases

getDataSize() uses useParallelI2S (wled00/bus_wrapper.h:1178–1192) but memUsage() does not (wled00/bus_wrapper.h:1248–1275); update the I_32_I2* branches in memUsage() to check _useParallelI2S and compute size consistent with getDataSize() (i.e., mirror the IP vs I2 class accounting / use the IP bus PixelsSize()*4 where appropriate).

🤖 Prompt for AI Agents
In wled00/bus_wrapper.h around lines 1248–1275, memUsage() currently treats all
I_32_I2_* cases the same and does not consider _useParallelI2S; update the
I_32_I2_* branches to mirror getDataSize() (lines ~1178–1192) by checking
_useParallelI2S and computing size as the IP-class path when parallel I2S is
used (i.e., account using the IP bus PixelsSize()*4 semantics) and otherwise use
the non-parallel I2 accounting (single buffer + DMA buffer semantics), so the
memUsage() result matches getDataSize() for both parallel and non-parallel I2S
configurations.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

the reason _useParallelI2S is used is to ge the correct function pointer, not to change the math.

Copy link
Contributor

Choose a reason for hiding this comment

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

@DedeHai Thank you for the clarification! You're absolutely right. Looking at the getDataSize() function, I can see that both the parallel I2S path (static_cast<B_32_IP_*>()->PixelsSize()*4) and non-parallel I2S path (static_cast<B_32_I2_*>()->PixelsSize()*4) return the same calculation - PixelsSize()*4. The _useParallelI2S flag is indeed used to select the correct bus type/function pointer rather than to change the underlying memory math.

The memory requirements are fundamentally the same for both configurations, so the memUsage() function correctly doesn't need to differentiate based on the parallel I2S flag. I appreciate you pointing this out!


✏️ Learnings added
Learnt from: DedeHai
PR: wled/WLED#4928
File: wled00/bus_wrapper.h:1248-1275
Timestamp: 2025-09-21T20:44:09.492Z
Learning: In WLED bus_wrapper.h, the _useParallelI2S flag is used for selecting the correct function pointer (between parallel I2S and non-parallel I2S bus types) rather than for changing memory calculations. Both parallel and non-parallel I2S configurations have the same memory requirements, so memUsage() correctly doesn't differentiate based on this flag.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: willmmiles
PR: wled/WLED#4939
File: wled00/FX_fcn.cpp:1183-1194
Timestamp: 2025-09-18T03:17:30.081Z
Learning: In WLED finalizeInit() bus creation (wled00/FX_fcn.cpp), digitalCount is intentionally incremented even for placeholder buses to maintain consistent resource mapping across all bus configurations.

Learnt from: DedeHai
PR: wled/WLED#4939
File: wled00/FX_fcn.cpp:1176-1187
Timestamp: 2025-09-16T18:08:42.848Z
Learning: In WLED finalizeInit() bus creation (wled00/FX_fcn.cpp), intentionally allowing memory overruns when bus configurations exceed MAX_LED_MEMORY is a deliberate design choice. The trade-off prioritizes creating buses with reduced LED counts over completely failing to create buses, which would cause no LED output and UI failures. This approach forces users to update configurations after migrating to version 0.16 while maintaining basic functionality.

Learnt from: blazoncek
PR: wled/WLED#4667
File: usermods/user_fx/user_fx.cpp:27-30
Timestamp: 2025-04-30T05:41:03.633Z
Learning: In WLED, the Segment::allocateData() method already includes optimization to check if data is allocated and sufficiently sized, handling buffer reuse to reduce memory fragmentation. Adding an external check like `if (SEGENV.data == nullptr && !SEGENV.allocateData(dataSize))` is unnecessary and could be problematic, as it bypasses proper size verification.

Learnt from: DedeHai
PR: wled/WLED#4798
File: wled00/FX.cpp:7531-7533
Timestamp: 2025-08-26T11:51:21.817Z
Learning: In WLED PR #4798, DedeHai confirmed that certain gamma-related calls in FX.cpp/FX_fcn.cpp/particle systems are intentional for effect-level shaping (e.g., brightness curves, TV sim, Pride 2015 pre-mix), distinct from final output gamma. Do not flag or remove these in future reviews; add comments when feasible to clarify intent.

Learnt from: mval-sg
PR: wled/WLED#4876
File: wled00/xml.cpp:0-0
Timestamp: 2025-08-28T08:09:20.630Z
Learning: The WLED codebase has opportunities for refactoring hardcoded array bounds (like the "15" used for DMX channels) to use sizeof(array)/sizeof(array[0]) for more maintainable code, but such changes should be done consistently across the entire codebase in a dedicated refactoring effort.

@DedeHai DedeHai merged commit 4d39dd0 into wled:main Sep 21, 2025
20 checks passed
netmindz pushed a commit to netmindz/WLED-MM that referenced this pull request Sep 23, 2025
* Attempt at better bus memory calculation and estimation
* Remov double buffer count for ESP8266 (thanks @DedeHai)
* improve UI calculation
* adding mendatory LED buffers to UI memory calculation
* adding buffer for transitions to memory calculation, change "error" to "warning"
* bugfixes in settings_leds.htm
* fix getDataSize() forESP8266, ESP8266 does not use double buffering.
* update led settings: fix parsing by @blazoncek
* new warnings for LED buffer use, deny adding a bus if it exceeds limits
* adds recommendations for users (reboot, disable transitions)

Co-authored-by: Blaž Kristan <blaz@kristan-sp.si>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Hardware Setup Length can't be saved after 659
4 participants