Skip to content

Conversation

@drdome
Copy link

@drdome drdome commented Nov 13, 2025

Per @DedeHai comment (#4566) I moved this to a usermod configuration instead of core effect.

Summary

  • adds a new usermods/chase_race folder that registers the “Chase Race” effect (three cars racing with Pace, Car length, and Gap controls, plus the existing color slots)
  • assigns a dedicated USERMOD_ID_CHASE_RACE and hooks the usermod into the load script so it can be enabled via custom_usermods = chase_race and -D USERMOD_CHASE_RACE
  • documents how to enable/build the effect and keeps the rest of WLED untouched unless the user opts in

Testing

  • pio run -e chase_race (esp32dev) builds successfully with both AudioReactive and Chase Race usermods linked
  • verified slider behavior and effect rendering on hardware (46 LED strip)

Summary by CodeRabbit

  • New Features

    • Added "Chase Race" lighting effect featuring three colored cars racing around the LED strip with adjustable speed, car length, gap spacing, and individual car colors.
    • Enhanced build system to support custom user-created modular effects.
  • Documentation

    • Added documentation for the new Chase Race effect including configuration options.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 13, 2025

Walkthrough

This PR introduces a new Chase Race effect usermod for WLED, featuring three colored cars racing around LED segments. It includes the effect implementation with position tracking and color animation, supporting build configuration files, and modifies the build system for improved usermod dependency path resolution.

Changes

Cohort / File(s) Summary
Chase Race Effect Implementation
usermods/chase_race/chase_race_usermod.cpp
Implements mode_chase_race() rendering three colored cars with configurable speed (Pace), length (Intensity), and gap (Custom 1). Registers ChaseRaceUsermod class via USERMOD_ID_CHASE_RACE (59) with UI metadata blob; maintains head position in SEGENV.aux0 and advances based on interval.
Usermod Registration & Metadata
usermods/chase_race/library.json, usermods/chase_race/readme.md
New library manifest (v0.1.0, no dependencies) and documentation describing effect controls and PlatformIO build steps.
Configuration & Constants
usermods/chase_race/const.h, wled00/const.h
New comprehensive constants header with extensive hardware/platform/API/networking/LED configuration macros; wled00/const.h adds USERMOD_ID_CHASE_RACE (59) constant.
Build System Path Resolution
pio-scripts/load_usermods.py
Refactors project_dir handling and is_wled_module detection to resolve dep.src_dir before path containment checks; improves usermod dependency resolution.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • usermods/chase_race/chase_race_usermod.cpp: Requires careful review of position tracking state (SEGENV.aux0), interval computation, car origin/spacing calculations, and WLED segment/color API usage patterns (~25 min)
  • usermods/chase_race/const.h: Large constants file with many macro definitions across diverse domains (hardware, networking, API limits); verify consistency with existing codebase patterns and ESP8266/ESP32 conditionals (~20 min)
  • pio-scripts/load_usermods.py: Path resolution changes to is_wled_module and project_dir require careful validation to ensure correct behavior across different usermod configurations (~10 min)
  • Integration testing: Verify usermod registration, UI metadata blob correctness, and effect rendering logic (~5 min)

Possibly related PRs

Suggested reviewers

  • DedeHai
  • willmmiles
  • softhack007

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ 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: adding a new Chase Race usermod effect to WLED. It is concise, clear, and directly reflects the primary objective of the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • 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: 1

🧹 Nitpick comments (4)
wled00/const.h (1)

210-210: Chase Race usermod ID definition is consistent with existing IDs

USERMOD_ID_CHASE_RACE fits the existing sequential ID pattern and matches how ChaseRaceUsermod::getId() uses it; no functional issues here. If you want to stay aligned with the pattern some usermods follow (keeping IDs local to avoid touching core and given plans to eventually de-emphasize numeric IDs), you could alternatively hardcode the ID in the usermod itself, but what you have is consistent with most of the table. Based on learnings

usermods/chase_race/library.json (1)

1-7: Manifest wiring looks good; consider updating the description

The manifest is structurally fine and libArchive=false matches the enforcement in load_usermods.py. The only nit is the "Scaffolding" wording, which undersells the now fully implemented effect—consider updating the description to something like “Three‑car Chase Race WLED effect usermod” for clarity.

pio-scripts/load_usermods.py (1)

51-61: Include-path caching and recursion guard look sound

cached_add_includes de-duplicates deps via the dep_cache set and only recurses into non-usermods (usermod_dir not in Path(dep.src_dir).parents), which avoids unnecessary cycles while still collecting transitive include dirs. That’s a reasonable balance between correctness and performance for the dependency graph.

usermods/chase_race/const.h (1)

1-4: Avoid duplicating core const.h under usermods; rely on wled00/const.h (or isolate mod‑specific bits)

This header appears to be a near‑copy of the core wled00/const.h (global limits, IDs, DMX modes, etc.) with USERMOD_ID_CHASE_RACE added, and it reuses the same include guard macro WLED_CONST_H. That combination is fragile:

  • If both headers are visible on the include path, whichever is included first will “win” and the other will be silently skipped due to the shared guard, making future divergence between the two very hard to debug.
  • Duplicating all core constants under usermods/chase_race adds maintenance burden; any future changes to core ranges, error codes, or usermod IDs must now be kept in sync in two places.

Given that wled00/const.h is already updated to define USERMOD_ID_CHASE_RACE, the usual pattern would be:

  • Keep the single source of truth in wled00/const.h.
  • In chase_race_usermod.cpp, include wled.h (which pulls in the core consts) and use USERMOD_ID_CHASE_RACE from there.
  • If you need a local header for chase‑race‑specific definitions, give it a unique name/guard and limit it to mod‑specific constants only.

I’d strongly recommend removing this duplicate const.h (or trimming it down to only chase‑race‑specific definitions with a distinct guard) and relying on the core header instead.

Please double‑check how USERMOD_ID_CHASE_RACE is currently obtained in chase_race_usermod.cpp and whether any translation unit includes this usermod const.h instead of the core one; if so, consider switching those includes to wled.h/wled00/const.h to keep a single authoritative definition.

Also applies to: 148-211

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd933ff and 8f23c47.

📒 Files selected for processing (7)
  • pio-scripts/load_usermods.py (2 hunks)
  • usermods/chase_race/chase_race_usermod.cpp (1 hunks)
  • usermods/chase_race/const.h (1 hunks)
  • usermods/chase_race/library.json (1 hunks)
  • usermods/chase_race/load_usermods.py (1 hunks)
  • usermods/chase_race/readme.md (1 hunks)
  • wled00/const.h (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
wled00/**/!(html_*)*.h

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

Use 2-space indentation for non-generated C++ header files (.h)

Files:

  • wled00/const.h
🧠 Learnings (16)
📓 Common learnings
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.
📚 Learning: 2025-03-29T01:22:54.617Z
Learnt from: willmmiles
Repo: wled/WLED PR: 4623
File: usermods/word-clock-matrix/word-clock-matrix.cpp:332-332
Timestamp: 2025-03-29T01:22:54.617Z
Learning: In the WLED project, hardcoded usermod IDs (like 500 in the WordClockMatrix's getId() method) are intentionally used as part of a strategy to avoid modifying core code. There are plans to remove numeric usermod IDs entirely in the future.

Applied to files:

  • wled00/const.h
  • usermods/chase_race/chase_race_usermod.cpp
📚 Learning: 2025-06-15T09:59:52.720Z
Learnt from: netmindz
Repo: wled/WLED PR: 4728
File: wled00/FX.h:378-378
Timestamp: 2025-06-15T09:59:52.720Z
Learning: In WLED's FX.h, MODE_COUNT represents the highest FX_MODE_ ID + 1, not the total count of FX_MODE_ definitions. This is because some effects may have been removed but their IDs are not reused, creating gaps in the numbering sequence. MODE_COUNT is used for array bounds and range checking.

Applied to files:

  • wled00/const.h
  • usermods/chase_race/chase_race_usermod.cpp
  • usermods/chase_race/const.h
📚 Learning: 2025-08-31T03:38:14.114Z
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.

Applied to files:

  • usermods/chase_race/chase_race_usermod.cpp
  • usermods/chase_race/const.h
📚 Learning: 2025-08-08T17:22:37.374Z
Learnt from: netmindz
Repo: wled/WLED PR: 4819
File: wled00/FX.cpp:10654-10655
Timestamp: 2025-08-08T17:22:37.374Z
Learning: In WLED FX.cpp, um_data returned by getAudioData() is intentionally untyped because different usermods provide different payloads, and simulateSound() may supply dummy data when the AudioReactive usermod isn’t built. Strongly typed accessors are not feasible; prefer explicit casts (e.g., samplePeak as uint8_t at u_data[3]) or small helpers that encapsulate these casts.

Applied to files:

  • usermods/chase_race/chase_race_usermod.cpp
📚 Learning: 2025-04-26T19:19:07.600Z
Learnt from: blazoncek
Repo: wled/WLED PR: 4658
File: wled00/const.h:140-141
Timestamp: 2025-04-26T19:19:07.600Z
Learning: In WLED, the WLED_MAX_PANELS macro is intentionally defined as a fixed constant value (18) with no redefinition mechanism, making it "unoverridable" - there's no need for a static assertion to check its maximum value.

Applied to files:

  • usermods/chase_race/const.h
📚 Learning: 2025-10-05T15:24:05.545Z
Learnt from: CR
Repo: wled/WLED PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-05T15:24:05.545Z
Learning: Applies to wled00/html_*.h : DO NOT edit generated embedded web header files (wled00/html_*.h)

Applied to files:

  • usermods/chase_race/const.h
📚 Learning: 2025-09-16T18:08:42.848Z
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.

Applied to files:

  • usermods/chase_race/const.h
📚 Learning: 2025-08-28T08:09:20.630Z
Learnt from: mval-sg
Repo: wled/WLED PR: 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:

  • usermods/chase_race/const.h
📚 Learning: 2025-08-26T11:51:21.817Z
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.

Applied to files:

  • usermods/chase_race/const.h
📚 Learning: 2025-08-29T00:26:15.808Z
Learnt from: ksedgwic
Repo: wled/WLED PR: 4883
File: usermods/usermod_v2_skystrip/rest_json_client.h:6-14
Timestamp: 2025-08-29T00:26:15.808Z
Learning: WLED uses a vendored ArduinoJson library (version 6) located at "src/dependencies/json/ArduinoJson-v6.h" which is included through wled.h. Usermods should not directly include ArduinoJson headers but instead rely on wled.h for ArduinoJson symbols. The standard pattern is to include wled.h and use JsonObject, JsonArray, DynamicJsonDocument, etc. without additional includes.

Applied to files:

  • usermods/chase_race/const.h
📚 Learning: 2025-10-20T09:38:51.997Z
Learnt from: blazoncek
Repo: wled/WLED PR: 4995
File: wled00/FX.cpp:5223-5226
Timestamp: 2025-10-20T09:38:51.997Z
Learning: WLED matrices: each dimension (SEG_W, SEG_H) is limited to ≤255; 256 or larger per side is not supported/feasible on ESP32, so effects should assume per-dimension max 255.

Applied to files:

  • usermods/chase_race/const.h
📚 Learning: 2025-02-19T12:43:34.200Z
Learnt from: blazoncek
Repo: wled/WLED PR: 4482
File: wled00/udp.cpp:147-149
Timestamp: 2025-02-19T12:43:34.200Z
Learning: In WLED, maximum segment name length varies by platform:
- ESP8266: 32 characters (WLED_MAX_SEGNAME_LEN = 32)
- ESP32: 64 characters (WLED_MAX_SEGNAME_LEN = 64)
This platform difference can cause truncation when syncing longer names from ESP32 to ESP8266. Additionally, the WLED UI has limitations regarding modified maximum segment name lengths.

Applied to files:

  • usermods/chase_race/const.h
📚 Learning: 2025-09-12T17:29:43.826Z
Learnt from: DedeHai
Repo: wled/WLED PR: 4923
File: wled00/FX.cpp:4883-4901
Timestamp: 2025-09-12T17:29:43.826Z
Learning: In WLED’s web UI, only one slider value (e.g., SEGMENT.intensity or SEGMENT.custom1) changes at a time; code relying on this may use simplified change guards, though presets/JSON can still update multiple fields atomically.

Applied to files:

  • usermods/chase_race/const.h
📚 Learning: 2025-09-01T10:26:17.959Z
Learnt from: mval-sg
Repo: wled/WLED PR: 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:

  • usermods/chase_race/const.h
📚 Learning: 2025-05-09T18:43:15.355Z
Learnt from: DedeHai
Repo: wled/WLED PR: 4682
File: wled00/FX.cpp:8997-9005
Timestamp: 2025-05-09T18:43:15.355Z
Learning: In the WLED codebase, SEGMENT.custom3 is always constrained to the range 0-31 and will not exceed this range.

Applied to files:

  • usermods/chase_race/const.h
🧬 Code graph analysis (3)
wled00/const.h (1)
usermods/chase_race/chase_race_usermod.cpp (1)
  • USERMOD_ID_CHASE_RACE (100-100)
usermods/chase_race/chase_race_usermod.cpp (1)
usermods/usermod_v2_animartrix/usermod_v2_animartrix.cpp (1)
  • SEGENV (64-76)
usermods/chase_race/const.h (1)
usermods/chase_race/chase_race_usermod.cpp (1)
  • USERMOD_ID_CHASE_RACE (100-100)
🪛 Clang (14.0.6)
usermods/chase_race/chase_race_usermod.cpp

[error] 1-1: 'wled.h' file not found

(clang-diagnostic-error)

usermods/chase_race/const.h

[error] 9-9: unknown type name 'constexpr'

(clang-diagnostic-error)


[error] 9-9: expected ';' after top level declarator

(clang-diagnostic-error)


[error] 10-10: unknown type name 'constexpr'

(clang-diagnostic-error)


[error] 10-10: expected ';' after top level declarator

(clang-diagnostic-error)


[error] 11-11: unknown type name 'constexpr'

(clang-diagnostic-error)


[error] 11-11: expected ';' after top level declarator

(clang-diagnostic-error)


[error] 12-12: unknown type name 'constexpr'

(clang-diagnostic-error)


[error] 12-12: expected ';' after top level declarator

(clang-diagnostic-error)


[error] 62-62: 'driver/ledc.h' file not found

(clang-diagnostic-error)

🪛 LanguageTool
usermods/chase_race/readme.md

[grammar] ~30-~30: Ensure spelling is correct
Context: ...els to honor the requested spacing, the usermod shrinks the gaps automatically (down ...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 Ruff (0.14.4)
usermods/chase_race/load_usermods.py

1-1: Undefined name Import

(F821)


8-8: Undefined name env

(F821)


27-27: Avoid specifying long messages outside the exception class

(TRY003)


37-37: Undefined name env

(F821)


48-48: Undefined name env

(F821)


48-48: Undefined name env

(F821)


48-48: Undefined name env

(F821)


65-65: Undefined name env

(F821)


109-109: Undefined name env

(F821)

pio-scripts/load_usermods.py

8-8: Undefined name env

(F821)

🔇 Additional comments (7)
usermods/chase_race/readme.md (1)

1-36: Readme matches the implementation and build flow

The build instructions and control descriptions line up with the usermod code (Pace/speed, Car length/intensity, Gap size/custom1, car color slots) and the PlatformIO env setup. No changes needed from a code/doc consistency standpoint.

pio-scripts/load_usermods.py (3)

8-9: Project/usermod path resolution is clear and efficient

Computing project_dir and usermod_dir once at import time via Path(...).resolve() is straightforward and avoids repeated env lookups, while also handling symlinks and relative paths cleanly.


29-33: Improved WLED-module detection via resolved source roots

Resolving dep.src_dir before checking whether it lives under usermod_dir and falling back to a name prefix ("wled-") gives a simple, robust test for WLED modules (including symlinked usermods). This should behave better across platforms than raw string checks.


68-105: Wrapper around ConfigureProjectLibBuilder correctly wires WLED usermods; just watch PlatformIO changes

The wrapper clones the original ConfigureProjectLibBuilder, walks its depbuilders, builds a shared extra_include_dirs deque, and then:

  • Identifies WLED modules via is_wled_module.
  • Prepends the WLED project src dir and all collected dependency include dirs to each WLED module’s CPPPATH.
  • Enforces dep.lib_archive == False, emitting a clear error if any usermod forgets to set libArchive=false.
  • Exposes the final WLED dependency list via WLED_MODULES for downstream use.

This is a clean way to centralize usermod wiring for modules like chase_race. Given this depends on PlatformIO’s internal builder API and on dep.src_dir being consistently populated, it’s worth re-validating after PlatformIO upgrades to ensure the hook still fires and depbuilders has the expected shape.

Also applies to: 108-109

usermods/chase_race/chase_race_usermod.cpp (3)

21-30: Effect metadata string and control mapping align with WLED conventions

The _data_FX_MODE_CHASE_RACE descriptor cleanly exposes Pace, Car length, Gap size, and three car color slots, matching both the comments and the Readme. The format (Name@sliders;colors;;) and the unused fourth slider slot via ,,, follow existing WLED patterns, so UI/JSON integration should be straightforward.


31-88: Race logic is bounded and degrades gracefully on small segments

The implementation of mode_chase_race:

  • Uses SEGENV.step/strip.now to derive a speed-dependent interval and advances the head position modulo segLen.
  • Computes carLenMax from segLen so three cars plus minimal gaps can fit where possible, and clamps carLen accordingly.
  • Derives maxGap and clamps gapLen, shrinking automatically down to zero when the strip is too short—matching the documented behaviour.
  • Clears the segment and draws up to three cars using modular indexing, so wrapping and small-strip cases don’t cause out-of-bounds access.

Overall the math is defensive and well-contained; no obvious off-by-one or overflow issues for typical WLED segment sizes.


92-105: Effect registration and usermod ID usage look correct; consider legacy ID compatibility

ChaseRaceUsermod::setup() follows the usual pattern of registering the effect via strip.addEffect, and getId() correctly returns USERMOD_ID_CHASE_RACE as added in wled00/const.h, so usermod identification is consistent.

One thing to double‑check: passing 255 as the first argument to addEffect likely means “append at the end”. If the intent is to preserve the original Chase Race effect ID for existing presets or API integrations, you may want to ensure this ID matches whatever the legacy FX_MODE_* ID was. If you’re fine with the mode moving to a new ID whenever the usermod is enabled, the current approach is OK.

Comment on lines +68 to +71
def wrapped_ConfigureProjectLibBuilder(xenv):
# Call the wrapped function
result = old_ConfigureProjectLibBuilder.clone(xenv)()

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Fix ConfigureProjectLibBuilder wrapper: .clone(xenv)() will likely break builds

old_ConfigureProjectLibBuilder is a bound SCons/PlatformIO method, not an object with a .clone method. Calling old_ConfigureProjectLibBuilder.clone(xenv)() will almost certainly raise AttributeError at build time when PlatformIO calls env.ConfigureProjectLibBuilder().

PlatformIO’s usual monkey‑patch pattern is to save the old bound method and call it with no arguments (the env is already bound), e.g. project = oldLibBuilder().

Recommend changing the wrapper to invoke the original directly:

-def wrapped_ConfigureProjectLibBuilder(xenv):
-  # Call the wrapped function
-  result = old_ConfigureProjectLibBuilder.clone(xenv)()
+def wrapped_ConfigureProjectLibBuilder(xenv):
+  # Call the original ConfigureProjectLibBuilder to resolve dependencies
+  result = old_ConfigureProjectLibBuilder()

The rest of the function (using xenv for PROJECT_SRC_DIR, setting WLED_MODULES, etc.) can stay as is.

Please re‑run a few representative pio run environments (including ones without custom_usermods) after applying this change to confirm the wrapper behaves correctly with your PlatformIO version.


I'll now verify the review comment by inspecting the codebase and checking PlatformIO documentation.

[/run_scripts]

[/web_search]


🏁 Script executed:

#!/bin/bash
# Find and inspect the file in question
find . -name "load_usermods.py" -type f 2>/dev/null | head -5

Length of output: 69


🏁 Script executed:

#!/bin/bash
# Search for ConfigureProjectLibBuilder usage patterns in the codebase
rg -n "ConfigureProjectLibBuilder" -C 3 --type py

Length of output: 3106


🏁 Script executed:

#!/bin/bash
# Look for any clone() method usage in Python files to understand context
rg -n "\.clone\(" --type py -C 2 | head -40

Length of output: 678


🌐 Web query:

PlatformIO ConfigureProjectLibBuilder API documentation

💡 Result:

The ConfigureProjectLibBuilder API is implemented in PlatformIO Core; see:

  • Source code (function and related lib-builder classes) — platformio/builder/tools/piolib.py (PlatformIO Core repository). [1]
  • PlatformIO build/scripting documentation (Advanced Scripting / Build System API and Library Builder docs) — PlatformIO documentation. [2]

References

  1. platformio/platformio-core — platformio/builder/tools/piolib.py (ConfigureProjectLibBuilder implementation). [GitHub]. [1]
  2. PlatformIO — Advanced Scripting / Build System API and Library builder documentation. [Docs]. [2]

I need more specific information about how ConfigureProjectLibBuilder is typically called. Let me search for examples and SCons documentation.

[/web_search]


🌐 Web query:

SCons environment clone method Python

💡 Result:

env.Clone(...) returns a separate copy of an SCons Construction Environment; any keyword args you pass override values in the copy. It accepts the same kinds of construction-variable assignments as Environment(...) and also supports tools, toolpath, parse_flags, and (since 4.8.0) variables. Example:

env = Environment(CC='gcc')
opt = env.Clone(CCFLAGS='-O2') # opt is a separate Environment with CC='gcc' and CCFLAGS='-O2'
dbg = env.Clone(CCFLAGS='-g') # dbg is independent from env and opt

You can also pass tools or parse_flags:
env2 = env.Clone(tools=['msvc', MyTool], parse_flags='!wx-config --cflags')

Docs: SCons user guide and API reference. [1][2]

Sources:
[1] SCons User Guide — Making Copies of Construction Environments (env.Clone).
[2] SCons API / Functions and Environment Methods (env.Clone details).


🌐 Web query:

PlatformIO ConfigureProjectLibBuilder example usage call

💡 Result:

Short answer — call it from a PlatformIO extra Python build script (the SCons/PlatformIO env). Example:

  • In library.json (or platformio.ini) point to an extra script:
    {
    "build": { "extraScript": "extra_script.py" }
    }
  • extra_script.py:
    Import("env")
    project_lib_builder = env.ConfigureProjectLibBuilder()

    then use project_lib_builder as needed (PlatformIO's library builder will

    configure/install/search libs for the project during the build)

References: PlatformIO extraScript / builder docs and examples, and PlatformIO builder code showing env.ConfigureProjectLibBuilder() usage. [1][2][3]

Sources

  • PlatformIO library manifest / build.extraScript examples. [1]
  • PlatformIO scripting examples (extraScript usage). [2]
  • PlatformIO builder code / community traces showing env.ConfigureProjectLibBuilder() usage. [3]

[1] https://docs.platformio.org/en/latest/manifests/library-json/fields/build/builder.html
[2] https://docs.platformio.org/en/latest/scripting/examples/index.html
[3] Example traces / builder code references (platformio piolib ConfigureProjectLibBuilder).


Fix ConfigureProjectLibBuilder wrapper: .clone(xenv)() will raise AttributeError at runtime

SCons env.Clone() creates a copy of a Construction Environment, but it's a method on Environment objects, not on bound methods. When you save old_ConfigureProjectLibBuilder = env.ConfigureProjectLibBuilder, you capture a bound method (a Python method object), not an Environment. The correct pattern for calling ConfigureProjectLibBuilder is to invoke it with no arguments: project_lib_builder = env.ConfigureProjectLibBuilder().

Calling old_ConfigureProjectLibBuilder.clone(xenv)() attempts to call .clone() on a bound method, which lacks that attribute and will raise AttributeError when PlatformIO executes this code during a build.

Update lines 68–71 to call the saved method directly:

 def wrapped_ConfigureProjectLibBuilder(xenv):
   # Call the wrapped function
-  result = old_ConfigureProjectLibBuilder.clone(xenv)()
+  result = old_ConfigureProjectLibBuilder()

The xenv parameter remains available for subsequent use in the function (e.g., line 74: xenv["PROJECT_SRC_DIR"]).

📝 Committable suggestion

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

Suggested change
def wrapped_ConfigureProjectLibBuilder(xenv):
# Call the wrapped function
result = old_ConfigureProjectLibBuilder.clone(xenv)()
def wrapped_ConfigureProjectLibBuilder(xenv):
# Call the wrapped function
result = old_ConfigureProjectLibBuilder()
🤖 Prompt for AI Agents
In usermods/chase_race/load_usermods.py around lines 68 to 71, the wrapper calls
old_ConfigureProjectLibBuilder.clone(xenv)() which will raise AttributeError
because old_ConfigureProjectLibBuilder is a bound method not an Environment;
replace that call by invoking the saved method directly (result =
old_ConfigureProjectLibBuilder()) so the wrapped ConfigureProjectLibBuilder runs
correctly and keep the xenv parameter available for later use in the function.

@softhack007 softhack007 added the usermod usermod related label Nov 13, 2025
@DedeHai
Copy link
Collaborator

DedeHai commented Nov 14, 2025

you should add your effect to the "user_fx" UM, not create a new one.

Copy link
Member

@softhack007 softhack007 Nov 14, 2025

Choose a reason for hiding this comment

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

Not sure if changing this script is necessary to integrate your usermod?
Generally, a modification / improvement for the new usermods framework should better be a separate PR, instead of mixing unrelated changes in one PR.

Copy link
Collaborator

@DedeHai DedeHai Nov 14, 2025

Choose a reason for hiding this comment

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

I suspect that AI was involved... a lot of things in this PR do not make sense to me. Anyway, it needs to go to user_fx, I will not accept any UMs that are just an effect.
That is exactly why we created the user_fx usermod and a lot of effort went into documenting it (I only did reviews)

Copy link
Member

Choose a reason for hiding this comment

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

I fully agree with your position 👍

@drdome
Copy link
Author

drdome commented Nov 14, 2025

I will resubmit with feedback incorporated as a user_fx.

@drdome drdome closed this Nov 14, 2025
@drdome drdome deleted the feature/chase-race branch November 14, 2025 14:37
active segment. Blank pixels between cars simulate the open road, and once the
lead car crosses the finish line the entire pack immediately wraps to the
start, creating an endless race loop.

Copy link
Member

@softhack007 softhack007 Nov 15, 2025

Choose a reason for hiding this comment

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

Can you add some short video or screenshots to explain your effect?
From the description it looks like just a very complicated way of moving three small bars around simultaneously? Maybe I'm wrong and the description is misleading.

uint16_t desiredGap = map(SEGMENT.custom1, 0, 255, 1, max<uint16_t>(1, segLen / 3));
uint16_t gapLen = (maxGap == 0)
? 0
: max<uint16_t>(1, min<uint16_t>(desiredGap, maxGap));
Copy link
Member

@softhack007 softhack007 Nov 15, 2025

Choose a reason for hiding this comment

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

Please read our coding style guideline, instead of being an artist with simple conditional assignments.


SEGMENT.fill(BLACK);

auto drawCar = [&](uint16_t start, uint32_t color) {
Copy link
Member

Choose a reason for hiding this comment

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

@coderabbitai please explain this part of the code - is it a c++ extension? Will it compile on all supported platforms?

Copy link
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check platformio.ini for C++ standard flags
echo "=== Checking C++ standard in platformio.ini ==="
if [ -f platformio.ini ]; then
  grep -i "std=" platformio.ini | head -20
else
  echo "platformio.ini not found"
fi

echo ""
echo "=== Checking for existing lambda usage in WLED codebase ==="
# Search for lambda expressions in the codebase (looking for [capture] patterns followed by parameters)
rg -n '\[\s*[&=,\w]*\s*\]\s*\([^)]*\)\s*(\{|->)' --type=cpp -g '!usermods/chase_race/*' | head -30

echo ""
echo "=== Checking build flags for C++ version ==="
if [ -f platformio.ini ]; then
  grep -i "build_flags" platformio.ini | head -10
fi

Length of output: 3743


@softhack007, great question! This is not a C++ extension—it's standard C++11 lambda function syntax, and yes, it will compile on all supported platforms.

What this syntax means:

auto drawCar = [&](uint16_t start, uint32_t color) {

This declares a lambda (anonymous function) with:

  • auto - type deduction (the compiler infers this is a function type)
  • [&] - capture clause that captures all local variables by reference
  • (uint16_t start, uint32_t color) - parameters the lambda accepts
  • The body follows in curly braces

Already used extensively in WLED:

Lambda functions are already used throughout the WLED codebase. For example, wled00/wled_server.cpp has 30+ lambda expressions for HTTP handlers:

server.on(F("/version"), HTTP_GET, [](AsyncWebServerRequest *request){
  // handler code
});

Platform compatibility:

  • ESP8266: Uses Arduino Core for ESP8266 with GCC 10+ (supports C++11/14/17)
  • ESP32 variants (ESP32, ESP32-S2, ESP32-S3, ESP32-C3): Use ESP-IDF with GCC 8+ (full C++11/14/17 support)

Lambda expressions have been standard since C++11 (released in 2011) and are fully supported by all modern compilers used for ESP platforms. This code will compile without any issues on all WLED-supported platforms. ✅


🧠 Learnings used
Learnt from: netmindz
Repo: wled/WLED PR: 4728
File: wled00/FX.h:378-378
Timestamp: 2025-06-15T09:59:52.720Z
Learning: In WLED's FX.h, MODE_COUNT represents the highest FX_MODE_ ID + 1, not the total count of FX_MODE_ definitions. This is because some effects may have been removed but their IDs are not reused, creating gaps in the numbering sequence. MODE_COUNT is used for array bounds and range checking.

Learnt from: netmindz
Repo: wled/WLED PR: 4728
File: wled00/FX.h:378-378
Timestamp: 2025-06-15T09:59:52.720Z
Learning: In WLED's FX.h, MODE_COUNT represents the highest FX_MODE_ ID + 1, not the total count of FX_MODE_ definitions. This is because some effects may have been removed but their IDs are not reused, creating gaps in the numbering sequence. MODE_COUNT is used for array bounds and range checking.

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

Copy link
Member

@softhack007 softhack007 Nov 15, 2025

Choose a reason for hiding this comment

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

@coderabbitai Can the code be simplified to avoid the lambda?

Copy link
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get the full implementation of the mode_chase_race function to see how the lambda is used
cat usermods/chase_race/chase_race_usermod.cpp

Length of output: 3234


@softhack007, yes the lambda could be removed, but it would actually make the code longer and more repetitive without any real benefit.

Current implementation (with lambda):

9 lines total - lambda definition (6 lines) + 3 calls (3 lines)

Alternative 1: Inline expansion (without lambda):

// Car 1
if (SEGCOLOR(2)) {
  for (uint16_t i = 0; i < carLen && i < segLen; i++) {
    uint16_t idx = (origin + i) % segLen;
    SEGMENT.setPixelColor(idx, SEGCOLOR(2));
  }
}
// Car 2
if (SEGCOLOR(1)) {
  for (uint16_t i = 0; i < carLen && i < segLen; i++) {
    uint16_t idx = ((origin + spacing) % segLen + i) % segLen;
    SEGMENT.setPixelColor(idx, SEGCOLOR(1));
  }
}
// Car 3
if (SEGCOLOR(0)) {
  for (uint16_t i = 0; i < carLen && i < segLen; i++) {
    uint16_t idx = ((origin + (2 * spacing)) % segLen + i) % segLen;
    SEGMENT.setPixelColor(idx, SEGCOLOR(0));
  }
}

~18 lines - duplicates the loop logic 3 times, harder to maintain

Alternative 2: Static helper function:

Would need to pass carLen and segLen as parameters, and define it outside the function scope. Similar complexity to the lambda but without the convenience of auto-capture.

Recommendation:

Keep the lambda ✅ because:

  1. Avoids code duplication - the drawing logic only appears once
  2. More maintainable - changes only need to be made in one place
  3. Cleaner - auto-captures local variables without parameter passing
  4. Standard C++ - lambdas are C++11 and already used throughout WLED (30+ instances in wled_server.cpp alone)
  5. Shorter - 9 lines vs 18+ lines for inline expansion

The lambda is actually the most elegant solution here for drawing three similar elements with different positions and colors. 👍


🧠 Learnings used
Learnt from: netmindz
Repo: wled/WLED PR: 4728
File: wled00/FX.h:378-378
Timestamp: 2025-06-15T09:59:52.720Z
Learning: In WLED's FX.h, MODE_COUNT represents the highest FX_MODE_ ID + 1, not the total count of FX_MODE_ definitions. This is because some effects may have been removed but their IDs are not reused, creating gaps in the numbering sequence. MODE_COUNT is used for array bounds and range checking.

Learnt from: netmindz
Repo: wled/WLED PR: 4728
File: wled00/FX.h:378-378
Timestamp: 2025-06-15T09:59:52.720Z
Learning: In WLED's FX.h, MODE_COUNT represents the highest FX_MODE_ ID + 1, not the total count of FX_MODE_ definitions. This is because some effects may have been removed but their IDs are not reused, creating gaps in the numbering sequence. MODE_COUNT is used for array bounds and range checking.

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: 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: 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
Repo: wled/WLED PR: 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: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2025-11-14T13:37:11.973Z
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: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2025-11-14T13:37:30.945Z
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.

Copy link
Collaborator

@DedeHai DedeHai Nov 15, 2025

Choose a reason for hiding this comment

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

@softhack007 see #4566 (comment)
this is what an AI outputs when properly instructed ;)

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Member

@softhack007 softhack007 Nov 15, 2025

Choose a reason for hiding this comment

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

OMG 🤦 we entered the era of bloated AI code that has no purpose other than appearing "impressive" 🤣

... so here we have an auto-typed lambda function with capture clause, instead of a simple nested for loop, and AI says "well if webserver callback hooks can be done with lambda, why not replace a for loop with the same very elegant idea" 🤔

Copy link
Member

Choose a reason for hiding this comment

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

I'm still puzzled why a simple effect needs a backpack full of python code in load_usermod.py. Even audioreactive can be configured with a few lines of load_usermod.py 🤔

@softhack007
Copy link
Member

softhack007 commented Nov 15, 2025

@drdome after maintainers have expressed their confusion and we are still unsure if you do understand your own code, let's summarize some basic expectations when you re-submit your effect:

  • AI generated code must be clearly identified, for example "// this function was generated by an AI. I (PR author) have reviewed and understood the code fully."
  • no modifications outside of the usermod folder, unless they are strictly necessary.
  • consider the suggestion by @DedeHai see Added Chase Race Effect to WLED #4566 (comment) to simplify your effect code.
  • if you integrate everything into the user_fx usermod, then no changes to load_usermod.py, unless you explain why such changes are required to make your code compile.

It's clear that AI can help to write code, and it looks like you took help from an AI. That's not a bad thing at all. But - please, by all means - say it explicitly instead of making us all wonder "wtf, what is this'n'that, and why is all this voodoo magic needed for a rather straight forward effect idea"...

@drdome please understand that we are not here to put pain on you. We are happy about contributions to WLED, because it's a community project that lives on individuals commiting time and effort to WLED. Please also understand that as maintainers, we have to always ask ourselves "is the source code clear, straight and well-documented"? Because it needs to be maintained by different people over several years.

@softhack007 softhack007 added the AI Partly generated by an AI. Make sure that the contributor fully understands the code! label Dec 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI Partly generated by an AI. Make sure that the contributor fully understands the code! usermod usermod related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants