Skip to content

Remove FRAMETIME return value from all FX#5314

Open
DedeHai wants to merge 13 commits intowled:mainfrom
DedeHai:removeFRAMETIME
Open

Remove FRAMETIME return value from all FX#5314
DedeHai wants to merge 13 commits intowled:mainfrom
DedeHai:removeFRAMETIME

Conversation

@DedeHai
Copy link
Collaborator

@DedeHai DedeHai commented Jan 24, 2026

since for smooth transitions the FPS was forced to FRAMETIME anyway and only a handful of FX actually used it there is no reason to keep this logic. With persistent segment buffers a frame can also be sent out multiple times, even if the FX does not render it (which it should for smooth transitions but it is not a requirement anymore)

  • Removed return value from all FX
  • added macro FX_FALLBACK_STATIC { mode_static(); return; }
  • Updated user_fx readme removing the reference to return FRAMETIME; and add FX_FALLBACK_STATIC references
  • fixes speedup by modifying FX to always render but not always update
    • updated mode_chase_flash()
    • updated mode_chase_flash_random()
    • updated mode_colortwinkle(), it still runs at fixed frame of 42FPS
    • udpated candle(), also still runs at fixed frame rate
    • had to almsot fully re-write ICU FX to a state-based version. I also added a slight modification: it now blinks while paused and not simply at the start of a pause (@Aircoookie please veto if you do not like it)

Additional changes:

  • bugfix in PS to prevent potential crashes: PS could still run on uninitialized SEGMENT.data in some edge cases like calling a 2D PS preset in a 1D setup.

Summary by CodeRabbit

  • Bug Fixes

    • Better error reporting and memory cleanup for unsupported particle/2D setups.
    • Removed inconsistent frame-return timing to prevent timing glitches and leaks.
    • Added a reliable static fallback for failure scenarios.
  • Refactor

    • Standardized effects to a fixed, cadence-driven, non-blocking update model.
    • Consolidated many effects to side-effect-driven handlers (no per-effect timing returns).
    • Simplified particle update flow by removing conditional render-only branches.
  • Documentation

    • Clarified cadence/frame-timing guidance and added fixed-rate examples.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 24, 2026

Walkthrough

Core FX mode functions and WS2812FX interfaces were changed from returning uint16_t (FRAMETIME) to void, replacing per-mode return-based timing with fixed cadence handling. ParticleSystem2D::updateFire lost its renderonly parameter and always updates; 2D/1D init failures now set error flags and deallocate data. README adds FX_FALLBACK_STATIC.

Changes

Cohort / File(s) Summary
Core FX typedefs & registration
wled00/FX.h
Changed mode function pointer types and ModeData to accept void (*fcn)(void) instead of uint16_t(*)(void); removed Segment.next_time initialization.
Scheduler / service loop
wled00/FX_fcn.cpp
Removed use of seg.next_time and per-mode frameDelay; service loop now advances frames with _lastServiceShow/_frametime and invokes mode functions directly.
Particle system API & error handling
wled00/FXparticleSystem.cpp, wled00/FXparticleSystem.h
ParticleSystem2D::updateFire() signature dropped renderonly; always updates. 2D/1D init failure paths now set ERR_NOT_IMPL, call SEGMENT.deallocateData(), and return false.
User FX modules & effects
usermods/..., usermods/user_fx/user_fx.cpp, usermods/user_fx/README.md, usermods/Temperature/Temperature.cpp, usermods/TetrisAI_v2/TetrisAI_v2.cpp, usermods/pov_display/pov_display.cpp, usermods/usermod_v2_animartrix/..., usermods/pixels_dice_tray/led_effects.h
Many mode_* functions converted from uint16_tvoid; FRAMETIME return statements removed or replaced with plain return; added FX_FALLBACK_STATIC macro and used it in setup/allocation-failure paths; sinelon_base changed to void with updated wrappers.
Docs / README
usermods/user_fx/README.md
Added FX_FALLBACK_STATIC macro; updated examples to use cadence-only updates and reflect sinelon_basevoid and wrapper adjustments.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • netmindz
  • willmmiles
  • softhack007
  • Aircoookie
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 72.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: removing FRAMETIME return values from all FX functions throughout the codebase.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

No actionable comments were generated in the recent review. 🎉


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

🤖 Fix all issues with AI agents
In `@usermods/user_fx/README.md`:
- Around line 83-85: The tutorial snippets call mode_static() when a non-2D
setup is detected but do not stop execution, allowing the flow to continue into
2D-specific logic; update the conditional that checks strip.isMatrix and
SEGMENT.is2D() so that after calling mode_static() you immediately return (e.g.,
wrap mode_static() in a block and add return;) and apply the same change to the
other occurrence referenced around the second snippet (the one at 130-132) so
both examples exit early after the fallback.

In `@wled00/FXparticleSystem.h`:
- Line 23: The header currently defines WLED_DEBUG_PS unconditionally which
forces particle-system debug and extra flash into every build; remove the
default `#define` and make WLED_DEBUG_PS opt-in by guarding debug code behind a
build-time macro (e.g., rely on a compile flag such as -DENABLE_WLED_DEBUG_PS or
an existing global debug macro) so that FXparticleSystem.h only enables debug
when that build flag is set; update references to WLED_DEBUG_PS in the file so
they are inside `#ifdef` ENABLE_WLED_DEBUG_PS (or the chosen macro) and document
the new build flag for opt‑in usage.
🧹 Nitpick comments (3)
usermods/user_fx/README.md (1)

253-253: Clarify how to re‑render when skipping state updates.

The new note says to re‑render every frame, but the example still renders only inside the update interval. Consider adding a short hint or snippet showing a “render every call, update conditionally” structure to avoid confusion for readers.

wled00/FXparticleSystem.cpp (2)

1098-1102: Consider nulling PartSys on early exit to avoid dangling pointers.
SEGMENT.deallocateData() invalidates prior PS memory; setting PartSys = nullptr here makes accidental reuse safer.

