Repository navigation
Hardware Setup Length can't be saved after 659 #4614
Description
Activity
- addedcannot reproduceDevelopers are not able reproduce. Might be fixed already, or report is missing important detailsDevelopers are not able reproduce. Might be fixed already, or report is missing important detailsand removed
on Mar 26, 2025 works just fine. maybe your config is broken.
Mine behaves the same. What part of the config should I look in to?
Also, this happened in the latest version upgrade.
Mine behaves the same. What part of the config should I look in to?
Also, this happened in the latest version upgrade.
Can you send a backup of your config prior to setting the larger value? Are you also using an ESP8266? I'll see if I can replicate it locally. Offhand, "just doesn't save" usually means "the software couldn't apply the configuration" -- unfortunately we don't yet have a way to pass a "why not" message back.
Sorry, no, I don't have a backup (which is annoying). I just upgraded through home assistant and never thought of that part.
I have a 900 LED strip that used to work. And I'm using and esp8266. Specifically this one: https://www.amazon.se/GLEDOPTO-LED-ljusremsa-APP-kontroll-belysningsl%C3%A4gen-g%C3%B6r-det-sj%C3%A4lv/dp/B0CB5C7GS6?pd_rd_w=fE94b&content-id=amzn1.sym.762f1299-afc8-404d-854b-e91ea0589751&pf_rd_p=762f1299-afc8-404d-854b-e91ea0589751&pf_rd_r=CV188HBVP4V9BX9KG8SQ&pd_rd_wg=FjJko&pd_rd_r=96495cb0-aa4f-438a-b8f4-e6c1d0780d0b&psc=1&ref_=pd_bap_m_grid_rp_0_0_sa with three of these in a chain: https://www.amazon.se/WS2812B-LED-remsa-Anpassningsbar-DIY-design-Heminredning/dp/B0BTVB7WRY?pd_rd_w=fE94b&content-id=amzn1.sym.762f1299-afc8-404d-854b-e91ea0589751&pf_rd_p=762f1299-afc8-404d-854b-e91ea0589751&pf_rd_r=CV188HBVP4V9BX9KG8SQ&pd_rd_wg=FjJko&pd_rd_r=96495cb0-aa4f-438a-b8f4-e6c1d0780d0b&pd_rd_i=B0BTVB7WRY&psc=1&ref_=pd_bap_m_grid_dv_rp_0_1_ec_ppx_yo2_mob_b_ts_rp_3_i. All powered by a 10A psu.
When I tested it back when this was raised I could not reproduce it, but I can confirm I saw this issue in 0.16.
Sorry, no, I don't have a backup (which is annoying).
Oh, I don't need a config from the old version, just whatever config you've got now. "Backup config" is the button in the settings screen to download it.
When I tested it back when this was raised I could not reproduce it, but I can confirm I saw this issue in 0.16.
Best guess offhand is that there's an OOM/fragmentation issue with ESP8266es and large setups. If that turns out to be the case, we should land #4791 first before getting too deep in to this one.
I don't think that PR fixes this IIRC I have seen it while running tests on that but I could be mistaken.
I agree, If this is an allocation problem, I don't think that PR will fix it. It's more that if we have to get in to changing when and where allocations are done to fix this case, there's a good chance it'd lead to a bunch of merge conflicts with #4791, so I'd prefer to merge that PR first before starting on any other changes to allocation planning.
I just wanted to chime in as after an update my "light bulb" I made stopped working. Found the same thing. It is made of 3 of the 256 matrix panels. Can only set a maximum of 659 when it would be 768. Was working perfectly before the update. I think Home Assistant initially did the update. Tried flashing the 0.15.1 firmware again and still have the same issue. I am using a ESP8266 as a controller.
10 remaining items
as for 0.16:
- the web UI and polybus also differ, with the new buffers, both are wrong.
- raising
MAX_LED_MEMORYfor ESP8266 is not an option - for WS281x strips, each LED uses 11 bytes, with a 4k limit that means 370 LEDs max, so ESP8266 becomes very limited in use
It's possible to eliminate the
_pixelsarray by inverting the loop structure inshow(). Instead of:for (segment) { for(pixel_in_segment) { blend_to(_pixels, pixel_in_segment)); } }; for(pixel in _pixels) { idx = map_pixel(pixel); for(bus) { write_to_bus(bus, idx, pixel); } }we could try:
for (bus) { for(pixel_in_bus) { idx = inverse_map(pixel_in_bus); color = black; for(segment) { if (idx in segment) { color = blend(color, segment_color_at(idx)); } write_to_bus(bus, pixel_in_bus, color); } }It would have the advantage of reduced memory, only blending pixels that are drawn (good in sparse setups), at the cost of potentially re-blending pixels if buses overlap.
I'm not sure we want to push back 0.16 to try that, though. I'd be happy to ship 0.16 as is (limitations and all), and maybe try something like that for 0.17.
It's possible to eliminate the
_pixelsarray by inverting the loop structure inshow().possible, but in current implementation, very computationally inefficient, if you have 3 layers of segments, for each pixel a whole lot of code is executed - 3 times. The blending code is not the most efficient as it is but its fast enough not to matter too much.
The global _pixels[] buffer is a slight issue on ESP8266 only, the segment buffers are what is eating up memory - one for each layer, another one during blending. IMHO its not worth spending efforts, it just does not have enough RAM for the new blending code. The only way to mitigate it somewhat would be to disable overlapping segments, disable blending and render the segment buffers directly to the strip.possible, but in current implementation, very computationally inefficient, if you have 3 layers of segments, for each pixel a whole lot of code is executed - 3 times. The blending code is not the most efficient as it is but its fast enough not to matter too much.
That code is already there, and is already executed three times, no matter how you organize it. Ultimately I think the performance would be pretty darn close, if not faster in some cases. With an inverted loop structure, the only computational difference is that you perform the "is this global address affected by this segment" test many times, instead of the "is this global address located on this bus" many times. The inverted structure will also do quite a lot fewer memory reads/writes as it blends each pixel completely before moving on to the next - it doesn't have to save the intermediate results for all pixels in between blending each segment.
The global _pixels[] buffer is a slight issue on ESP8266 only, the segment buffers are what is eating up memory - one for each layer, another one during blending. IMHO its not worth spending efforts, it just does not have enough RAM for the new blending code. The only way to mitigate it somewhat would be to disable overlapping segments, disable blending and render the segment buffers directly to the strip.
Ultimately I class that as a 'you get what the chip can give you'. I agree that it won't be possible to support 1k LEDs with multiple segments overlapping. I do think we can continue to manage 1k LEDs with no overlapping, or 500 LEDs with 1 buffer overlapping, and so forth, if we can get rid of the extra buffer during blending.
Of course, for smaller systems it's no problem at all, even as is - my 200-pixel prop has absolutely no trouble with 0.16 blending three FX.
It would be nice if we had some statistics on the usage; maybe we should spend more time on @netmindz's reporting usermod. We certainly hear a lot from users pushing the limits of this chip's capability, because they're the ones most likely to run in to issues. That said, I suspect the overwhelming majority of installations are small (<256LEDs), and fit comfortably even in the available RAM. We just don't hear much from them because everything just works. :)
- added a commit that references this issue
on Aug 29, 2025 That code is already there, and is already executed three times, no matter how you organize it.
than I misunderstand some of the logic (current and proposed). The greatest inefficiency in current blending are, as you state, multiple unwrapping and wrapping of colors. What is even worse: each sub-color calls a blend function and since its an array of function pointers, each call is out of context, so no inlining, no efficient pipelining. Making the blend function a switch-case function would make it way faster (no wrap/unwrap of colors, no function call overheads), I quickly compared that using a snippet with godbolt to check if the compiler can optimize the function-pointer array - it can not.
If we don't need the global buffer for speed then there is no reason to have it, it is only used during blending.RE: ESP8266
200 Pixels still works fine, 400 is already pushing it, 600 definitely brings it to its knees. When I run my allocation tests, I set it up with several segments and at least one segment with a lot of effect-data, like a PS effect or "Dissolve". Then randomly apply those presets to shuffle and fragment the heap - which is quite easy to do and once fragmented badly not much will work anymore. I have a defrag function that deals with fragmented heap, but its messy and can temporarily make things worse. I can polish that up and do a draft PR if you want to check it out.I think there are quite a few setups out there with more than 500 LEDs running on ESP8266, given that
a) its the cheapest option
b) it worked perfectly fine up to this pointIMHO if we fix this issue up in 15.2 and make sure 0.16 will not erase the bus config so users can downgrade easily its not too big of an issue but since it will be viewed as a regression I wonder if the 0.16 release will trigger a wave of complaints. And: which will be worse: allowing for only 300 LEDs but stable or allowing 600+ LEDs but not so stable. My current tests with #4791 are promising that it at least won't become unresponsive.
- addedconfirmedThe bug is reproducable and confirmedThe bug is reproducable and confirmedand removedcannot reproduceDevelopers are not able reproduce. Might be fixed already, or report is missing important detailsDevelopers are not able reproduce. Might be fixed already, or report is missing important details
on Aug 29, 2025 That code is already there, and is already executed three times, no matter how you organize it.
than I misunderstand some of the logic (current and proposed). The greatest inefficiency in current blending are, as you state, multiple unwrapping and wrapping of colors. What is even worse: each sub-color calls a blend function and since its an array of function pointers, each call is out of context, so no inlining, no efficient pipelining. Making the blend function a switch-case function would make it way faster (no wrap/unwrap of colors, no function call overheads), I quickly compared that using a snippet with godbolt to check if the compiler can optimize the function-pointer array - it can not. If we don't need the global buffer for speed then there is no reason to have it, it is only used during blending.
Lots to hack on, for sure. This is the right way to do it: first get something working, then iteratively refine it. FWIW, the penalty for dynamic dispatch isn't nearly as bad on these microcontrollers as it is on desktop CPUs - the pipelines aren't very deep. (Same goes for random RAM accesses -- to the microcontroller, everything in internal memory is like the equivalent of the L2 cache on a desktop CPU).
RE: ESP8266 200 Pixels still works fine, 400 is already pushing it, 600 definitely brings it to its knees. When I run my allocation tests, I set it up with several segments and at least one segment with a lot of effect-data, like a PS effect or "Dissolve". Then randomly apply those presets to shuffle and fragment the heap - which is quite easy to do and once fragmented badly not much will work anymore. I have a defrag function that deals with fragmented heap, but its messy and can temporarily make things worse. I can polish that up and do a draft PR if you want to check it out.
We should probably also run an allocator trace and track down those longer-lived smaller allocations that are resulting in lasting fragmentation as well. If we know what they are, we might be able to find solutions to get them out of the way. (Offhand I really think
Segment::nameshould be moved to some class with the small-string optimization, so it wouldn't even need to touch the heap most of the time...)IMHO if we fix this issue up in 15.2 and make sure 0.16 will not erase the bus config so users can downgrade easily its not too big of an issue but since it will be viewed as a regression I wonder if the 0.16 release will trigger a wave of complaints. And: which will be worse: allowing for only 300 LEDs but stable or allowing 600+ LEDs but not so stable.
Back to #4882 again! Funny how these things pop up all over at the same time. Truthfully, I would consider a significantly lowered functional LED count limit a regression. Still, since I believe we do ultimately have a path forward in the longer term, I concur that it'd be OK to ship it as a known issue, especially if we guarantee it's safe to revert.
Lots to hack on, for sure. This is the right way to do it: first get something working, then iteratively refine it. FWIW, the penalty for dynamic dispatch isn't nearly as bad on these microcontrollers as it is on desktop CPUs - the pipelines aren't very deep. (Same goes for random RAM accesses -- to the microcontroller, everything in internal memory is like the equivalent of the L2 cache on a desktop CPU).
I took another look at blending code and I see your point, there is only very few calculations done per segment, most is done per segment pixel. When inverting the logic, it will add some overhead (using a lookup table defies the purpose) but in general I agree it would not be much slower, there could be some pitfalls due to grouping / spacing / mirroring.
RE pipelining: true its fast on MCUs compared to CPUs but current blending implementation does a function call for every color channel, so even if its just a handful of clock cycles, it adds up very quickly.We should probably also run an allocator trace and track down those longer-lived smaller allocations that are resulting in lasting fragmentation as well. If we know what they are, we might be able to find solutions to get them out of the way. (Offhand I really think
Segment::nameshould be moved to some class with the small-string optimization, so it wouldn't even need to touch the heap most of the time...)I have a pretty good idea what the culprits are: vectors. I wrote a function to "print" the heap as a map to serial and marks things that belong to a
Segmentwith letters. Usually what divides heap is as you suspect the segment names and the segments themselves. There are occasionally other things that I can't pinpoint, but in general usingvectorsfor persistent things is bad for heap fragmentation unless they usereserveatsetup()time, but that is not a long lasting solution as vectors can be purged and re-allocated seepurgeSegments()or it may re-allocate if the reserve is not large enough. Is there a way to enforce a re-allocation of a vector so it is moved to a new heap region? could do that in the proposed defrag function so it gets moved out of the way before defragging. Another way would be to write our own version where we could use a memory pool. Any other ideas? Maybe move this discussion to a new issue :)Back to #4882 again! Funny how these things pop up all over at the same time. Truthfully, I would consider a significantly lowered functional LED count limit a regression. Still, since I believe we do ultimately have a path forward in the longer term, I concur that it'd be OK to ship it as a known issue, especially if we guarantee it's safe to revert.
I would also consider it a regression but to get the 1000+ LED limit back there can not be any segment buffers, meaning
- no particle system
- no blending
- render directly to NPB as it was in 0.15
- new ABL will not work if there are overlapping segments
- ...?
basically it requires a seperate rendering path. I would rather call 0.15 the end of the line for ESP8266 with 500+ pixels than maintaining the complexity of two rendering paths.
Is there a way to enforce a re-allocation of a vector so it is moved to a new heap region?
There's no explicit API for that, though you could potentially do something like:
template<typename vec_type> void vec_realloc(vec_type& vec) { auto v2 = vec_type {}; v2.reserve(vec.size()); for(auto& element: vec) { v2.push_back(std::move(element)); } vec = std::move(v2); }
At the limit, though, we should probably consider alternatives to
std::vectorentirely, particularly for_segments-- it's not clear to me how important a continugous memory layout of Segment objects or O(1) indexing is for our application. We could also arrange a pool allocator for the Segment objects themselves so they could live in some space allocated once at startup.I would also consider it a regression but to get the 1000+ LED limit back there can not be any segment buffers, meaning
I'd like to reserve judgement on that -- I think we have a number of irons in the fire that will help: fragmentation management, web server improvements, and so forth. I do agree that we should not be maintaining alternate rendering paths, but I'm cautiously optimistic that we'll be able to get things to work up to about that size.
Reacted by Damian Schneider- added a commit that references this issue
on Sep 14, 2025 This is a pretty major regression for WLED if support is suddenly dropped for esp8266 with more than 660 LEDs. It sounds like the reason is mostly to support 2D configurations but I would argue they're a minority of installs so that shouldn't be prioritized.
@kbickar in 0.15 this limit exists due to a bug which will be fixed in 0.15.2 release.
in 0.16 this limit is real. I managed to get it up to 720 LEDs but thats as far as we dare to push it currently. The reason is not 2D support but a new approach to how things are rendered which is much more versatile and a lot faster. To remove that limit would mean to disable most new features in 0.16 which is a bit pointless as you can just stay on 0.15.2.
just FYI, we are currently discussing it here: #4939
What happened?
When you want to save a number above the 659 in the LED & hardware setup it resets again to 30.
Tried to set it to 720


After save, its again 30
To Reproduce Bug
Go to LED & Hardware setup
In Hardwaresetup LED outputs choose a length greater then 659.
After save, its set at 30 again
Expected Behavior
Go to LED & Hardware setup
In Hardwaresetup LED outputs choose a length greater then 659.
After save, its saved the number
Install Method
Binary from WLED.me
What version of WLED?
Nightly Release 20250326
Which microcontroller/board are you seeing the problem on?
ESP8266
Relevant log/trace output
Anything else?
No response
Code of Conduct