From 6c0c875fe66e3150c421d60edf0a9bdfcece9c48 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Sep 2025 04:43:20 +0000 Subject: [PATCH 1/6] Initial plan From a441ee81b678daf25528db7408f2dc8bc59106fe Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Sep 2025 04:52:35 +0000 Subject: [PATCH 2/6] Implement bus creation fallback when memory limit exceeded Co-authored-by: DedeHai <6280424+DedeHai@users.noreply.github.com> --- wled00/FX_fcn.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 2f8d5515fd..3be3e533dc 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -13,6 +13,10 @@ #include "FXparticleSystem.h" // TODO: better define the required function (mem service) in FX.h? #include "palettes.h" +#ifndef DEFAULT_LED_COLOR_ORDER + #define DEFAULT_LED_COLOR_ORDER COL_ORDER_GRB //default to GRB +#endif + /* Custom per-LED mapping has moved! @@ -1182,6 +1186,14 @@ void WS2812FX::finalizeInit() { busConfigs.clear(); busConfigs.shrink_to_fit(); + // ensure at least one bus exists as fallback to prevent UI issues and provide basic LED output + if (BusManager::getNumBusses() == 0) { + DEBUG_PRINTLN(F("No buses created due to memory limits! Creating fallback bus with default LED count.")); + uint8_t defPin[1] = {DEFAULT_LED_PIN}; + BusConfig defCfg = BusConfig(DEFAULT_LED_TYPE, defPin, 0, DEFAULT_LED_COUNT, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY, 0); + BusManager::add(defCfg); + } + _length = 0; for (size_t i=0; i Date: Mon, 15 Sep 2025 05:10:24 +0000 Subject: [PATCH 3/6] Improve bus creation with memory-aware truncation and UI error handling Co-authored-by: DedeHai <6280424+DedeHai@users.noreply.github.com> --- wled00/FX_fcn.cpp | 102 +++++++++++++++++++++++++++++++++++++------ wled00/data/index.js | 15 ++++++- 2 files changed, 101 insertions(+), 16 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 3be3e533dc..78f36651c9 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1175,24 +1175,98 @@ void WS2812FX::finalizeInit() { digitalCount = 0; #endif - // create buses/outputs + // create buses/outputs with memory-aware truncation unsigned mem = 0; - for (const auto &bus : busConfigs) { - mem += bus.memUsage(Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type) ? digitalCount++ : 0); // includes global buffer - if (mem <= MAX_LED_MEMORY) { - if (BusManager::add(bus) == -1) break; - } else DEBUG_PRINTF_P(PSTR("Out of LED memory! Bus %d (%d) #%u not created."), (int)bus.type, (int)bus.count, digitalCount); + std::vector truncatedConfigs; + + for (auto bus : busConfigs) { + // Calculate what this bus would use with its current configuration + unsigned busMemUsage = bus.memUsage(Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type) ? digitalCount : 0); + + if (mem + busMemUsage <= MAX_LED_MEMORY) { + // Bus fits as-is, add it + if (BusManager::add(bus) != -1) { + mem += busMemUsage; + if (Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type)) digitalCount++; + truncatedConfigs.push_back(bus); + } else break; + } else { + // Bus doesn't fit, try to truncate LED count to fit available memory + unsigned remainingMem = MAX_LED_MEMORY - mem; + if (remainingMem > 0) { + // Binary search to find maximum LED count that fits in remaining memory + uint16_t maxCount = bus.count; + uint16_t minCount = 1; + uint16_t bestCount = 0; + + while (minCount <= maxCount) { + uint16_t testCount = minCount + (maxCount - minCount) / 2; + BusConfig testBus = bus; + testBus.count = testCount; + + unsigned testMem = testBus.memUsage(Bus::isDigital(testBus.type) && !Bus::is2Pin(testBus.type) ? digitalCount : 0); + + if (testMem <= remainingMem) { + bestCount = testCount; + minCount = testCount + 1; + } else { + maxCount = testCount - 1; + } + } + + if (bestCount > 0) { + // Found a truncated configuration that fits + bus.count = bestCount; + DEBUG_PRINTF_P(PSTR("Truncated bus %d from %u to %u LEDs to fit memory constraints.\n"), (int)bus.type, (int)maxCount, (int)bestCount); + + if (BusManager::add(bus) != -1) { + mem += bus.memUsage(Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type) ? digitalCount : 0); + if (Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type)) digitalCount++; + truncatedConfigs.push_back(bus); + } + } else { + DEBUG_PRINTF_P(PSTR("Out of LED memory! Bus %d (%d) #%u not created - no room for even 1 LED."), (int)bus.type, (int)bus.count, digitalCount); + } + } else { + DEBUG_PRINTF_P(PSTR("Out of LED memory! Bus %d (%d) #%u not created."), (int)bus.type, (int)bus.count, digitalCount); + } + } } - busConfigs.clear(); - busConfigs.shrink_to_fit(); - - // ensure at least one bus exists as fallback to prevent UI issues and provide basic LED output + + // If no buses were successfully created, create a minimal fallback using first user config or defaults if (BusManager::getNumBusses() == 0) { - DEBUG_PRINTLN(F("No buses created due to memory limits! Creating fallback bus with default LED count.")); - uint8_t defPin[1] = {DEFAULT_LED_PIN}; - BusConfig defCfg = BusConfig(DEFAULT_LED_TYPE, defPin, 0, DEFAULT_LED_COUNT, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY, 0); - BusManager::add(defCfg); + DEBUG_PRINTLN(F("No buses created due to memory limits! Creating fallback bus with user config or defaults.")); + + BusConfig fallbackCfg; + if (!busConfigs.empty()) { + // Use first user configuration but with minimal LED count + fallbackCfg = busConfigs[0]; + fallbackCfg.count = 1; // Start with minimal count + fallbackCfg.start = 0; // Ensure it starts at 0 + + // Try to fit as many LEDs as possible within memory constraints + for (uint16_t testCount = 1; testCount <= DEFAULT_LED_COUNT; testCount++) { + fallbackCfg.count = testCount; + unsigned testMem = fallbackCfg.memUsage(Bus::isDigital(fallbackCfg.type) && !Bus::is2Pin(fallbackCfg.type) ? 0 : 0); + if (testMem > MAX_LED_MEMORY) { + fallbackCfg.count = (testCount > 1) ? testCount - 1 : 1; + break; + } + } + + DEBUG_PRINTF_P(PSTR("Using user config: type=%d, pin=%d, count=%d\n"), + (int)fallbackCfg.type, (int)fallbackCfg.pins[0], (int)fallbackCfg.count); + } else { + // Use complete defaults + uint8_t defPin[1] = {DEFAULT_LED_PIN}; + fallbackCfg = BusConfig(DEFAULT_LED_TYPE, defPin, 0, DEFAULT_LED_COUNT, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY, 0); + } + + BusManager::add(fallbackCfg); } + + busConfigs.clear(); + busConfigs.shrink_to_fit(); _length = 0; for (size_t i=0; i 0 ? null:"none"; From 1b19c35625f3112b3285ef686a1b07896f158b09 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Sep 2025 05:45:21 +0000 Subject: [PATCH 4/6] Add comprehensive UI error handling for empty segment lists Co-authored-by: DedeHai <6280424+DedeHai@users.noreply.github.com> --- wled00/data/index.js | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/wled00/data/index.js b/wled00/data/index.js index 17a7e09e9c..61ac4ed00c 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -923,12 +923,24 @@ function populateSegments(s) gId(`segr${i}`).classList.add("hide"); } if (segCount < 2) { - gId(`segd${lSeg}`).classList.add("hide"); // hide delete if only one segment - if (parseInt(gId("seg0bri").value)==255) gId(`segp0`).classList.add("hide"); + // Add safety check for segment elements to prevent UI crashes + const segdElement = gId(`segd${lSeg}`); + if (segdElement) segdElement.classList.add("hide"); // hide delete if only one segment + const seg0briElement = gId("seg0bri"); + const segp0Element = gId(`segp0`); + if (seg0briElement && segp0Element && parseInt(seg0briElement.value)==255) segp0Element.classList.add("hide"); // hide segment controls if there is only one segment in simplified UI if (simplifiedUI) gId("segcont").classList.add("hide"); } - if (!isM && !noNewSegs && (cfg.comp.seglen?parseInt(gId(`seg${lSeg}s`).value):0)+parseInt(gId(`seg${lSeg}e`).value) 1) ? "block":"none"; // rsbtn parent if (Array.isArray(li.maps) && li.maps.length>1) { @@ -2264,7 +2276,9 @@ function rptSeg(s) var rev = gId(`seg${s}rev`).checked; var mi = gId(`seg${s}mi`).checked; var sel = gId(`seg${s}sel`).checked; - var pwr = gId(`seg${s}pwr`).classList.contains('act'); + // Add safety check for segment power element to prevent UI crashes + const segPwrElement = gId(`seg${s}pwr`); + var pwr = segPwrElement ? segPwrElement.classList.contains('act') : false; var obj = {"seg": {"id": s, "n": name, "start": start, "stop": (cfg.comp.seglen?start:0)+stop, "rev": rev, "mi": mi, "on": pwr, "bri": parseInt(gId(`seg${s}bri`).value), "sel": sel}}; if (gId(`seg${s}grp`)) { var grp = parseInt(gId(`seg${s}grp`).value); @@ -2391,7 +2405,13 @@ function setGrp(s, g) function setSegPwr(s) { - var pwr = gId(`seg${s}pwr`).classList.contains('act'); + // Add safety check for segment power element to prevent UI crashes + const segPwrElement = gId(`seg${s}pwr`); + if (!segPwrElement) { + console.warn('Segment power element not found, skipping power toggle'); + return; + } + var pwr = segPwrElement.classList.contains('act'); var obj = {"seg": {"id": s, "on": !pwr}}; requestJson(obj); } From f75b13a29ac2b9e92708feaaef3239a22a4aa11e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Sep 2025 06:15:20 +0000 Subject: [PATCH 5/6] Simplify bus creation logic as requested - replace complex binary search with simple default count fallback Co-authored-by: DedeHai <6280424+DedeHai@users.noreply.github.com> --- wled00/FX_fcn.cpp | 90 +++++------------------------------------------ 1 file changed, 9 insertions(+), 81 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 78f36651c9..45164a9915 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1175,94 +1175,22 @@ void WS2812FX::finalizeInit() { digitalCount = 0; #endif - // create buses/outputs with memory-aware truncation + // create buses/outputs unsigned mem = 0; - std::vector truncatedConfigs; - for (auto bus : busConfigs) { // Calculate what this bus would use with its current configuration unsigned busMemUsage = bus.memUsage(Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type) ? digitalCount : 0); - if (mem + busMemUsage <= MAX_LED_MEMORY) { - // Bus fits as-is, add it - if (BusManager::add(bus) != -1) { - mem += busMemUsage; - if (Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type)) digitalCount++; - truncatedConfigs.push_back(bus); - } else break; - } else { - // Bus doesn't fit, try to truncate LED count to fit available memory - unsigned remainingMem = MAX_LED_MEMORY - mem; - if (remainingMem > 0) { - // Binary search to find maximum LED count that fits in remaining memory - uint16_t maxCount = bus.count; - uint16_t minCount = 1; - uint16_t bestCount = 0; - - while (minCount <= maxCount) { - uint16_t testCount = minCount + (maxCount - minCount) / 2; - BusConfig testBus = bus; - testBus.count = testCount; - - unsigned testMem = testBus.memUsage(Bus::isDigital(testBus.type) && !Bus::is2Pin(testBus.type) ? digitalCount : 0); - - if (testMem <= remainingMem) { - bestCount = testCount; - minCount = testCount + 1; - } else { - maxCount = testCount - 1; - } - } - - if (bestCount > 0) { - // Found a truncated configuration that fits - bus.count = bestCount; - DEBUG_PRINTF_P(PSTR("Truncated bus %d from %u to %u LEDs to fit memory constraints.\n"), (int)bus.type, (int)maxCount, (int)bestCount); - - if (BusManager::add(bus) != -1) { - mem += bus.memUsage(Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type) ? digitalCount : 0); - if (Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type)) digitalCount++; - truncatedConfigs.push_back(bus); - } - } else { - DEBUG_PRINTF_P(PSTR("Out of LED memory! Bus %d (%d) #%u not created - no room for even 1 LED."), (int)bus.type, (int)bus.count, digitalCount); - } - } else { - DEBUG_PRINTF_P(PSTR("Out of LED memory! Bus %d (%d) #%u not created."), (int)bus.type, (int)bus.count, digitalCount); - } - } - } - - // If no buses were successfully created, create a minimal fallback using first user config or defaults - if (BusManager::getNumBusses() == 0) { - DEBUG_PRINTLN(F("No buses created due to memory limits! Creating fallback bus with user config or defaults.")); - - BusConfig fallbackCfg; - if (!busConfigs.empty()) { - // Use first user configuration but with minimal LED count - fallbackCfg = busConfigs[0]; - fallbackCfg.count = 1; // Start with minimal count - fallbackCfg.start = 0; // Ensure it starts at 0 - - // Try to fit as many LEDs as possible within memory constraints - for (uint16_t testCount = 1; testCount <= DEFAULT_LED_COUNT; testCount++) { - fallbackCfg.count = testCount; - unsigned testMem = fallbackCfg.memUsage(Bus::isDigital(fallbackCfg.type) && !Bus::is2Pin(fallbackCfg.type) ? 0 : 0); - if (testMem > MAX_LED_MEMORY) { - fallbackCfg.count = (testCount > 1) ? testCount - 1 : 1; - break; - } - } - - DEBUG_PRINTF_P(PSTR("Using user config: type=%d, pin=%d, count=%d\n"), - (int)fallbackCfg.type, (int)fallbackCfg.pins[0], (int)fallbackCfg.count); - } else { - // Use complete defaults - uint8_t defPin[1] = {DEFAULT_LED_PIN}; - fallbackCfg = BusConfig(DEFAULT_LED_TYPE, defPin, 0, DEFAULT_LED_COUNT, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY, 0); + // If memory exceeds limit, set count to default length and add anyway + if (mem + busMemUsage > MAX_LED_MEMORY) { + bus.count = DEFAULT_LED_COUNT; + DEBUG_PRINTF_P(PSTR("Bus %d memory usage exceeds limit, setting count to default %d\n"), (int)bus.type, DEFAULT_LED_COUNT); } - BusManager::add(fallbackCfg); + if (BusManager::add(bus) != -1) { + mem += bus.memUsage(Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type) ? digitalCount : 0); + if (Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type)) digitalCount++; + } else break; } busConfigs.clear(); From 859053827278d830b3702e0ac4eb136d7ea54cea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Sep 2025 06:26:11 +0000 Subject: [PATCH 6/6] Use min() to prevent increasing bus count and maintain user configuration Co-authored-by: DedeHai <6280424+DedeHai@users.noreply.github.com> --- wled00/FX_fcn.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 45164a9915..91316a84d0 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1181,10 +1181,10 @@ void WS2812FX::finalizeInit() { // Calculate what this bus would use with its current configuration unsigned busMemUsage = bus.memUsage(Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type) ? digitalCount : 0); - // If memory exceeds limit, set count to default length and add anyway + // If memory exceeds limit, set count to minimum of current count and default length if (mem + busMemUsage > MAX_LED_MEMORY) { - bus.count = DEFAULT_LED_COUNT; - DEBUG_PRINTF_P(PSTR("Bus %d memory usage exceeds limit, setting count to default %d\n"), (int)bus.type, DEFAULT_LED_COUNT); + bus.count = min(bus.count, DEFAULT_LED_COUNT); + DEBUG_PRINTF_P(PSTR("Bus %d memory usage exceeds limit, setting count to %d\n"), (int)bus.type, bus.count); } if (BusManager::add(bus) != -1) {