♻️ Suggested change
   if (!strip.isMatrix) {
     errorFlag = ERR_NOT_IMPL; // TODO: need a better error code if more codes are added
     SEGMENT.deallocateData(); // deallocate any data to make sure data is null (there is no valid PS in data and data can only be checked for null)
+    PartSys = nullptr;
     return false; // only for 2D
   }

1850-1854: Also null PartSys in the 1D early-exit path.
This keeps callers from accidentally using a stale pointer after SEGMENT.deallocateData().

♻️ Suggested change
   if (SEGLEN == 1) {
     errorFlag = ERR_NOT_IMPL; // TODO: need a better error code if more codes are added
     SEGMENT.deallocateData(); // deallocate any data to make sure data is null (there is no valid PS in data and data can only be checked for null)
+    PartSys = nullptr;
     return false; // single pixel not supported
   }

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

Caution

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

⚠️ Outside diff range comments (1)
usermods/user_fx/README.md (1)

302-309: Update Sinelon example to void-return to match FX changes.
The example still declares uint16_t and later shows return FRAMETIME;, but FX_FALLBACK_STATIC expands to return; and the API is now void. Please align the snippet and its narrative to avoid copy-paste compile errors.

✏️ Proposed doc fix
-static uint16_t sinelon_base(bool dual, bool rainbow=false) {
+static void sinelon_base(bool dual, bool rainbow=false) {
   if (SEGLEN <= 1) FX_FALLBACK_STATIC;
   ...
-  return FRAMETIME;
 }
-uint16_t mode_sinelon(void) {
-  return sinelon_base(false);
-}
+void mode_sinelon(void) {
+  sinelon_base(false);
+}
 
-uint16_t mode_sinelon_dual(void) {
-  return sinelon_base(true);
-}
+void mode_sinelon_dual(void) {
+  sinelon_base(true);
+}
 
-uint16_t mode_sinelon_rainbow(void) {
-  return sinelon_base(false, true);
-}
+void mode_sinelon_rainbow(void) {
+  sinelon_base(false, true);
+}

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

🤖 Fix all issues with AI agents
In `@wled00/FXparticleSystem.h`:
- Around line 145-147: The comment for updateFire still mentions the removed
renderonly parameter and should be updated to reflect the new always-update
behavior; modify the comment on the updateFire(const uint8_t intensity)
declaration in FXparticleSystem.h to remove the "if renderonly is set, particles
are not updated" clause and instead state that updateFire always updates
particles (and is used to keep fire behavior consistent across frame skips/
transitions), or simply remove the outdated phrase so the comment matches
current behavior.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (4)
usermods/Temperature/Temperature.cpp (1)

3-3: Fix forward declaration return type (compile error).

The forward declaration still returns uint16_t while the definition is void, which is a conflicting type error.

🛠️ Proposed fix
-static uint16_t mode_temperature();
+static void mode_temperature();
usermods/TetrisAI_v2/TetrisAI_v2.cpp (1)

101-108: Remove return value from void function.

mode_2DTetrisAI() is void, but still returns 350, which is invalid.

🛠️ Proposed fix
   if (!strip.isMatrix || !SEGENV.allocateData(sizeof(tetrisai_data)))
   {
     // not a 2D set-up
     SEGMENT.fill(SEGCOLOR(0));
-    return 350;
+    return;
   }
usermods/pixels_dice_tray/led_effects.h (2)

44-66: Fix return-type mismatch after switching running_copy to void (compile break).

running_copy() is now void, but check_roll() still does return running_copy(...) (Line 112) and the mode wrappers (pulse_roll, check_roll, plus the extern mode_* declarations) still use uint16_t returns. With the global FX return removal, this will fail to compile. Update the usermod effects and externs to void and stop returning their results.

Example shape (outside this hunk, for illustration):

extern void mode_breath();
extern void mode_blends();
extern void mode_glitter();
extern void mode_gravcenter();

static void pulse_roll() {
  auto roll = GetLastRollForSegment();
  if (roll.state != pixels::RollState::ON_FACE) { mode_breath(); return; }
  mode_blends();
  ...
}

static void check_roll() {
  auto roll = GetLastRollForSegment();
  if (roll.state != pixels::RollState::ON_FACE) { running_copy(SEGCOLOR(0), SEGCOLOR(2)); return; }
  if (roll.current_face + 1 >= SEGMENT.custom2) { mode_glitter(); return; }
  mode_gravcenter();
}

68-81: Clamp num_segments and avoid <= to prevent out-of-range index.

When roll.current_face is max, num_segments can equal SEGLEN, and i <= num_segments calls setPixelColor(SEGLEN, ...). Safer to clamp and use <.

✅ Suggested fix
-    uint16_t num_segments = float(roll.current_face + 1) / 20.0 * SEGLEN;
-    for (int i = 0; i <= num_segments; i++) {
+    uint16_t num_segments = (uint32_t)(roll.current_face + 1) * SEGLEN / 20;
+    if (num_segments > SEGLEN) num_segments = SEGLEN;
+    for (int i = 0; i < num_segments; i++) {
       SEGMENT.setPixelColor(i, SEGCOLOR(0));
     }
🤖 Fix all issues with AI agents
In `@usermods/user_fx/README.md`:
- Around line 255-257: Typo: change the misspelled word "fuction" to "function"
in the README paragraph that mentions SEGMENT.call and FRAMETIME; update the
sentence "Also `SEGMENT.call` is still incremented on each fuction call." to
"Also `SEGMENT.call` is still incremented on each function call." to correct the
spelling while keeping the wording and references to FRAMETIME, refresh_ms and
SEGMENT.call intact.

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)
usermods/pixels_dice_tray/led_effects.h (1)

73-79: Prevent out-of-bounds write when the roll fills the segment.
If num_segments == SEGLEN, i <= num_segments writes index SEGLEN (invalid).

🐛 Proposed fix
-    uint16_t num_segments = float(roll.current_face + 1) / 20.0 * SEGLEN;
-    for (int i = 0; i <= num_segments; i++) {
+    uint16_t num_segments = float(roll.current_face + 1) / 20.0 * SEGLEN;
+    if (num_segments > SEGLEN) num_segments = SEGLEN;
+    for (int i = 0; i < num_segments; i++) {
       SEGMENT.setPixelColor(i, SEGCOLOR(0));
     }
🤖 Fix all issues with AI agents
In `@usermods/pixels_dice_tray/led_effects.h`:
- Around line 109-114: In function check_roll() fix the compile error by adding
the missing semicolon after the bare return inside the if branch; locate
check_roll and the offending return within the block that calls
running_copy(SEGCOLOR(0), SEGCOLOR(2)) and terminate that statement with a
semicolon so the function compiles correctly.

In `@usermods/user_fx/README.md`:
- Line 309: The function signature for sinelon_base is missing the explicit
return type; update the declaration "static sinelon_base(bool dual, bool
rainbow=false)" to include the void return type (i.e., change it to "static void
sinelon_base(...)") so it matches the PR's move from uint16_t to void and the
rest of the API; modify the declaration and any matching forward declarations or
definitions of sinelon_base to use the void return type.
- Around line 398-410: The three wrapper functions mode_sinelon,
mode_sinelon_dual, and mode_sinelon_rainbow are missing return type specifiers;
update their declarations to include the proper WLED FX convention (e.g., static
void) before each function name so they match sinelon_base's usage and signature
expectations; ensure you change mode_sinelon(void), mode_sinelon_dual(void), and
mode_sinelon_rainbow(void) to static void mode_sinelon(void), static void
mode_sinelon_dual(void), and static void mode_sinelon_rainbow(void)
respectively.

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

🤖 Fix all issues with AI agents
In `@usermods/user_fx/README.md`:
- Around line 255-262: The snippet currently uses an early return which prevents
rendering and breaks smooth transitions; instead modify the logic around
SEGENV.step/strip.now so you only skip updating effect state when (strip.now -
SEGENV.step) < FRAMETIME_FIXED but do not return — allow the rest of the
function to run to perform rendering and let SEGMENT.call increment. Concretely,
change the early-return pattern to a conditional branch that bypasses
state-mutating code (state update block) when the interval hasn't elapsed, but
still executes rendering/gradient/brightness/segment output code; keep updating
SEGENV.step only when you actually perform the state update so timing remains
correct.

@netmindz
Copy link
Member

I've not tested all the effects, but approve in principle the change

@DedeHai
Copy link
Collaborator Author

DedeHai commented Jan 31, 2026

thanks, its almost exclusively "house-keeping". only the "ICU" FX has fudamental changes.

While checking feasiblity of #5135 a few hours ago I actually found that seg.next_time is no longer needed, it could be made static or since it is used only in strip class, moved there (which I think should be the solution). Or is there a good reason to keep next_time per segment or even in the segment class?

the segmen's internal "next time" could be replaced with "time_offset" then all that is missing is instead of strip.now() use strip.now()+time_offset and #5135 has a base to work with.

@DedeHai
Copy link
Collaborator Author

DedeHai commented Feb 5, 2026

@coderabbitai please explain the use of "direct" in
WS2812FX::setBrightness(uint8_t b, bool direct)
and if it is still useful with the changes from this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 5, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the setBrightness function definition
rg -n -A 20 "void.*setBrightness\s*\(\s*uint8_t.*bool\s+direct" --type=cpp

Length of output: 1283


🏁 Script executed:

#!/bin/bash
# Also search for where setBrightness is called with the direct parameter
rg -n -C 3 "setBrightness\s*\([^)]*,\s*(true|false|direct)" --type=cpp | head -100

Length of output: 3220


🏁 Script executed:

#!/bin/bash
# Find the declaration in header files
rg -n "setBrightness.*direct" --type=h

Length of output: 96


@DedeHai The direct parameter in setBrightness(uint8_t b, bool direct) controls whether the function should automatically trigger an immediate LED update when changing brightness.

How it works

Looking at the implementation in wled00/FX_fcn.cpp:1715-1727:

void WS2812FX::setBrightness(uint8_t b, bool direct) {
  // ... brightness update logic ...
  BusManager::setBrightness(scaledBri(b));
  if (!direct) {
    unsigned long t = millis();
    if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) trigger();
  }
}

When direct = false (default):

  • Checks if the next scheduled frame update is more than 22ms away
  • If so, calls trigger() to immediately apply the brightness change
  • Prevents visible delay in brightness response when effects are running slowly

When direct = true:

  • Skips the automatic trigger check
  • Caller takes responsibility for updating the LEDs
  • Used when the caller will handle the update themselves or wants precise timing control

Usage patterns in the codebase

  • Realtime modes (UDP, DMX): Use direct=true to manage their own frame timing
  • E131: Uses direct=true for direct pixel control, direct=false for brightness-only changes to trigger state updates
  • JSON API: Uses direct=true when stopping transitions to avoid race conditions

Still useful after this PR?

Yes, the direct parameter remains useful after your changes:

  1. Your PR changes how next_time is calculated (fixed FRAMETIME instead of dynamic), but doesn't remove the next_time mechanism itself
  2. The check _segments[0].next_time > t + 22 still makes sense - it detects when the next frame is far away
  3. Realtime modes still need to bypass the automatic trigger to maintain their own timing control
  4. The parameter provides important control over when LED updates happen, which is independent of how effect frame timing works

The fixed frame timing from your PR actually makes the behavior more predictable, but the need to optionally trigger immediate updates when changing brightness remains valid.


🧠 Learnings used
Learnt from: DedeHai
Repo: wled/WLED PR: 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: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2025-12-28T14:06:48.772Z
Learning: In WLED post-commit ee9ac947, the rendering pipeline uses per-segment buffers and per-pixel bus updates. Unmapped (0xFFFF) mapping entries are now skipped in WS2812FX::show() (no “clear to black”), which can leave physical gap LEDs with stale/random colors unless they are explicitly cleared. This is a behavior change from pre-0.16 where a full physical buffer was effectively refreshed each frame.

Learnt from: BobLoeffler68
Repo: wled/WLED PR: 5293
File: usermods/user_fx/user_fx.cpp:167-171
Timestamp: 2026-01-13T03:07:20.560Z
Learning: In WLED effects (usermods/user_fx/user_fx.cpp and similar), when storing future timestamps, assignments like `stored_time = now + delay` are correct and handle uint32_t wraparound properly. Only comparison statements need to use subtraction-based elapsed-time checks (e.g., `(now - stored_time) >= delay`). Do not flag assignment statements as needing fixes for wraparound handling.

Learnt from: BobLoeffler68
Repo: wled/WLED PR: 5109
File: wled00/FX.cpp:3174-3343
Timestamp: 2025-11-27T06:33:11.436Z
Learning: WLED Ants effect (wled00/FX.cpp): The author prefers the current velocity initialization using hw_random16(1000, 5000)/5000.0f, resulting in an effective range of ~3.6–10.0 (with VELOCITY_MIN=2.0, VELOCITY_MAX=10.0), and wants the code kept as-is with comments updated to document this behavior. Avoid suggesting changes to span the full 2.0–10.0 range in future reviews.

Learnt from: DedeHai
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2026-01-13T21:23:35.514Z
Learning: In WLED, the global `paletteBlend` variable (wled.h:603) and the `WS2812FX::paletteBlend` member (FX.h:940) are duplicates without synchronization code. The global is loaded/saved in cfg.cpp and set via UI in set.cpp, but never copied to the strip member. This is the only such case in the codebase; other settings are either strip-only members (autoSegments, correctWB, cctFromRgb, isMatrix) or global-only (gammaCorrectCol/Bri/Val, blendingStyle).

Learnt from: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2025-11-14T13:37:11.994Z
Learning: In WLED code reviews, when code is modified or added, look for "magic numbers" (hardcoded numeric literals) and suggest replacing them with appropriate defined constants when those constants are meaningful in the context of the PR. For example, the hardcoded value 32 should be replaced with WLED_MAX_SEGNAME_LEN when it represents a segment name length limit. This improves code maintainability and reduces the risk of inconsistencies.

Learnt from: freakintoddles2
Repo: wled/WLED PR: 4904
File: wled00/FX.cpp:3915-3930
Timestamp: 2025-09-02T19:42:57.475Z
Learning: WLED wled00/FX.cpp::mode_percent — For time-based options (% of Minute/Hour/Day), behavior is clock-like: at rollover (elapsed==0) intentionally show SEGLEN lit for one tick to represent completion, then move to 1 LED at the next second. This is by design (not a progress bar) and should not be “fixed”.

Learnt from: DedeHai
Repo: wled/WLED PR: 4926
File: wled00/FX.cpp:4727-4730
Timestamp: 2025-11-16T19:40:46.260Z
Learning: WLED AuroraWave (wled00/FX.cpp): wave_start and wave_end intentionally use int16_t; segments longer than 32k LEDs are not supported (bounded by MAX_LEDS), so widening to 32-bit is unnecessary.

Learnt from: BobLoeffler68
Repo: wled/WLED PR: 5293
File: usermods/user_fx/user_fx.cpp:149-151
Timestamp: 2026-01-24T18:51:09.740Z
Learning: In WLED's Spinning Wheel effect (usermods/user_fx/user_fx.cpp), the color scale calculation (SEGENV.aux1) must match the position modulo range. When position uses `pos = (pos_fixed >> 16) % SEGLEN`, the color scale must be `aux1 = (255 << 8) / SEGLEN` to distribute the palette evenly across the full position range. Using vstripLen for color scaling while position uses SEGLEN causes palette colors to cycle too quickly and appear incomplete.

Learnt from: softhack007
Repo: wled/WLED PR: 5251
File: usermods/user_fx/user_fx.cpp:225-227
Timestamp: 2026-01-01T13:18:14.738Z
Learning: In WLED effects, when computing time differences between unsigned long timestamps (e.g., `strip.now`, `lastBumpUpdate`), always cast the subtraction result to `int` before converting to `float` to ensure correct behavior on ESP32-C3 (RISC-V). Use pattern: `float(int(timestamp1 - timestamp2))` instead of `float(timestamp1 - timestamp2)`.

Learnt from: KrX3D
Repo: wled/WLED PR: 4585
File: usermods/seven_segment_display_reloaded_v2/seven_segment_display_reloaded_v2.cpp:430-435
Timestamp: 2025-04-28T20:51:29.773Z
Learning: In WLED, `bri` is a global variable used for brightness control.

Learnt from: blazoncek
Repo: wled/WLED PR: 5140
File: wled00/data/settings_time.htm:66-76
Timestamp: 2025-12-01T07:01:16.949Z
Learning: In WLED PR `#5134`, the fix for macros being initialized with the enable bit set only handles new configurations, not existing ones. If there is a bug in timer/macro handling code (e.g., in settings_time.htm), it must be fixed to work correctly for existing configurations as well.

Learnt from: BobLoeffler68
Repo: wled/WLED PR: 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.

@DedeHai
Copy link
Collaborator Author

DedeHai commented Feb 5, 2026

@coderabbitai regarding the above comment about seg.next_time: since all segments now update at the same pace, should this be made static or is it better to move it into the strip class?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 5, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the declaration of next_time in the segment structure
rg -n "next_time" --type=h -C 3

# Find all usages of next_time in the codebase
echo "=== All next_time usages ==="
rg -n "next_time" --type=cpp -C 2

# Check if there are any segment-specific timing patterns
echo "=== Checking for per-segment timing logic ==="
rg -n "seg\.next_time|SEGMENT\.next_time|segments\[.*\]\.next_time" --type=cpp -C 3

Length of output: 3304


🏁 Script executed:

#!/bin/bash
# Check if segments can be started/stopped independently or have different states
echo "=== Checking segment activation/lifecycle ==="
rg -n "seg\.isActive|SEGMENT\.isActive" --type=cpp -C 2 | head -50

# Check the service loop more carefully for timing logic
echo "=== Service loop context ==="
rg -n "void.*service\(" wled00/FX_fcn.cpp -A 50

# Look for any segment-specific speed or delay modifications
echo "=== Checking for segment speed modifications ==="
rg -n "seg\.speed|SEGMENT\.speed" --type=cpp -C 2 | head -30

Length of output: 8711


@DedeHai, excellent question! After analyzing the codebase, I recommend keeping next_time per-segment rather than making it static or moving it to the strip class. Here's why:

Current usage patterns

From the code analysis:

  • Line FX_fcn.cpp:1285: Each segment is checked individually: if (nowUp > seg.next_time || _triggered...)
  • Line FX_fcn.cpp:1312: All segments set seg.next_time = nowUp + FRAMETIME (same rate)
  • Line FX_fcn.cpp:220: When a segment resets, next_time = 0 (immediate update)
  • Line FX_fcn.cpp:1725: setBrightness checks _segments[0].next_time for trigger decision

Why per-segment timing is still valuable

Even though all segments now update at the same rate (FRAMETIME), they don't necessarily update at the same time:

  1. Independent lifecycle: Segments can be activated/deactivated at any point. When a segment becomes active, its next_time = 0 allows it to start rendering immediately in the next service() call, rather than waiting for a global timer tick.

  2. Staggered starts: If you activate multiple segments at different times, they'll naturally stagger their updates slightly, which can help distribute processing load.

  3. Reset behavior: When a segment is reset (mode change, effect restart), it can update immediately without forcing all other segments to update.

  4. Future flexibility: Your time_offset idea for issue #5135 would work naturally with per-segment next_time. You could implement per-segment timing offsets by adjusting when each segment's next_time is set, allowing synchronized or phased animations across segments.

If you moved to static/strip-level

A single global timer would mean:

  • Newly activated segments must wait for the next global tick (up to FRAMETIME delay)
  • All segments would update in perfect lockstep (might cause processing spikes)
  • Less flexibility for future per-segment timing features
  • The setBrightness check would be simpler (no _segments[0] indexing needed)

Recommendation

Keep it per-segment. The memory cost is minimal (4 bytes × num_segments), and it preserves the ability for segments to have independent lifecycles while still rendering at a uniform rate. This design also leaves the door open for your time_offset enhancement without requiring another refactor.


🧠 Learnings used
Learnt from: DedeHai
Repo: wled/WLED PR: 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: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2025-12-28T14:06:48.772Z
Learning: In WLED post-commit ee9ac947, the rendering pipeline uses per-segment buffers and per-pixel bus updates. Unmapped (0xFFFF) mapping entries are now skipped in WS2812FX::show() (no “clear to black”), which can leave physical gap LEDs with stale/random colors unless they are explicitly cleared. This is a behavior change from pre-0.16 where a full physical buffer was effectively refreshed each frame.

Learnt from: BobLoeffler68
Repo: wled/WLED PR: 5293
File: usermods/user_fx/user_fx.cpp:167-171
Timestamp: 2026-01-13T03:07:20.560Z
Learning: In WLED effects (usermods/user_fx/user_fx.cpp and similar), when storing future timestamps, assignments like `stored_time = now + delay` are correct and handle uint32_t wraparound properly. Only comparison statements need to use subtraction-based elapsed-time checks (e.g., `(now - stored_time) >= delay`). Do not flag assignment statements as needing fixes for wraparound handling.

Learnt from: DedeHai
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2026-01-13T21:23:35.514Z
Learning: In WLED, the global `paletteBlend` variable (wled.h:603) and the `WS2812FX::paletteBlend` member (FX.h:940) are duplicates without synchronization code. The global is loaded/saved in cfg.cpp and set via UI in set.cpp, but never copied to the strip member. This is the only such case in the codebase; other settings are either strip-only members (autoSegments, correctWB, cctFromRgb, isMatrix) or global-only (gammaCorrectCol/Bri/Val, blendingStyle).

Learnt from: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2025-11-14T13:37:11.994Z
Learning: In WLED code reviews, when code is modified or added, look for "magic numbers" (hardcoded numeric literals) and suggest replacing them with appropriate defined constants when those constants are meaningful in the context of the PR. For example, the hardcoded value 32 should be replaced with WLED_MAX_SEGNAME_LEN when it represents a segment name length limit. This improves code maintainability and reduces the risk of inconsistencies.

Learnt from: freakintoddles2
Repo: wled/WLED PR: 4904
File: wled00/FX.cpp:3915-3930
Timestamp: 2025-09-02T19:42:57.475Z
Learning: WLED wled00/FX.cpp::mode_percent — For time-based options (% of Minute/Hour/Day), behavior is clock-like: at rollover (elapsed==0) intentionally show SEGLEN lit for one tick to represent completion, then move to 1 LED at the next second. This is by design (not a progress bar) and should not be “fixed”.

Learnt from: BobLoeffler68
Repo: wled/WLED PR: 5293
File: usermods/user_fx/user_fx.cpp:149-151
Timestamp: 2026-01-24T18:51:09.740Z
Learning: In WLED's Spinning Wheel effect (usermods/user_fx/user_fx.cpp), the color scale calculation (SEGENV.aux1) must match the position modulo range. When position uses `pos = (pos_fixed >> 16) % SEGLEN`, the color scale must be `aux1 = (255 << 8) / SEGLEN` to distribute the palette evenly across the full position range. Using vstripLen for color scaling while position uses SEGLEN causes palette colors to cycle too quickly and appear incomplete.

Learnt from: BobLoeffler68
Repo: wled/WLED PR: 5109
File: wled00/FX.cpp:3174-3343
Timestamp: 2025-11-27T06:33:11.436Z
Learning: WLED Ants effect (wled00/FX.cpp): The author prefers the current velocity initialization using hw_random16(1000, 5000)/5000.0f, resulting in an effective range of ~3.6–10.0 (with VELOCITY_MIN=2.0, VELOCITY_MAX=10.0), and wants the code kept as-is with comments updated to document this behavior. Avoid suggesting changes to span the full 2.0–10.0 range in future reviews.

Learnt from: BobLoeffler68
Repo: wled/WLED PR: 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.

Learnt from: DedeHai
Repo: wled/WLED PR: 4926
File: wled00/FX.cpp:4727-4730
Timestamp: 2025-11-16T19:40:46.260Z
Learning: WLED AuroraWave (wled00/FX.cpp): wave_start and wave_end intentionally use int16_t; segments longer than 32k LEDs are not supported (bounded by MAX_LEDS), so widening to 32-bit is unnecessary.

Learnt from: blazoncek
Repo: wled/WLED PR: 5140
File: wled00/data/settings_time.htm:66-76
Timestamp: 2025-12-01T07:01:16.949Z
Learning: In WLED PR `#5134`, the fix for macros being initialized with the enable bit set only handles new configurations, not existing ones. If there is a bug in timer/macro handling code (e.g., in settings_time.htm), it must be fixed to work correctly for existing configurations as well.

@DedeHai
Copy link
Collaborator Author

DedeHai commented Feb 5, 2026

@softhack007 would there be any downside to moving the seg.next_time into the strip class and have all segments & strips always update in sync? I can not think of a reason not to do that.

@softhack007
Copy link
Member

@DedeHai good question.
As much as I remember, "next_time" was for segments have individual "frame rates" based on FRAMETIME returned by each effect. If all effects now progress at the same rate, next_time could be removed. At least that's what i recall, @blazoncek might know more.

  • Line FX_fcn.cpp:1312: All segments set seg.next_time = nowUp + FRAMETIME (same rate)

nowUp (and SEGMENT.now) is already controlled by the same (global) clock, so these might not be needed or could be moved to strip level.

  • Line FX_fcn.cpp:220: When a segment resets, next_time = 0 (immediate update)

This might be a tricky one. Not sure if next_time = 0 causes special behaviour in some functions. A segment reset should also lead to segment::call == 0.

@blazoncek
Copy link
Contributor

I really have no idea why you keep asking me while at the same time ignoring my comments.
Do as you please and deal with the consequences.

@softhack007
Copy link
Member

Don't worry, we will find the answer ourselves.

@softhack007
Copy link
Member

softhack007 commented Feb 6, 2026

WLED/wled00/FX_fcn.cpp

Lines 1725 to 1730 in 129137d

BusManager::setBrightness(scaledBri(b));
if (!direct) {
unsigned long t = millis();
if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) trigger(); //apply brightness change immediately if no refresh soon
}
}

  • Line FX_fcn.cpp:1725: setBrightness checks _segments[0].next_time for trigger decision

I think the condition on next_time is obsolete now - if all segments have the same update speed, there is no need any more to "apply brightness change immediately if no refresh soon".

As far as my memory gets me, this was added long ago together with smooth transitions, and we had a problem with the "solid" effect that was only updating 3x per second. I'm still wondering why only _segments[0].next_time was checked, but that knowledge may RIP because imho the code is not needed any more.

--> Simplifying the condition to if (t - _lastShow > MIN_SHOW_DELAY) trigger(); should work.

@DedeHai
Copy link
Collaborator Author

DedeHai commented Feb 6, 2026

I think the condition on next_time is obsolete now

I was under the same impression, at least from a "local" viepoint - not thinking of what happens to DMX / streaming / external control has got me in the past.

@softhack007
Copy link
Member

@DedeHai I'll ask the rabbit for an extra "confidence check", just to be sure.

Line FX_fcn.cpp:1725: setBrightness checks _segments[0].next_time for trigger decision

@coderabbitai please identify the commit that added the condition "if (_segments[0].next_time > t + 22", and also check for old issue tickets and PR that might explain the intention behind this check. Considering that MIN_SHOW_DELAY is either 8 or 16, and all segments are now progressing at the same speed, do you see user-noticeable impact (transitions, external control, etc) if we simplify the condition as suggested in #5314 (comment) ? Looking at the history that's available to you, what was the initial purpose of the condition?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 6, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the commit that added the condition with next_time > t + 22
echo "=== Finding commit that added next_time > t + 22 condition ==="
git log -p --all -S "next_time > t + 22" -- "*FX_fcn.cpp" | head -200

echo -e "\n=== Git blame for the setBrightness function ==="
git blame -L 1715,1730 wled00/FX_fcn.cpp

echo -e "\n=== Search for related commits with 'setBrightness' and 'trigger' ==="
git log --all --grep="setBrightness" --grep="trigger" --grep="brightness" --oneline | head -20

echo -e "\n=== Check current implementation ==="
rg -n -A 15 "void.*setBrightness.*direct" wled00/FX_fcn.cpp

Unrecoverable error: Request timed out after 300000ms (requestId=70f6895a-34b1-449e-9b9e-e3ee3a280621)


🏁 Script executed:

#!/bin/bash
# Try a more targeted approach - look at the current code first
echo "=== Current setBrightness implementation ==="
sed -n '1715,1730p' wled00/FX_fcn.cpp

echo -e "\n=== Git blame with simpler output ==="
git blame -L 1720,1727 wled00/FX_fcn.cpp --line-porcelain | grep -E "^(author|summary|^[0-9a-f]{40})" | head -30

echo -e "\n=== Find commits that touched this function recently ==="
git log --oneline --all -- wled00/FX_fcn.cpp | head -30

Length of output: 3526


🏁 Script executed:

#!/bin/bash
# Look at the specific commit that mentions "Always repaint NPB buffer on brightness change"
echo "=== Commit 8ccf3494 details ==="
git show 8ccf3494 --stat

echo -e "\n=== Looking for the actual addition of the 't + 22' condition ==="
git log --all --oneline --grep="trigger" --grep="brightness" --since="2023-01-01" --until="2023-12-31" -- wled00/FX_fcn.cpp

echo -e "\n=== Check commit 8ccf3494 in detail ==="
git show 8ccf3494 -- wled00/FX_fcn.cpp | grep -A 10 -B 10 "next_time\|trigger"

Length of output: 1510


🏁 Script executed:

#!/bin/bash
# Check the earlier commits that led to this
echo "=== Commit 5b9630cf (Repaint NPB buffer on brightness updates) ==="
git show 5b9630cf -- wled00/FX_fcn.cpp | head -80

echo -e "\n=== Search for related issues or PRs around that time ==="
git log --all --oneline --since="2023-06-01" --until="2023-08-01" --grep="brightness\|NPB\|trigger" | head -20

echo -e "\n=== Look at the context around MIN_SHOW_DELAY definition ==="
rg -n "MIN_SHOW_DELAY" --type=h

echo -e "\n=== Check what MIN_SHOW_DELAY value is ==="
rg -n "#define MIN_SHOW_DELAY" 

Length of output: 2241


🏁 Script executed:

#!/bin/bash
# Search for issues/PRs related to brightness and NPB around that time
echo "=== Search GitHub for related issues ==="
gh issue list --repo wled/WLED --search "brightness NPB NeoPixelBus created:2023-06-01..2023-08-01" --state all --limit 20

echo -e "\n=== Search for PRs around that time ==="
gh pr list --repo wled/WLED --search "brightness created:2023-06-01..2023-08-01" --state all --limit 20

echo -e "\n=== Check if there's a specific issue number in commit messages ==="
git log --all --since="2023-07-01" --until="2023-08-01" --oneline | grep -E "#[0-9]+"

Length of output: 1479


🏁 Script executed:

#!/bin/bash
# Check PR `#3280` which seems related to this
echo "=== PR `#3280` details ==="
gh pr view 3280 --repo wled/WLED --json title,body,url,number,state,createdAt

echo -e "\n=== Check issue `#3264` about Auto Brightness Limiter ==="
gh issue view 3264 --repo wled/WLED --json title,body,url,number,state,createdAt,comments --jq '{title, url, body, comments: [.comments[] | {author: .author.login, body: .body}]}'

echo -e "\n=== Look for 'solid' effect timing issues ==="
git log --all --oneline --grep="solid\|static.*effect" --since="2023-01-01" --until="2023-08-01" | head -10

Length of output: 24309


@softhack007, I've traced the history. Here's what I found:

Origin of the condition

The condition was added in commit 8ccf349 on July 19, 2023, with the message "Always repaint NPB buffer on brightness change". This was part of addressing issue #3264 and PR #3280 "Bus-level global buffering".

The original problem

Issue #3264 revealed a critical behavior difference with NeoPixelBusLg: when you change brightness with SetLuminance(), it doesn't affect pixels already in the buffer, unlike the old NeoPixelBrightnessBus which would modify all existing pixels. This caused:

  1. Rhythmic pulsing: The brightness limiter was "one frame behind", causing dim-bright flickering
  2. Slow response to brightness changes: Especially problematic for the "solid" effect which was only updating 3x per second, causing up to ~333ms delay when users adjusted brightness

Why the > t + 22 check?

The condition checks whether the next scheduled frame is far enough away to justify forcing an immediate LED update:

if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) trigger();
  • next_time > t + 22: Is the next frame more than 22ms away?
  • t - _lastShow > MIN_SHOW_DELAY: Has enough time passed since the last update? (8 or 15ms)

The "22" appears chosen to be larger than both possible MIN_SHOW_DELAY values (8 or 15ms) with some margin.

Impact of the proposed simplification

With your suggested simplification to just if (t - _lastShow > MIN_SHOW_DELAY) trigger();:

Pros:

  • ✅ Simpler code, easier to understand
  • ✅ With fixed FRAMETIME, all effects update at same rate, reducing the original problem scope
  • ✅ Brightness changes are infrequent (user-initiated), so extra updates are minimal
  • MIN_SHOW_DELAY still prevents rapid-fire updates

Cons:

  • ⚠️ Would trigger immediate update even if next scheduled frame is just 1-2ms away
  • ⚠️ Could cause unnecessary duplicate frames when brightness changes near a scheduled update

My assessment: The simplification should work fine with minimal user-noticeable impact because:

  1. With your PR, next_time is now always nowUp + FRAMETIME for all segments, making the timing more predictable
  2. Brightness changes are rare enough that occasional redundant frames won't matter
  3. The MIN_SHOW_DELAY check still provides protection against excessive update rates
  4. The worst-case scenario (triggering just before a scheduled frame) would just send the same pixels twice—wasteful but not harmful

The original condition was optimizing for slow-running effects (like 3 FPS solid), which benefited from "smart" immediate updates only when needed. With uniform FRAMETIME, that optimization is less critical.

Recommendation: The simplification is safe to implement. Consider adding a comment explaining that immediate brightness updates are prioritized for responsiveness, relying on MIN_SHOW_DELAY to prevent excessive update rates.


🧠 Learnings used
Learnt from: DedeHai
Repo: wled/WLED PR: 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: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2025-12-28T14:06:48.772Z
Learning: In WLED post-commit ee9ac947, the rendering pipeline uses per-segment buffers and per-pixel bus updates. Unmapped (0xFFFF) mapping entries are now skipped in WS2812FX::show() (no “clear to black”), which can leave physical gap LEDs with stale/random colors unless they are explicitly cleared. This is a behavior change from pre-0.16 where a full physical buffer was effectively refreshed each frame.

Learnt from: BobLoeffler68
Repo: wled/WLED PR: 5293
File: usermods/user_fx/user_fx.cpp:167-171
Timestamp: 2026-01-13T03:07:20.560Z
Learning: In WLED effects (usermods/user_fx/user_fx.cpp and similar), when storing future timestamps, assignments like `stored_time = now + delay` are correct and handle uint32_t wraparound properly. Only comparison statements need to use subtraction-based elapsed-time checks (e.g., `(now - stored_time) >= delay`). Do not flag assignment statements as needing fixes for wraparound handling.

Learnt from: freakintoddles2
Repo: wled/WLED PR: 4904
File: wled00/FX.cpp:3915-3930
Timestamp: 2025-09-02T19:42:57.475Z
Learning: WLED wled00/FX.cpp::mode_percent — For time-based options (% of Minute/Hour/Day), behavior is clock-like: at rollover (elapsed==0) intentionally show SEGLEN lit for one tick to represent completion, then move to 1 LED at the next second. This is by design (not a progress bar) and should not be “fixed”.

Learnt from: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2025-11-14T13:37:11.994Z
Learning: In WLED code reviews, when code is modified or added, look for "magic numbers" (hardcoded numeric literals) and suggest replacing them with appropriate defined constants when those constants are meaningful in the context of the PR. For example, the hardcoded value 32 should be replaced with WLED_MAX_SEGNAME_LEN when it represents a segment name length limit. This improves code maintainability and reduces the risk of inconsistencies.

Learnt from: BobLoeffler68
Repo: wled/WLED PR: 5109
File: wled00/FX.cpp:3174-3343
Timestamp: 2025-11-27T06:33:11.436Z
Learning: WLED Ants effect (wled00/FX.cpp): The author prefers the current velocity initialization using hw_random16(1000, 5000)/5000.0f, resulting in an effective range of ~3.6–10.0 (with VELOCITY_MIN=2.0, VELOCITY_MAX=10.0), and wants the code kept as-is with comments updated to document this behavior. Avoid suggesting changes to span the full 2.0–10.0 range in future reviews.

Learnt from: DedeHai
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2026-01-13T21:23:35.514Z
Learning: In WLED, the global `paletteBlend` variable (wled.h:603) and the `WS2812FX::paletteBlend` member (FX.h:940) are duplicates without synchronization code. The global is loaded/saved in cfg.cpp and set via UI in set.cpp, but never copied to the strip member. This is the only such case in the codebase; other settings are either strip-only members (autoSegments, correctWB, cctFromRgb, isMatrix) or global-only (gammaCorrectCol/Bri/Val, blendingStyle).

Learnt from: blazoncek
Repo: wled/WLED PR: 5140
File: wled00/data/settings_time.htm:66-76
Timestamp: 2025-12-01T07:01:16.949Z
Learning: In WLED PR `#5134`, the fix for macros being initialized with the enable bit set only handles new configurations, not existing ones. If there is a bug in timer/macro handling code (e.g., in settings_time.htm), it must be fixed to work correctly for existing configurations as well.

Learnt from: softhack007
Repo: wled/WLED PR: 5251
File: usermods/user_fx/user_fx.cpp:225-227
Timestamp: 2026-01-01T13:18:14.738Z
Learning: In WLED effects, when computing time differences between unsigned long timestamps (e.g., `strip.now`, `lastBumpUpdate`), always cast the subtraction result to `int` before converting to `float` to ensure correct behavior on ESP32-C3 (RISC-V). Use pattern: `float(int(timestamp1 - timestamp2))` instead of `float(timestamp1 - timestamp2)`.

Learnt from: DedeHai
Repo: wled/WLED PR: 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: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2025-11-14T13:37:30.955Z
Learning: In WLED code reviews, when code is modified or added, look for "magic numbers" (hardcoded numeric literals) and suggest replacing them with defined constants when meaningful constants exist in the codebase. For example, suggest replacing hardcoded "32" with WLED_MAX_SEGNAME_LEN if the context relates to segment name length limits.

Learnt from: DedeHai
Repo: wled/WLED PR: 4997
File: wled00/e131.cpp:33-44
Timestamp: 2025-10-10T18:34:06.550Z
Learning: In WLED's DDP packet handling (ws.cpp and e131.cpp), only prevent out-of-bounds memory access rather than enforcing DDP spec compliance. Don't check the 1440-byte spec limit—accept out-of-spec packets assuming correct encoding. The bounds check `maxDataIndex = c + numLeds * ddpChannelsPerLed; if (maxDataIndex > dataLen) reject` is sufficient and already covers the timecode flag case (when c=4) without needing separate validation.

Learnt from: DedeHai
Repo: wled/WLED PR: 4926
File: wled00/FX.cpp:4727-4730
Timestamp: 2025-11-16T19:40:46.260Z
Learning: WLED AuroraWave (wled00/FX.cpp): wave_start and wave_end intentionally use int16_t; segments longer than 32k LEDs are not supported (bounded by MAX_LEDS), so widening to 32-bit is unnecessary.

Learnt from: BobLoeffler68
Repo: wled/WLED PR: 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.

@softhack007
Copy link
Member

softhack007 commented Feb 6, 2026

@DedeHai now we know everything ;-) I've never seen the rabbit work for longer than 10 minutes for answering a question 😅

  • bus-level global LEDs buffering is history
  • strip.trigger() does not cause a LEDs refresh immediately, it just tells strip.service() to get active in the next run of the main loop.
  • Testcase: check if ABL (auto brightness limiter) causes pulsing LEDs when the strip power estimation is above the threshold. Especially test with low values of "target FPS".

I think its safest to add a small "margin", like if (t - _lastShow > MIN_SHOW_DELAY +2) trigger();

@DedeHai
Copy link
Collaborator Author

DedeHai commented Feb 8, 2026

I checked, no pulsing using ABL. Approved and good to merge from my side.

Copy link
Member

@softhack007 softhack007 left a comment

Choose a reason for hiding this comment

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

looks good to me 👍

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants