From 2cf79937b61ee255c7441a3019d1d679264f629d Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 6 Apr 2026 00:13:40 +0200 Subject: [PATCH 01/48] copilot-instructions rework Initial commit based on WLED-MM version * shorten previous instructions, by removing duplicated information and shortening wording * move agent instructions into a separate file * added coding conventions for C++, webUI, CI/CD (based on WLED-MM, needs adjustment for WLED) * added .coderabbit.yaml so the rabbits picks the same instructions for reviews. more information: * https://github.com/MoonModules/WLED-MM/pull/353 * https://github.com/MoonModules/WLED-MM/pull/354 * https://github.com/MoonModules/WLED-MM/pull/356 --- .coderabbit.yaml | 76 +++++ .github/agent-build.instructions.md | 112 +++++++ .github/cicd.instructions.md | 160 ++++++++++ .github/copilot-instructions.md | 249 +++++----------- .github/cpp.instructions.md | 448 ++++++++++++++++++++++++++++ .github/web.instructions.md | 27 ++ 6 files changed, 903 insertions(+), 169 deletions(-) create mode 100644 .coderabbit.yaml create mode 100644 .github/agent-build.instructions.md create mode 100644 .github/cicd.instructions.md create mode 100644 .github/cpp.instructions.md create mode 100644 .github/web.instructions.md diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 0000000000..aae196e1b1 --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,76 @@ +# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json +# +# CodeRabbit configuration — references existing guideline files to avoid +# duplicating conventions. See: +# .github/copilot-instructions.md — project overview & general rules +# .github/cpp.instructions.md — C++ coding conventions +# .github/web.instructions.md — Web UI coding conventions +# .github/cicd.instructions.md — GitHub Actions / CI-CD conventions +# +# NOTE: This file must be committed (tracked by git) for CodeRabbit to read +# it from the repository. If it is listed in .gitignore, CodeRabbit will +# not see it and these settings will have no effect. + +language: en-US + +reviews: + path_instructions: + - path: "**/*.{cpp,h,hpp,ino}" + instructions: > + Follow the C++ coding conventions documented in .github/cpp.instructions.md + and the general project guidelines in .github/copilot-instructions.md. + + Key rules: 2-space indentation (no tabs), camelCase functions/variables, + PascalCase classes, UPPER_CASE macros. No C++ exceptions — use return codes and debug macros. + + Hot-path optimization guidelines (attributes, uint_fast types, caching, + unsigned range checks) apply from pixel set/get operations and strip.show() downward — + NOT to effect functions in FX.cpp, which have diverse contributor styles. + + - path: "wled00/data/**" + instructions: > + Follow the web UI conventions documented in .github/web.instructions.md. + + Key rules: indent HTML and JavaScript with tabs, CSS with tabs. + Files here are built into wled00/html_*.h by tools/cdata.js — never + edit those generated headers directly. + + - path: "wled00/html_*.h" + instructions: > + These files are auto-generated from wled00/data/ by tools/cdata.js. + They must never be manually edited or committed. Flag any PR that + includes changes to these files. + + - path: "usermods/**" + instructions: > + Usermods are community add-ons. + Each usermod lives in its own directory under usermods/ and is implemented + as a .cpp file with a dedicated library.json file to manage dependancies. + Follow the same C++ conventions as the core firmware (.github/cpp.instructions.md). + + - path: ".github/workflows/*.{yml,yaml}" + instructions: > + Follow the CI/CD conventions documented in .github/cicd.instructions.md. + + Key rules: 2-space indentation, descriptive name: on every workflow/job/step. + Third-party actions must be pinned to a specific version tag — branch pins + such as @main or @master are not allowed. Declare explicit permissions: blocks + scoped to least privilege. Never interpolate github.event.* values directly + into run: steps — pass them through an env: variable to prevent script + injection. Do not use pull_request_target unless fully justified. + + - path: ".github/*.instructions.md" + instructions: | + This file contains both AI-facing rules and human-only reference sections. + Human-only sections are enclosed in `` / + `` HTML comment markers and should not be used as + actionable review criteria. + + When this file is modified in a PR, perform the following alignment check: + 1. For each ` ... ` block, + verify that its examples and guidance are consistent with (and do not + contradict) the AI-facing rules stated in the same file. + 2. Flag any HUMAN_ONLY section whose content has drifted from the surrounding + AI-facing rules due to edits introduced in this PR. + 3. If new AI-facing rules were added without updating a related HUMAN_ONLY + reference section, note this as a suggestion (not a required fix). \ No newline at end of file diff --git a/.github/agent-build.instructions.md b/.github/agent-build.instructions.md new file mode 100644 index 0000000000..74dac60522 --- /dev/null +++ b/.github/agent-build.instructions.md @@ -0,0 +1,112 @@ +--- +applyTo: "**" +--- +# Agent-Mode Build & Test Instructions + +Detailed build workflow, timeouts, and troubleshooting for making code changes in agent mode. Always reference these instructions first when running builds or validating changes. + +## Build Timing and Timeouts + +Use these timeout values when running builds: + +| Command | Typical Time | Minimum Timeout | Notes | +|---|---|---|---| +| `npm run build` | ~3 s | 30 s | Web UI → `wled00/html_*.h` headers | +| `npm test` | ~40 s | 2 min | Validates build system | +| `npm run dev` | continuous | — | Watch mode, auto-rebuilds on changes | +| `pio run -e ` | 15–20 min | 30 min | First build downloads toolchains; subsequent builds are faster | + +**NEVER cancel long-running builds.** PlatformIO downloads and compilation require patience. + +## Development Workflow + +### Web UI Changes + +1. Edit files in `wled00/data/` +2. Run `npm run build` to regenerate `wled00/html_*.h` headers +3. Test with local HTTP server (see Manual Testing below) +4. Run `npm test` to validate + +### Firmware Changes + +1. Edit files in `wled00/` (but **never** `html_*.h` files) +2. Ensure web UI is built first: `npm run build` +3. Build firmware: `pio run -e esp32dev` (set timeout ≥ 30 min) +4. Flash to device: `pio run -e [target] --target upload` + +### Combined Web + Firmware Changes + +1. Always build web UI first +2. Test web interface manually +3. Then build and test firmware + + +## Before Finishing Work - Testing + +**You MUST complete ALL of these before marking work as done:** + +1. **Run tests**: `npm test` — must pass +2. **Build firmware**: `pio run -e esp32dev` — must succeed after source code changes, **never skip this step**. + - Set timeout to 30+ minutes, **never cancel** + - Choose `esp32dev` as a common, representative environment + - If the build fails, fix the issue before proceeding +3. **For web UI changes**: manually test the interface (see below) + +If any step fails, fix the issue. **Do NOT mark work complete with failing builds or tests.** + +## Manual Web UI Testing + +Start a local server: + +```sh +cd wled00/data && python3 -m http.server 8080 +# Open http://localhost:8080/index.htm +``` + +Test these scenarios after every web UI change: + +- **Load**: `index.htm` loads without JavaScript errors (check browser console) +- **Navigation**: switching between main page and settings pages works +- **Color controls**: color picker and brightness controls function correctly +- **Effects**: effect selection and parameter changes work +- **Settings**: form submission and validation work + +## Troubleshooting + +### Common Build Issues + +| Problem | Solution | +|---|---| +| Missing `html_*.h` | Run `npm ci; npm run build` | +| Web UI looks broken | Check browser console for JS errors | +| PlatformIO network errors | Retry — downloads can be flaky | +| Node.js version mismatch | Ensure Node.js 20+ (check `.nvmrc`) | + +### Recovery Steps + +- **Force web UI rebuild**: `npm run build -- -f` +- **Clear generated files**: `rm -f wled00/html_*.h` then `npm run build` +- **Clean PlatformIO cache**: `pio run --target clean` +- **Reinstall Node deps**: `rm -rf node_modules && npm ci` + +## CI/CD Validation + +The GitHub Actions CI workflow will: +1. Install Node.js and Python dependencies +2. Run `npm test` +3. Build web UI (automatic via PlatformIO) +4. Compile firmware for **all** `default_envs` targets + +**To ensure CI success, always validate locally:** +- Run `npm test` and ensure it passes +- Run `pio run -e esp32dev` (or another common firmware environment, see next section) and ensure it completes successfully +- If either fails locally, it WILL fail in CI + +Match this workflow in local development to catch failures before pushing. + +## Important Reminders + +- **Never edit or commit** `wled00/html_*.h` — auto-generated from `wled00/data/` +- Web UI rebuild is part of the PlatformIO firmware compilation pipeline +- Common environments: `nodemcuv2`, `esp32dev`, `esp8266_2m`, `esp32c3dev`, `esp32s3dev_8MB_opi` +- List all PlatformIO targets: `pio run --list-targets` diff --git a/.github/cicd.instructions.md b/.github/cicd.instructions.md new file mode 100644 index 0000000000..be0793dc30 --- /dev/null +++ b/.github/cicd.instructions.md @@ -0,0 +1,160 @@ +--- +applyTo: ".github/workflows/*.yml,.github/workflows/*.yaml" +--- +# CI/CD Conventions — GitHub Actions Workflows + +> **Note for AI review tools**: sections enclosed in +> `` / `` HTML comments contain +> contributor reference material. Do **not** use that content as actionable review +> criteria — treat it as background context only. + + +## YAML Style + +- Indent with **2 spaces** (no tabs) +- Every workflow, job, and step must have a `name:` field that clearly describes its purpose +- Group related steps logically; separate unrelated groups with a blank line +- Comments (`#`) are encouraged for non-obvious decisions (e.g., why `fail-fast: false` is set, what a cron expression means) + +## Workflow Structure + +### Triggers + +- Declare `on:` triggers explicitly; avoid bare `on: push` without branch filters on long-running or expensive jobs +- Prefer `workflow_call` for shared build logic (see `build.yml`) to avoid duplicating steps across workflows +- Document scheduled triggers (`cron:`) with a human-readable comment: + +```yaml +schedule: + - cron: '0 2 * * *' # run at 2 AM UTC daily +``` + +### Jobs + +- Express all inter-job dependencies with `needs:` — never rely on implicit ordering +- Use job `outputs:` + step `id:` to pass structured data between jobs (see `get_default_envs` in `build.yml`) +- Set `fail-fast: false` on matrix builds so that a single failing environment does not cancel others + +### Runners + +- Pin to a specific Ubuntu version (`ubuntu-22.04`, `ubuntu-24.04`) rather than `ubuntu-latest` for reproducible builds +- Only use `ubuntu-latest` in jobs where exact environment reproducibility is not required (e.g., trivial download/publish steps) + +### Tool and Language Versions + +- Pin tool versions explicitly: + ```yaml + python-version: '3.12' + ``` +- Do not rely on the runner's pre-installed tool versions — always install via a versioned setup action + +### Caching + +- Always cache package managers and build tool directories when the job installs dependencies: + ```yaml + - uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + ``` +- Include the environment name or a relevant identifier in cache keys when building multiple targets + +### Artifacts + +- Name artifacts with enough context to be unambiguous (e.g., `firmware-${{ matrix.environment }}`) +- Avoid uploading artifacts that will never be consumed downstream + + +--- + +## Security + +### Permissions — Least Privilege + +Declare explicit `permissions:` blocks. The default token permissions are broad; scope them to the minimum required: + +```yaml +permissions: + contents: read # for checkout +``` + +For jobs that publish releases or write to the repository: + +```yaml +permissions: + contents: write # create/update releases +``` + +A common safe baseline for build-only jobs: + +```yaml +permissions: + contents: read +``` + +### Supply Chain — Action Pinning + +**Third-party actions** (anything outside the `actions/` and `github/` namespaces) should be pinned to a specific release tag. Branch pins (`@main`, `@master`) are **not allowed** — they can be updated by the action author at any time without notice: + +```yaml +# ✅ Acceptable — specific version tag. SHA pinning recommended for more security, as @v2 is still a mutable tag. +uses: softprops/action-gh-release@v2 + +# ❌ Not acceptable — mutable branch reference +uses: andelf/nightly-release@main +``` + +SHA pinning (e.g., `uses: someorg/some-action@abc1234`) is the most secure option for third-party actions; it is recommended when auditing supply-chain risk is a priority. At minimum, always use a specific version tag. + +**First-party actions** (`actions/checkout`, `actions/cache`, `actions/upload-artifact`, etc.) pinned to a major version tag (e.g., `@v4`) are acceptable because GitHub maintains and audits these. + +When adding a new third-party action: +1. Check that the action's repository is actively maintained +2. Review the action's source before adding it +3. Prefer well-known, widely-used actions over obscure ones + +### Credentials and Secrets + +- Use `${{ secrets.GITHUB_TOKEN }}` for operations within the same repository — it is automatically scoped and rotated +- Never commit secrets, tokens, or passwords into workflow files or any tracked file +- Never print secrets in `run:` steps, even with `echo` — GitHub masks known secrets but derived values are not automatically masked +- Scope secrets to the narrowest step that needs them using `env:` at the step level, not at the workflow level: + +```yaml +# ✅ Scoped to the step that needs it +- name: Create release + uses: softprops/action-gh-release@v2 + with: + token: ${{ secrets.GITHUB_TOKEN }} + +# ❌ Unnecessarily broad +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +``` + +- Personal Access Tokens (PATs, stored as repository secrets) should have the minimum required scopes and should be rotated periodically + +### Script Injection + +`${{ }}` expressions are evaluated before the shell script runs. If an expression comes from untrusted input (PR titles, issue bodies, branch names from forks), it can inject arbitrary shell commands. + +**Never** interpolate `github.event.*` values directly into a `run:` step: + +```yaml +# ❌ Injection risk — PR title is attacker-controlled +- run: echo "${{ github.event.pull_request.title }}" + +# ✅ Safe — value passed through an environment variable +- env: + PR_TITLE: ${{ github.event.pull_request.title }} + run: echo "$PR_TITLE" +``` + +This rule applies to any value that originates outside the repository (issue bodies, labels, comments, commit messages from forks). + +### Pull Request Workflows + +- Workflows triggered by `pull_request` from a fork run with **read-only** token permissions and no access to repository secrets — this is intentional and correct +- Do not use `pull_request_target` unless you fully understand the security implications; it runs in the context of the base branch and *does* have secret access, making it a common attack surface diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 44d422a54c..697f8f1a61 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -4,94 +4,79 @@ WLED is a fast and feature-rich implementation of an ESP32 and ESP8266 webserver Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here. -## Working Effectively - -### Initial Setup -- Install Node.js 20+ (specified in `.nvmrc`): Check your version with `node --version` -- Install dependencies: `npm ci` (takes ~5 seconds) -- Install PlatformIO for hardware builds: `pip install -r requirements.txt` (takes ~60 seconds) - -### Build and Test Workflow -- **ALWAYS build web UI first**: `npm run build` -- takes 3 seconds. NEVER CANCEL. -- **Run tests**: `npm test` -- takes 40 seconds. NEVER CANCEL. Set timeout to 2+ minutes. -- **Development mode**: `npm run dev` -- monitors file changes and auto-rebuilds web UI -- **Hardware firmware build**: `pio run -e [environment]` -- takes 15+ minutes. NEVER CANCEL. Set timeout to 30+ minutes. - -### Build Process Details -The build has two main phases: -1. **Web UI Generation** (`npm run build`): - - Processes files in `wled00/data/` (HTML, CSS, JS) - - Minifies and compresses web content - - Generates `wled00/html_*.h` files with embedded web content - - **CRITICAL**: Must be done before any hardware build - -2. **Hardware Compilation** (`pio run`): - - Compiles C++ firmware for various ESP32/ESP8266 targets - - Common environments: `nodemcuv2`, `esp32dev`, `esp8266_2m` - - List all targets: `pio run --list-targets` - -## Before Finishing Work - -**CRITICAL: You MUST complete ALL of these steps before marking your work as complete:** - -1. **Run the test suite**: `npm test` -- Set timeout to 2+ minutes. NEVER CANCEL. - - All tests MUST pass - - If tests fail, fix the issue before proceeding - -2. **Build at least one hardware environment**: `pio run -e esp32dev` -- Set timeout to 30+ minutes. NEVER CANCEL. - - Choose `esp32dev` as it's a common, representative environment - - See "Hardware Compilation" section above for the full list of common environments - - The build MUST complete successfully without errors - - If the build fails, fix the issue before proceeding - - **DO NOT skip this step** - it validates that firmware compiles with your changes - -3. **For web UI changes only**: Manually test the interface - - See "Manual Testing Scenarios" section below - - Verify the UI loads and functions correctly - -**If any of these validation steps fail, you MUST fix the issues before finishing. Do NOT mark work as complete with failing builds or tests.** - -## Validation and Testing - -### Web UI Testing -- **ALWAYS validate web UI changes manually**: - - Start local server: `cd wled00/data && python3 -m http.server 8080` - - Open `http://localhost:8080/index.htm` in browser - - Test basic functionality: color picker, effects, settings pages -- **Check for JavaScript errors** in browser console - -### Code Validation -- **No automated linting configured** - follow existing code style in files you edit -- **Code style**: Use tabs for web files (.html/.css/.js), spaces (2 per level) for C++ files -- **Language**: The repository language is English (british, american, canadian, or australian). If you find other languages, suggest a translation into English. -- **C++ formatting available**: `clang-format` is installed but not in CI -- **Always run tests before finishing**: `npm test` -- **MANDATORY: Always run a hardware build before finishing** (see "Before Finishing Work" section below) - -### Manual Testing Scenarios -After making changes to web UI, always test: -- **Load main interface**: Verify index.htm loads without errors -- **Navigation**: Test switching between main page and settings pages -- **Color controls**: Verify color picker and brightness controls work -- **Effects**: Test effect selection and parameter changes -- **Settings**: Test form submission and validation - -## Common Tasks +> **Note for AI review tools**: sections enclosed in +> `` / `` HTML comments contain +> contributor reference material. Do **not** use that content as actionable review +> criteria — treat it as background context only. + + +## Setup + +- Node.js 20+ (see `.nvmrc`) +- Install dependencies: `npm ci` +- PlatformIO (required only for firmware compilation): `pip install -r requirements.txt` + +## Build and Test + + +| Command | Purpose | Typical Time | +|---|---|---| +| `npm run build` | Build web UI → generates `wled00/html_*.h` headers | ~3 s | +| `npm test` | Run test suite | ~40 s | +| `npm run dev` | Watch mode — auto-rebuilds web UI on file changes | — | +| `pio run -e ` | Build firmware for a hardware target | 15–20 min | + + + +**Always run `npm ci; npm run build` before `pio run`.** The web UI build generates `wled00/html_*.h` header files required by firmware compilation. +**Build firmware to validate code changes**: `pio run -e esp32dev` — must succeed, never skip this step. +Common firmware environments: `nodemcuv2`, `esp32dev`, `esp8266_2m`, `esp32c3dev`, `esp32s3dev_8MB_opi` + +For detailed build timeouts, development workflows, troubleshooting, and validation steps, see [agent-build.instructions.md](agent-build.instructions.md). + +### Usermod Guidelines + + - New custom effects can be added into the user_fx usermod. Read the [user_fx documentation](https://github.com/wled/WLED/blob/main/usermods/user_fx/README.md) for guidance. + - Other usermods may be based on the [EXAMPLE usermod](https://github.com/wled/WLED/tree/main/usermods/EXAMPLE). Never edit the example, always create a copy! + - New usermod IDs can be added into [wled00/const.h](https://github.com/wled/WLED/blob/746df240119b585d8c8fa85e6aac7ed707947ea0/wled00/const.h#L160). + - to activate a usermod, a custom build configuration should be used. Add the usermod name to ``custom_usermods``. + +## Project Structure Overview ### Project Branch / Release Structure -``` + + +```text main # Main development trunk (daily/nightly) 17.0.0-dev ├── V5 # special branch: code rework for esp-idf 5.5.x (unstable) ├── V5-C6 # special branch: integration of new MCU types: esp32-c5, esp32-c6, esp32-p4 (unstable) 16_x # current beta, preparations for next release 16.0.0 -0_15_x # maintainance (bugfixes only) for current release 0.15.4 -(tag) v0.14.4 # previous version 0.14.4 (no maintainance) -(tag) v0.13.3 # old version 0.13.3 (no maintainance) +0_15_x # maintenance (bugfixes only) for current release 0.15.4 +(tag) v0.14.4 # previous version 0.14.4 (no maintenance) +(tag) v0.13.3 # old version 0.13.3 (no maintenance) (tag) v0. ... . ... # historical versions 0.12.x and before ``` + + +- ``main``: development trunk (daily/nightly) 17.0.0-dev +- `` V5`` and ``V5-C6``: code rework for esp-idf 5.5.x (unstable) - branched from ``main``. +- ``0_15_x``: bugfixing / maintenance for release 0.15.x ### Repository Structure -``` + +tl;dr: +* Firmware source: `wled00/` (C++). +* Build targets: `platformio.ini`. +* Web UI source: `wled00/data/`. +* Auto-generated headers: `wled00/html_*.h` — **never edit or commit**. +* ArduinoJSON + AsyncJSON: `wled00/dependencies/json` +* Usermods: `usermods/` (C++, with individual library.json). +* CI/CD: `.github/workflows/`. + + +Detailed overview: + +```text wled00/ # Main firmware source (C++) "WLED core" ├── data/ # Web interface files │ ├── index.htm # Main UI @@ -109,109 +94,35 @@ platformio_override.sample.ini # examples for custom build configurations - entr # platformio_override.ini is _not_ stored in the WLED repository! usermods/ # User-contributed addons to the WLED core, maintained by individual contributors (C++, with individual library.json) package.json # Node.js dependencies and scripts, release identification -pio-scripts/ # Build tools (platformio) +pio-scripts/ # Build tools (PlatformIO) tools/ # Build tools (Node.js), partition files, and generic utilities ├── cdata.js # Web UI build script └── cdata-test.js # Test suite .github/workflows/ # CI/CD pipelines ``` -### Key Files and Their Purpose -- `wled00/data/index.htm` - Main web interface -- `wled00/data/settings*.htm` - Configuration pages -- `tools/cdata.js` - Converts web files to C++ headers -- `wled00/wled.h` - Main firmware configuration -- `platformio.ini` - Hardware build targets and settings - -### Development Workflow (instructions for agent mode) -1. **For web UI changes**: - - Edit files in `wled00/data/` - - Run `npm run build` to regenerate headers - - Test with local HTTP server - - Run `npm test` to validate build system - -2. **For firmware changes**: - - Edit files in `wled00/` (but NOT `html_*.h` files) - - Ensure web UI is built first (`npm run build`) - - Build firmware: `pio run -e [target]` - - Flash to device: `pio run -e [target] --target upload` - -3. **For both web and firmware**: - - Always build web UI first - - Test web interface manually - - Build and test firmware if making firmware changes - -#### Adding a new usermod - - New custom effects can be added into the user_fx usermod. Read the [user_fx documentation](https://github.com/wled/WLED/blob/main/usermods/user_fx/README.md) for guidance. - - Other usermods may be based on the [EXAMPLE usermod](https://github.com/wled/WLED/tree/main/usermods/EXAMPLE). Never edit the example, always create a copy! - - New usermod IDs can be added into [wled00/const.h](https://github.com/wled/WLED/blob/746df240119b585d8c8fa85e6aac7ed707947ea0/wled00/const.h#L160). - - to activate a usermod, a custom build configuration should be used. Add the usermod name to ``custom_usermods``. - -## Build Timing and Timeouts - -**IMPORTANT: Use these timeout values when running builds:** - -- **Web UI build** (`npm run build`): 3 seconds typical - Set timeout to 30 seconds minimum -- **Test suite** (`npm test`): 40 seconds typical - Set timeout to 120 seconds (2 minutes) minimum -- **Hardware builds** (`pio run -e [target]`): 15-20 minutes typical for first build - Set timeout to 1800 seconds (30 minutes) minimum - - Subsequent builds are faster due to caching - - First builds download toolchains and dependencies which takes significant time -- **NEVER CANCEL long-running builds** - PlatformIO downloads and compilation require patience - -**When validating your changes before finishing, you MUST wait for the hardware build to complete successfully. Set the timeout appropriately and be patient.** + +## General Guidelines -## Troubleshooting - -### Common Issues -- **Build fails with missing html_*.h**: Run `npm run build` first -- **Web UI looks broken**: Check browser console for JavaScript errors -- **PlatformIO network errors**: Try again, downloads can be flaky -- **Node.js version issues**: Ensure Node.js 20+ is installed (check `.nvmrc`) - -### When Things Go Wrong -- **Clear generated files**: `rm -f wled00/html_*.h` then rebuild -- **Force web UI rebuild**: `npm run build -- --force` or `npm run build -- -f` -- **Clean PlatformIO cache**: `pio run --target clean` -- **Reinstall dependencies**: `rm -rf node_modules && npm install` - -## Important Notes - -- **Always commit source files** -- **Web UI re-built is part of the platformio firmware compilation** -- **do not commit generated html_*.h files** -- **DO NOT edit `wled00/html_*.h` files** - they are auto-generated. If needed, modify Web UI files in `wled00/data/`. -- **Test web interface manually after any web UI changes** +- **Repository language is English.** Suggest translations for non-English content. +- **Use VS Code with PlatformIO extension** for best development experience. +- **Never edit or commit** `wled00/html_*.h` — auto-generated from `wled00/data/`. +- If updating Web UI files in `wled00/data/`, **make use of common functions in `wled00/data/common.js` whenever possible**. - **When unsure, say so.** Gather more information rather than guessing. - **Acknowledge good patterns** when you see them. Summarize good practices as part of your review - positive feedback always helps. - **Provide references** when making analyses or recommendations. Base them on the correct branch or PR. -- **Highlight user-visible "breaking" changes and ripple effects**. Ask for confirmation that these were introduced intentionally. +- **Highlight user-visible "breaking" changes and ripple effects**. Ask for confirmation that these were introduced intentionally. - **Unused / dead code must be justified or removed**. This helps to keep the codebase clean, maintainable and readable. -- If updating Web UI files in `wled00/data/`, **make use of common functions availeable in `wled00/data/common.js` where possible**. -- **Use VS Code with PlatformIO extension for best development experience** -- **Hardware builds require appropriate ESP32/ESP8266 development board** - -## CI/CD Pipeline +- No automated linting is configured — match existing code style in files you edit. See `cpp.instructions.md` and `web.instructions.md` for language-specific conventions, and `cicd.instructions.md` for GitHub Actions workflows. -**The GitHub Actions CI workflow will:** -1. Installs Node.js and Python dependencies -2. Runs `npm test` to validate build system (MUST pass) -3. Builds web UI with `npm run build` (automatically run by PlatformIO) -4. Compiles firmware for ALL hardware targets listed in `default_envs` (MUST succeed for all) -5. Uploads build artifacts - -**To ensure CI success, you MUST locally:** -- Run `npm test` and ensure it passes -- Run `pio run -e esp32dev` (or another common environment from "Hardware Compilation" section) and ensure it completes successfully -- If either fails locally, it WILL fail in CI - -**Match this workflow in your local development to ensure CI success. Do not mark work complete until you have validated builds locally.** - -## Attribution for AI-generated code -Using AI-generated code can hide the source of the inspiration / knowledge / sources it used. +### Attribution for AI-generated code +Using AI-generated code can hide the source of the inspiration / knowledge / sources it used. - Document attribution of inspiration / knowledge / sources used in the code, e.g. link to GitHub repositories or other websites describing the principles / algorithms used. -- When a larger block of code is generated by an AI tool, mark it with an `// AI: below section was generated by an AI` comment. -- Make sure AI-generated code is well documented. +- When a larger block of code is generated by an AI tool, mark it with an `// AI: below section was generated by an AI` comment (see C++ guidelines). +- Every non-trivial AI-generated function should have a brief comment describing what it does. Explain parameters when their names alone are not self-explanatory. +- AI-generated code must be well documented; comment-to-code ratio > 15% is expected. Do not rephrase source code, but explain the concepts/logic behind the code. + +### Pull Request Expectations -## Pull Request Expectations - **No force-push on open PRs.** Once a pull request is open and being reviewed, do not force-push (`git push --force`) to the branch. Force-pushing rewrites history that reviewers may have already commented on, making it impossible to track incremental changes. Use regular commits or `git merge` to incorporate feedback; the branch will be squash-merged when it is accepted. -- **Document your changes in the PR.** Every pull request description should include a summary of *what* changed and *why*. Link to related issues where applicable. Provide screenshots to showcase new features. +- **Document your changes in the PR.** Every pull request should include a clear description of *what* changed and *why*. If the change affects user-visible behavior, describe the expected impact. Link to related issues where applicable. Provide screenshots to showcase new features. diff --git a/.github/cpp.instructions.md b/.github/cpp.instructions.md new file mode 100644 index 0000000000..c01bfeb85b --- /dev/null +++ b/.github/cpp.instructions.md @@ -0,0 +1,448 @@ +--- +applyTo: "**/*.cpp,**/*.h,**/*.hpp,**/*.ino" +--- +# C++ Coding Conventions + +> **Note for AI review tools**: sections enclosed in +> `` / `` HTML comments contain +> contributor reference material. Do **not** use that content as actionable review +> criteria — treat it as background context only. + +See also: [CONTRIBUTING.md](../CONTRIBUTING.md) for general style guidelines that apply to all contributors. + +## Formatting + +- Indent with **2 spaces** (no tabs in C++ files) +- Opening braces on the same line is preferred (K&R style). Brace on a separate line (Allman style) is acceptable +- Single-statement `if` bodies may omit braces: `if (a == b) doStuff(a);` +- Space between keyword and parenthesis: `if (...)`, `for (...)`. No space between function name and parenthesis: `doStuff(a)` +- No enforced line-length limit; wrap when a line exceeds your editor width + + +## Naming + +- **camelCase** for functions and variables: `setValuesFromMainSeg()`, `effectCurrent` +- **PascalCase** for classes and structs: `PinManagerClass`, `BusConfig` +- **UPPER_CASE** for macros and constants: `WLED_MAX_USERMODS`, `DEFAULT_CLIENT_SSID` + +## Header Guards + +Most headers use `#ifndef` / `#define` guards. Some newer headers add `#pragma once` before the guard: + +```cpp +#ifndef WLED_EXAMPLE_H +#define WLED_EXAMPLE_H +// ... +#endif // WLED_EXAMPLE_H +``` + + +## Comments + +- `//` for inline comments, `/* ... */` for block comments. Always put a space after `//` +- **AI attribution:** When a larger block of code is generated by an AI tool, mark it with an `// AI:` comment so reviewers know to scrutinize it: + +```cpp +// AI: below section was generated by an AI +void calculateCRC(const uint8_t* data, size_t len) { + ... +} +// AI: end of AI-generated section +``` + + Single-line AI-assisted edits do not need the marker — use it when the AI produced a contiguous block that a human did not write line-by-line. + + + +- **Function & feature comments:** Every non-trivial function should have a brief comment above it describing what it does. Include a note about each parameter when the names alone are not self-explanatory: + +```cpp +/* ***** + * Apply gamma correction to a single color channel. + * @param value raw 8-bit channel value (0–255) + * @param gamma gamma exponent (typically 2.8) + * @return corrected 8-bit value + ***** */ +uint8_t gammaCorrect(uint8_t value, float gamma); +``` + + + Short accessor-style functions (getters/setters, one-liners) may skip this if their purpose is obvious from the name. + +## Preprocessor & Feature Flags + +- Prefer compile-time feature flags (`#ifdef` / `#ifndef`) over runtime checks where possible +- Platform differentiation: `ARDUINO_ARCH_ESP32` vs `ESP8266` +- PSRAM availability: `BOARD_HAS_PSRAM` + +## Error Handling + +- `DEBUG_PRINTF()` / `DEBUG_PRINTLN()` for developer diagnostics (compiled out unless `-D WLED_DEBUG`) +- Don't rely on C++ exceptions — use return codes (`-1` / `false` for errors) and global flags (e.g. `errorFlag = ERR_LOW_MEM`). Some builds don't support C++ exceptions. + +## Strings + +- Use `F("string")` for string constants (stores in PROGMEM, saves RAM) +- Use `const char*` for temporary/parsed strings +- Avoid `String` (Arduino heap-allocated string) in hot paths; acceptable in config/setup code + +## Memory + +- **PSRAM-aware allocation**: use `d_malloc()` (prefer DRAM), `p_malloc()` (prefer PSRAM) from `fcn_declare.h` +- **Avoid Variable Length Arrays (VLAs)**: FreeRTOS task stacks are typically 2–8 KB. A runtime-sized VLA can silently exhaust the stack. Use fixed-size arrays or heap allocation (`d_malloc` / `p_malloc`). Any VLA must be explicitly justified in source or PR. + + GCC/Clang support VLAs as an extension (they are not part of the C++ standard), so they look like a legitimate feature — but they are allocated on the stack at runtime. On ESP32/ESP8266, a VLA whose size depends on a runtime parameter (segment dimensions, pixel counts, etc.) can silently exhaust the stack and cause the program to behave in unexpected ways or crash. + +- **Larger buffers** (LED data, JSON documents) should use PSRAM when available and technically feasible +- **Hot-path**: some data should stay in DRAM or IRAM for performance reasons +- Memory efficiency matters, but is less critical on boards with PSRAM + +## `const` and `constexpr` +Add `const` to cached locals in hot-path code (helps the compiler keep values in registers). Pass and store objects by `const&` to avoid copies in loops. + + +`const` is a promise to the compiler that a value (or object) will not change - a function declared with a `const char* message` parameter is not allowed to modify the content of `message`. +This pattern enables optimizations and makes intent clear to reviewers. + +### `const` locals + +Adding `const` to a local variable that is only assigned once is not necessary — but it **is** required when the variable is passed to a function that takes a `const` parameter (pointer or reference). In hot-path code, `const` on cached locals helps the compiler keep values in registers: + +```cpp +const uint_fast16_t cols = vWidth(); +const uint_fast16_t rows = vHeight(); +``` + +### `const` references to avoid copies + +Pass and store objects by `const &` (or `&`) instead of copying them implicitly. This avoids constructing temporary objects on every access — especially important in loops. + + +```cpp +const auto &m = _mappings[i]; // reference, not a copy (bus_manager.cpp) +Segment& sourcesegment = strip.getSegment(sourceid); // alias — avoids creating a temporary Segment instance +``` + +For function parameters that are read-only, prefer `const &`: + +```cpp +BusManager::add(const BusConfig &bc, bool placeholder) { +``` + + +### `constexpr` over `#define` + + +Prefer `constexpr` for compile-time constants. Unlike `#define`, `constexpr` respects scope and type safety, keeping the global namespace clean: + +```cpp +// Prefer: +constexpr uint32_t TWO_CHANNEL_MASK = 0x00FF00FF; +constexpr int WLED_MAX_BUSSES = WLED_MAX_DIGITAL_CHANNELS + WLED_MAX_ANALOG_CHANNELS; + +// Avoid (when possible): +#define TWO_CHANNEL_MASK 0x00FF00FF +``` + +Note: `#define` is still needed for conditional compilation guards (`#ifdef`), platform macros, and values that must be overridable from build flags. + +### `static_assert` over `#error` + +Use `static_assert` instead of the C-style `#if … #error … #endif` pattern when validating compile-time constants. It provides a clear message and works with `constexpr` values: + +```cpp +// Prefer: +constexpr int WLED_MAX_BUSSES = WLED_MAX_DIGITAL_CHANNELS + WLED_MAX_ANALOG_CHANNELS; +static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit"); + +// Avoid: +#if (WLED_MAX_BUSSES > 32) + #error "WLED_MAX_BUSSES exceeds hard limit" +#endif +``` + + +Prefer `constexpr` over `#define` for typed constants (scope-safe, debuggable). Use `static_assert` instead of `#if … #error` for compile-time validation. +Exception: `#define` is required for conditional-compilation guards and build-flag-overridable values. + +### `static` and `const` class methods + +#### `const` member functions + +Marking a member function `const` tells the compiler that it does not modify the object's state: + +```cpp +uint16_t length() const { return _len; } +bool isActive() const { return _active; } +``` + +Benefits for GCC/Xtensa/RISC-V: +- The compiler knows the method cannot write to `this`, so it is free to **keep member values in registers** across the call and avoid reload barriers. +- `const` methods can be called on `const` objects and `const` references — essential when passing large objects as `const &` to avoid copying. +- `const` allows the compiler to **eliminate redundant loads**: if a caller already has a member value cached, the compiler can prove the `const` call cannot invalidate it. + + +Declare getter, query, or inspection methods `const`. If you need to mark a member `mutable` to work around this (e.g. for a cache or counter), document the reason. + +#### `static` member functions + + +A `static` member function has no implicit `this` pointer. This has two distinct advantages: + +1. **Smaller code, faster calls**: no `this` is passed in a register. On Xtensa and RISC-V, this removes one register argument from every call site and prevents the compiler from emitting `this`-preservation code around inlined blocks. +2. **Better inlining**: GCC can inline a `static` method with more certainty because it cannot be overridden by a derived class (no virtual dispatch ambiguity) and has no aliasing concern through `this`. + +Use `static` for any method that does not need access to instance members: + +```cpp +// Factory / utility — no instance needed: +static BusConfig fromJson(JsonObject obj); + +// Pure computation helpers: +static uint8_t gamma8(uint8_t val); +static uint32_t colorBalance(uint32_t color, uint8_t r, uint8_t g, uint8_t b); +``` + + +`static` communicates intent clearly: a reviewer immediately knows the method is stateless and safe to call without a fully constructed object. + +> **Rule of thumb**: if a method does not read or write any member variable, make it `static`. If it only reads member variables, make it `const`. Note: `static` methods cannot also be `const`-qualified because there is no implicit `this` pointer to be const — just use `static`. Both qualifiers reduce coupling and improve generated code on all ESP32 targets. + +--- + +## Hot-Path Optimization + +The hot path is the per-frame pixel pipeline: **Segment → Strip → BusManager → Bus(Digital,HUB75,Network) or PolyBus → LED driver, plus ``WS2812FX::show()`` and below**. +Speed is the priority here. The patterns below are taken from existing hot-path code (`FX_fcn.cpp`, `FX_2Dfcn.cpp`, `bus_manager.cpp`, `colors.cpp`) and should be followed when modifying these files. + +Note: `FX.cpp` (effect functions) is written by many contributors and has diverse styles — that is acceptable. + +### Function Attributes + +Stack the appropriate attributes on hot-path functions. Defined in `const.h`: + +| Attribute | Meaning | When to use | +|---|---|---| +| `__attribute__((hot))` | Branch-prediction hint | hot-path functions with complex logic | +| `IRAM_ATTR` | Place in fast IRAM (ESP32) | Critical per-pixel functions (e.g. `BusDigital::setPixelColor`) | +| `IRAM_ATTR_YN` | IRAM on ESP32, no-op on ESP8266 | Hot functions that ESP8266 can't fit in IRAM | +| `WLED_O2_ATTR` | Force `-O2` optimization | Most hot-path functions | +| `WLED_O3_ATTR` | Force `-O3,fast-math` | Innermost color math (e.g. `color_blend`) | +| `[[gnu::hot]] inline` | Modern C++ attribute + inline | Header-defined accessors (e.g. `progress()`, `currentBri()`) | + +Note: `WLED_O3_ATTR` sometimes causes performance loss compared to `WLED_O2_ATTR`. Choose optimization levels based on test results. + +Example signature: + +```cpp +void IRAM_ATTR_YN WLED_O2_ATTR __attribute__((hot)) Segment::setPixelColor(unsigned i, uint32_t c) +``` + + +### Cache Members to Locals Before Loops + +Copy class members and virtual-call results to local variables before entering a loop: + +```cpp +uint_fast8_t count = numBusses; // avoid repeated member access +for (uint_fast8_t i = 0; i < count; i++) { + Bus* const b = busses[i]; // const pointer hints to compiler + uint_fast16_t bstart = b->getStart(); + uint_fast16_t blen = b->getLength(); + ... +} +``` + + +### Unsigned Range Check + +Replace two-comparison range tests with a single unsigned subtraction: + +```cpp +// Instead of: if (pix >= bstart && pix < bstart + blen) +if ((uint_fast16_t)(pix - bstart) < blen) // also catches negative pix via unsigned underflow +``` + +### Early Returns + +Guard every hot-path function with the cheapest necessary checks first: + +```cpp +if (!isActive()) return; // inactive segment +if (unsigned(i) >= vLength()) return; // bounds check (catches negative i too) +``` + +### Avoid Nested Calls — Fast Path / Complex Path + +Avoid calling non-inline functions or making complex decisions inside per-pixel hot-path code. When a function has both a common simple case and a rare complex case, split it into two variants and choose once per frame rather than per pixel. + +General rules: +- Keep fast-path functions free of non-inline calls, multi-way branches, and complex switch-case decisions. +- Hoist per-frame decisions (e.g. simple vs. complex segment) out of the per-pixel loop. +- Code duplication between fast/slow variants is acceptable to keep the fast path lean. + +### Function Pointers to Eliminate Repeated Decisions + +When the same decision (e.g. "which drawing routine?") would be evaluated for every pixel, assign the chosen variant to a function pointer once and let the inner loop call through the pointer. This removes the branch entirely — the calling code (e.g. the GIF decoder loop) only ever invokes one function per frame, with no per-pixel decision. + + +`image_loader.cpp` demonstrates the pattern: `calculateScaling()` picks the best drawing callback once per frame based on segment dimensions and GIF size, then passes it to the decoder via `setDrawPixelCallback()`: + +```cpp +// calculateScaling() — called once per frame +if ((perPixelX < 2) && (perPixelY < 2)) + decoder.setDrawPixelCallback(drawPixelCallbackDownScale2D); // downscale-only variant +else + decoder.setDrawPixelCallback(drawPixelCallback2D); // full-scaling variant +``` + +Each callback is a small, single-purpose function with no internal branching — the decoder's per-pixel loop never re-evaluates which strategy to use. + + +### Template Specialization (Advanced) + +Templates can eliminate runtime decisions by generating separate code paths at compile time. For example, a pixel setter could be templated on color order or channel count so the compiler removes dead branches and produces tight, specialized machine code: + +```cpp +template +void setChannel(uint8_t* out, uint32_t col) { + out[0] = R(col); out[1] = G(col); out[2] = B(col); + if constexpr (hasWhite) out[3] = W(col); // compiled out when hasWhite==false +} +``` + +Use sparingly — each instantiation duplicates code in flash. On ESP8266 and small-flash ESP32 boards this can exhaust IRAM/flash. Prefer templates only when the hot path is measurably faster and the number of instantiations is small (2–4). + +### RAII Lock-Free Synchronization (Advanced) + +Where contention is rare and the critical section is short, consider replacing mutex-based locking with lock-free techniques using `std::atomic` and RAII scoped guards. A scoped guard sets a flag on construction and clears it on destruction, guaranteeing cleanup even on early return: + +```cpp +struct ScopedBusyFlag { + std::atomic& flag; + bool acquired; + ScopedBusyFlag(std::atomic& f) : flag(f), acquired(false) { + bool expected = false; + acquired = flag.compare_exchange_strong(expected, true); + } + ~ScopedBusyFlag() { if (acquired) flag.store(false); } + explicit operator bool() const { return acquired; } +}; + +// Usage +static std::atomic busySending{false}; +ScopedBusyFlag guard(busySending); +if (!guard) return; // another task is already sending +// ... do work — flag auto-clears when guard goes out of scope +``` + +This avoids FreeRTOS semaphore overhead and the risk of forgetting `esp32SemGive`. There are no current examples of this pattern in the codebase — consult with maintainers before introducing it in new code, to ensure it aligns with the project's synchronization conventions. + + +### Pre-Compute Outside Loops + +Move invariant calculations before the loop. Pre-compute reciprocals to replace division with multiplication. + +```cpp +const uint_fast16_t cols = virtualWidth(); +const uint_fast16_t rows = virtualHeight(); +uint_fast8_t fadeRate = (255 - rate) >> 1; +float mappedRate_r = 1.0f / (float(fadeRate) + 1.1f); // reciprocal — avoid division inside loop +``` + + +### Parallel Channel Processing + +Process R+B and W+G channels simultaneously using the two-channel mask pattern: + +```cpp +constexpr uint32_t TWO_CHANNEL_MASK = 0x00FF00FF; +uint32_t rb = (((c1 & TWO_CHANNEL_MASK) * amount) >> 8) & TWO_CHANNEL_MASK; +uint32_t wg = (((c1 >> 8) & TWO_CHANNEL_MASK) * amount) & ~TWO_CHANNEL_MASK; +return rb | wg; +``` + +### Bit Shifts Over Division (mainly for RISC-V boards) + +ESP32 and ESP32-S3 (Xtensa core) have a fast "integer divide" instruction, so manual shifts rarely help. +On RISC-V targets (ESP32-C3/C6/P4), prefer explicit bit-shifts for power-of-two arithmetic — the compiler does **not** always convert divisions to shifts on RISC-V at `-O2`. Always use unsigned operands; signed right-shift is implementation-defined. + + +On RISC-V-based boards (ESP32-C3, ESP32-C6, ESP32-C5) explicit shifts can be beneficial. +```cpp +position >> 3 // instead of position / 8 +(255U - rate) >> 1 // instead of (255 - rate) / 2 +i & 0x0007 // instead of i % 8 +``` + +**Important**: The bit-shifted expression should be unsigned. On some MCUs, "signed right-shift" is implemented by an "arithmetic shift right" that duplicates the sign bit: ``0b1010 >> 1 = 0b1101``. + + +### Static Caching for Expensive Computations + +Cache results in static locals when the input rarely changes between calls: + +```cpp +static uint16_t lastKelvin = 0; +static byte correctionRGB[4] = {255,255,255,0}; +if (lastKelvin != kelvin) { + colorKtoRGB(kelvin, correctionRGB); // expensive — only recalculate when input changes + lastKelvin = kelvin; +} +``` + +### Inlining Strategy + +- Move frequently-called small functions to headers for inlining (e.g. `Segment::setPixelColorRaw` is in `FX.h`) +- Use `static inline` for file-local helpers + + +### `delay()` vs `yield()` in ESP32 Tasks + +On ESP32, `delay(ms)` calls `vTaskDelay(ms / portTICK_PERIOD_MS)`, which **suspends only the calling task**. The FreeRTOS scheduler immediately runs all other ready tasks. This differs from ESP8266, where `delay()` stalled the entire system unless `yield()` was called inside. + +**`delay()` in `loopTask` is allowed.** The Arduino `loop()` function runs inside `loopTask`. Calling `delay()` there does not block the network stack, audio FFT, LED DMA, or any other FreeRTOS task. + + +In arduino-esp32, `yield()` calls `vTaskDelay(0)`, which only switches to tasks at equal or higher priority — the IDLE task (priority 0) is never reached. +**Do not use `yield()` to pace ESP32 tasks or assume it feeds any watchdog**. + +**Custom `xTaskCreate()` tasks must call `delay(1)` in their loop, not `yield()`.** Without a real blocking call, the IDLE task is starved. The IDLE watchdog panic is the first visible symptom — but the damage starts earlier: deleted task memory leaks, software timers stop firing, light sleep is disabled, and Wi-Fi/BT idle hooks don't run. See `esp-idf.instructions.md` for a full explanation of what IDLE does. Structure custom tasks like this: + +```cpp +// WRONG — IDLE task is never scheduled; yield() does not feed the idle task watchdog. +void myTask(void*) { + for (;;) { + doWork(); + yield(); + } +} + +// CORRECT — delay(1) suspends the task for ≥1 ms, IDLE task runs, IDLE watchdog is fed +void myTask(void*) { + for (;;) { + doWork(); + delay(1); // DO NOT REMOVE — lets IDLE(0) run and feeds its watchdog + } +} +``` + +Prefer blocking FreeRTOS primitives (`xQueueReceive`, `ulTaskNotifyTake`, `vTaskDelayUntil`) over `delay(1)` polling where precise timing or event-driven behaviour is needed. + +**Watchdog note.** WLED disables the Task Watchdog by default (`WLED_WATCHDOG_TIMEOUT 0` in `wled.h`). When enabled, `esp_task_wdt_reset()` is called at the end of each `loop()` iteration. Long blocking operations inside `loop()` — such as OTA downloads or slow file I/O — must call `esp_task_wdt_reset()` periodically, or be restructured so the main loop is not blocked for longer than the configured timeout. + +## General + +- Follow the existing style in the file you are editing +- If possible, use `static` for local (C-style) variables and functions (keeps the global namespace clean) +- Avoid unexplained "magic numbers". Prefer named constants (`constexpr`) or C-style `#define` constants for repeated numbers that have the same meaning +- Include `"wled.h"` as the primary project header where needed +- **Float-to-unsigned conversion is undefined behavior when the value is out of range.** Converting a negative `float` directly to an unsigned integer type (`uint8_t`, `uint16_t`, …) is UB per the C++ standard — the Xtensa (ESP32) toolchain may silently wrap, but RISC-V (ESP32-C3/C6) can produce different results due to clamping. Cast through a signed integer first: + ```cpp + // Undefined behavior — avoid: + uint8_t angle = 40.74f * atan2f(dy, dx); // negative float → uint8_t is UB + + // Correct — cast through int first: + // atan2f returns [-π..+π], scaled ≈ [-128..+128] as int; uint8_t wraps negative ints via 2's complement (e.g. -1 → 255) + uint8_t angle = int(40.74f * atan2f(dy, dx)); // float→int (defined), int→uint8_t (defined) + ``` diff --git a/.github/web.instructions.md b/.github/web.instructions.md new file mode 100644 index 0000000000..1d6fb9581a --- /dev/null +++ b/.github/web.instructions.md @@ -0,0 +1,27 @@ +--- +applyTo: "wled00/data/**" +--- +# Web UI Coding Conventions + +## Formatting + +- Indent **HTML and JavaScript** with **tabs** +- Indent **CSS** with **tabs** + +## JavaScript Style + +- **camelCase** for functions and variables: `gId()`, `selectedFx`, `currentPreset` +- Abbreviated helpers are common: `d` for `document`, `gId()` for `getElementById()` + +## Key Files + +- `index.htm` — main interface +- `index.js` — functions that manage / update the main interface +- `settings*.htm` — configuration pages +- `*.css` — stylesheets (inlined during build) +- `common.js` — helper functions + +## Build Integration + +Files in this directory are processed by `tools/cdata.js` into `wled00/html_*.h` headers. +Run `npm run build` after any change. **Never edit the generated `html_*.h` files directly.** From b99e1a9c6f3eb9eab0c52544ba2c74afd2361f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Mon, 6 Apr 2026 00:39:32 +0200 Subject: [PATCH 02/48] fix typo in .coderabbit.yaml Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .coderabbit.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.coderabbit.yaml b/.coderabbit.yaml index aae196e1b1..7667d0d9ae 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -45,7 +45,7 @@ reviews: instructions: > Usermods are community add-ons. Each usermod lives in its own directory under usermods/ and is implemented - as a .cpp file with a dedicated library.json file to manage dependancies. + as a .cpp file with a dedicated library.json file to manage dependencies. Follow the same C++ conventions as the core firmware (.github/cpp.instructions.md). - path: ".github/workflows/*.{yml,yaml}" From 94138b3f8ed853129201e90267d3cb3896e6afc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Mon, 6 Apr 2026 00:42:05 +0200 Subject: [PATCH 03/48] Update .github/web.instructions.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .github/web.instructions.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/web.instructions.md b/.github/web.instructions.md index 1d6fb9581a..2518b8dd47 100644 --- a/.github/web.instructions.md +++ b/.github/web.instructions.md @@ -23,5 +23,6 @@ applyTo: "wled00/data/**" ## Build Integration -Files in this directory are processed by `tools/cdata.js` into `wled00/html_*.h` headers. -Run `npm run build` after any change. **Never edit the generated `html_*.h` files directly.** +Files in this directory are processed by `tools/cdata.js` into generated headers +(`wled00/html_*.h`, `wled00/js_*.h`). +Run `npm run build` after any change. **Never edit generated headers directly.** From c6a559776a9dd2e0c41dfa2c9d9669f8cd21e96a Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 6 Apr 2026 00:52:44 +0200 Subject: [PATCH 04/48] fix findings by the rabbit --- .github/cicd.instructions.md | 2 ++ .github/copilot-instructions.md | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/cicd.instructions.md b/.github/cicd.instructions.md index be0793dc30..8dfb9c9aea 100644 --- a/.github/cicd.instructions.md +++ b/.github/cicd.instructions.md @@ -71,6 +71,8 @@ schedule: ## Security +Important: Several current workflows still violate parts of the baseline below - migration is in progress. + ### Permissions — Least Privilege Declare explicit `permissions:` blocks. The default token permissions are broad; scope them to the minimum required: diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 697f8f1a61..945809eb17 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -9,7 +9,6 @@ Always reference these instructions first and fallback to search or bash command > contributor reference material. Do **not** use that content as actionable review > criteria — treat it as background context only. - ## Setup - Node.js 20+ (see `.nvmrc`) @@ -69,7 +68,7 @@ tl;dr: * Build targets: `platformio.ini`. * Web UI source: `wled00/data/`. * Auto-generated headers: `wled00/html_*.h` — **never edit or commit**. -* ArduinoJSON + AsyncJSON: `wled00/dependencies/json` +* ArduinoJSON + AsyncJSON: `wled00/src/dependencies/json` * Usermods: `usermods/` (C++, with individual library.json). * CI/CD: `.github/workflows/`. @@ -111,7 +110,7 @@ tools/ # Build tools (Node.js), partition files, and generic uti - **When unsure, say so.** Gather more information rather than guessing. - **Acknowledge good patterns** when you see them. Summarize good practices as part of your review - positive feedback always helps. - **Provide references** when making analyses or recommendations. Base them on the correct branch or PR. -- **Highlight user-visible "breaking" changes and ripple effects**. Ask for confirmation that these were introduced intentionally. +- **Highlight user-visible breaking changes and ripple effects**. Ask for confirmation that these were introduced intentionally. - **Unused / dead code must be justified or removed**. This helps to keep the codebase clean, maintainable and readable. - No automated linting is configured — match existing code style in files you edit. See `cpp.instructions.md` and `web.instructions.md` for language-specific conventions, and `cicd.instructions.md` for GitHub Actions workflows. From 6f59582ec2a668b6d033f0fe6f4dbf2b58d3fcee Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 6 Apr 2026 01:02:37 +0200 Subject: [PATCH 05/48] remove extra space --- .github/copilot-instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 945809eb17..bcf783aed6 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -58,7 +58,7 @@ main # Main development trunk (daily/nightly) 17.0.0-dev - ``main``: development trunk (daily/nightly) 17.0.0-dev -- `` V5`` and ``V5-C6``: code rework for esp-idf 5.5.x (unstable) - branched from ``main``. +- ``V5`` and ``V5-C6``: code rework for esp-idf 5.5.x (unstable) - branched from ``main``. - ``0_15_x``: bugfixing / maintenance for release 0.15.x ### Repository Structure From 4a06a6c742618666fbc4c0ec93b87133238149f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Mon, 6 Apr 2026 01:26:39 +0200 Subject: [PATCH 06/48] Update documentation on semaphore and float conversion Clarify float-to-unsigned conversion behavior and update semaphore return note. --- .github/cpp.instructions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/cpp.instructions.md b/.github/cpp.instructions.md index c01bfeb85b..0c6b4cf50e 100644 --- a/.github/cpp.instructions.md +++ b/.github/cpp.instructions.md @@ -336,7 +336,7 @@ if (!guard) return; // another task is already sending // ... do work — flag auto-clears when guard goes out of scope ``` -This avoids FreeRTOS semaphore overhead and the risk of forgetting `esp32SemGive`. There are no current examples of this pattern in the codebase — consult with maintainers before introducing it in new code, to ensure it aligns with the project's synchronization conventions. +This avoids FreeRTOS semaphore overhead and the risk of forgetting to return a semaphore. There are no current examples of this pattern in the codebase — consult with maintainers before introducing it in new code, to ensure it aligns with the project's synchronization conventions. ### Pre-Compute Outside Loops @@ -437,7 +437,7 @@ Prefer blocking FreeRTOS primitives (`xQueueReceive`, `ulTaskNotifyTake`, `vTask - If possible, use `static` for local (C-style) variables and functions (keeps the global namespace clean) - Avoid unexplained "magic numbers". Prefer named constants (`constexpr`) or C-style `#define` constants for repeated numbers that have the same meaning - Include `"wled.h"` as the primary project header where needed -- **Float-to-unsigned conversion is undefined behavior when the value is out of range.** Converting a negative `float` directly to an unsigned integer type (`uint8_t`, `uint16_t`, …) is UB per the C++ standard — the Xtensa (ESP32) toolchain may silently wrap, but RISC-V (ESP32-C3/C6) can produce different results due to clamping. Cast through a signed integer first: +- **Float-to-unsigned conversion is undefined behavior when the value is out of range.** Converting a negative `float` directly to an unsigned integer type (`uint8_t`, `uint16_t`, …) is UB per the C++ standard — the Xtensa (ESP32) toolchain may silently wrap, but RISC-V (ESP32-C3/C5/C6/P4) can produce different results due to clamping. Cast through a signed integer first: ```cpp // Undefined behavior — avoid: uint8_t angle = 40.74f * atan2f(dy, dx); // negative float → uint8_t is UB From 2226b845a19748f6306646f2e65b6653822486bf Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 6 Apr 2026 01:39:11 +0200 Subject: [PATCH 07/48] instruction updates based on review feedback --- .github/copilot-instructions.md | 4 ++-- .github/web.instructions.md | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index bcf783aed6..fd4669f0af 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -38,7 +38,7 @@ For detailed build timeouts, development workflows, troubleshooting, and validat - New custom effects can be added into the user_fx usermod. Read the [user_fx documentation](https://github.com/wled/WLED/blob/main/usermods/user_fx/README.md) for guidance. - Other usermods may be based on the [EXAMPLE usermod](https://github.com/wled/WLED/tree/main/usermods/EXAMPLE). Never edit the example, always create a copy! - New usermod IDs can be added into [wled00/const.h](https://github.com/wled/WLED/blob/746df240119b585d8c8fa85e6aac7ed707947ea0/wled00/const.h#L160). - - to activate a usermod, a custom build configuration should be used. Add the usermod name to ``custom_usermods``. + - To activate a usermod, a custom build configuration should be used. Add the usermod name to ``custom_usermods``. ## Project Structure Overview @@ -119,7 +119,7 @@ Using AI-generated code can hide the source of the inspiration / knowledge / sou - Document attribution of inspiration / knowledge / sources used in the code, e.g. link to GitHub repositories or other websites describing the principles / algorithms used. - When a larger block of code is generated by an AI tool, mark it with an `// AI: below section was generated by an AI` comment (see C++ guidelines). - Every non-trivial AI-generated function should have a brief comment describing what it does. Explain parameters when their names alone are not self-explanatory. -- AI-generated code must be well documented; comment-to-code ratio > 15% is expected. Do not rephrase source code, but explain the concepts/logic behind the code. +- AI-generated code must be well documented with meaningful comments that explain intent, assumptions, and non-obvious logic. Do not rephrase source code; explain concepts and reasoning. ### Pull Request Expectations diff --git a/.github/web.instructions.md b/.github/web.instructions.md index 2518b8dd47..9e59c8ebe6 100644 --- a/.github/web.instructions.md +++ b/.github/web.instructions.md @@ -21,6 +21,8 @@ applyTo: "wled00/data/**" - `*.css` — stylesheets (inlined during build) - `common.js` — helper functions +*Reuse shared helpers from `common.js` whenever possible** instead of duplicating utilities in page-local scripts. + ## Build Integration Files in this directory are processed by `tools/cdata.js` into generated headers From 1a7457d9490f3a1458d0c8cc93edc77e1b964890 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 6 Apr 2026 01:53:10 +0200 Subject: [PATCH 08/48] instruction tweaks --- .github/copilot-instructions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index fd4669f0af..ce61d7ea7d 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -37,7 +37,7 @@ For detailed build timeouts, development workflows, troubleshooting, and validat - New custom effects can be added into the user_fx usermod. Read the [user_fx documentation](https://github.com/wled/WLED/blob/main/usermods/user_fx/README.md) for guidance. - Other usermods may be based on the [EXAMPLE usermod](https://github.com/wled/WLED/tree/main/usermods/EXAMPLE). Never edit the example, always create a copy! - - New usermod IDs can be added into [wled00/const.h](https://github.com/wled/WLED/blob/746df240119b585d8c8fa85e6aac7ed707947ea0/wled00/const.h#L160). + - New usermod IDs can be added into [wled00/const.h](https://github.com/wled/WLED/blob/main/wled00/const.h#L160). - To activate a usermod, a custom build configuration should be used. Add the usermod name to ``custom_usermods``. ## Project Structure Overview @@ -57,7 +57,7 @@ main # Main development trunk (daily/nightly) 17.0.0-dev ``` -- ``main``: development trunk (daily/nightly) 17.0.0-dev +- ``main``: development trunk (daily/nightly) - ``V5`` and ``V5-C6``: code rework for esp-idf 5.5.x (unstable) - branched from ``main``. - ``0_15_x``: bugfixing / maintenance for release 0.15.x From f408bb406196d64209c862cf3adbb66ece8c8e7b Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 6 Apr 2026 02:03:05 +0200 Subject: [PATCH 09/48] typo --- .github/web.instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/web.instructions.md b/.github/web.instructions.md index 9e59c8ebe6..87c9b74531 100644 --- a/.github/web.instructions.md +++ b/.github/web.instructions.md @@ -21,7 +21,7 @@ applyTo: "wled00/data/**" - `*.css` — stylesheets (inlined during build) - `common.js` — helper functions -*Reuse shared helpers from `common.js` whenever possible** instead of duplicating utilities in page-local scripts. +**Reuse shared helpers from `common.js` whenever possible** instead of duplicating utilities in page-local scripts. ## Build Integration From cf8411f1f790464ad5aca8456562c18e19d078d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Mon, 6 Apr 2026 13:29:06 +0200 Subject: [PATCH 10/48] Include js_*.h instructions for header files These files are auto-generated, too --- .github/agent-build.instructions.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/agent-build.instructions.md b/.github/agent-build.instructions.md index 74dac60522..26296a8649 100644 --- a/.github/agent-build.instructions.md +++ b/.github/agent-build.instructions.md @@ -11,7 +11,7 @@ Use these timeout values when running builds: | Command | Typical Time | Minimum Timeout | Notes | |---|---|---|---| -| `npm run build` | ~3 s | 30 s | Web UI → `wled00/html_*.h` headers | +| `npm run build` | ~3 s | 30 s | Web UI → `wled00/html_*.h` `wled00/js_*.h` headers | | `npm test` | ~40 s | 2 min | Validates build system | | `npm run dev` | continuous | — | Watch mode, auto-rebuilds on changes | | `pio run -e ` | 15–20 min | 30 min | First build downloads toolchains; subsequent builds are faster | @@ -23,13 +23,13 @@ Use these timeout values when running builds: ### Web UI Changes 1. Edit files in `wled00/data/` -2. Run `npm run build` to regenerate `wled00/html_*.h` headers +2. Run `npm run build` to regenerate `wled00/html_*.h` `wled00/js_*.h` headers 3. Test with local HTTP server (see Manual Testing below) 4. Run `npm test` to validate ### Firmware Changes -1. Edit files in `wled00/` (but **never** `html_*.h` files) +1. Edit files in `wled00/` (but **never** `html_*.h` and `js_*.h` files) 2. Ensure web UI is built first: `npm run build` 3. Build firmware: `pio run -e esp32dev` (set timeout ≥ 30 min) 4. Flash to device: `pio run -e [target] --target upload` @@ -85,7 +85,7 @@ Test these scenarios after every web UI change: ### Recovery Steps - **Force web UI rebuild**: `npm run build -- -f` -- **Clear generated files**: `rm -f wled00/html_*.h` then `npm run build` +- **Clear generated files**: `rm -f wled00/html_*.h wled00/js_*.h` then `npm run build` - **Clean PlatformIO cache**: `pio run --target clean` - **Reinstall Node deps**: `rm -rf node_modules && npm ci` @@ -106,7 +106,8 @@ Match this workflow in local development to catch failures before pushing. ## Important Reminders -- **Never edit or commit** `wled00/html_*.h` — auto-generated from `wled00/data/` +- Always **commit source code** +- **Never edit or commit** `wled00/html_*.h` and `wled00/js_*.h` — auto-generated from `wled00/data/` - Web UI rebuild is part of the PlatformIO firmware compilation pipeline - Common environments: `nodemcuv2`, `esp32dev`, `esp8266_2m`, `esp32c3dev`, `esp32s3dev_8MB_opi` - List all PlatformIO targets: `pio run --list-targets` From ec8c59c1bdc71535eeb501301dc76b927215c329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Mon, 6 Apr 2026 13:43:03 +0200 Subject: [PATCH 11/48] Update memory management recommendations in cpp.instructions.md Emphasize the importance of avoiding heap fragmentation and suggest strategies for memory allocation. --- .github/cpp.instructions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/cpp.instructions.md b/.github/cpp.instructions.md index 0c6b4cf50e..44a07ee86d 100644 --- a/.github/cpp.instructions.md +++ b/.github/cpp.instructions.md @@ -95,6 +95,7 @@ uint8_t gammaCorrect(uint8_t value, float gamma); - **Larger buffers** (LED data, JSON documents) should use PSRAM when available and technically feasible - **Hot-path**: some data should stay in DRAM or IRAM for performance reasons +- Heap fragmentation can lead to crashes, even when the overall amount of availeable heap is still large. Avoid frequent `d_malloc` and `d_free` inside a function, especially for small sizes. Allocate buffers early, and try to re-use them. Instead of incrementally appending to a `String`, reserve the expected max buffer upfront by using the `reserve()` method. - Memory efficiency matters, but is less critical on boards with PSRAM ## `const` and `constexpr` From 88075840cc59bde7d1100e6eac1543afc51ca2de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Mon, 6 Apr 2026 13:44:28 +0200 Subject: [PATCH 12/48] Update .github/agent-build.instructions.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .github/agent-build.instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/agent-build.instructions.md b/.github/agent-build.instructions.md index 26296a8649..7f3a9b39d8 100644 --- a/.github/agent-build.instructions.md +++ b/.github/agent-build.instructions.md @@ -86,7 +86,7 @@ Test these scenarios after every web UI change: - **Force web UI rebuild**: `npm run build -- -f` - **Clear generated files**: `rm -f wled00/html_*.h wled00/js_*.h` then `npm run build` -- **Clean PlatformIO cache**: `pio run --target clean` +- **Clean PlatformIO build artifacts**: `pio run --target clean` - **Reinstall Node deps**: `rm -rf node_modules && npm ci` ## CI/CD Validation From dc7267e51cfb0a333613848a5b156061ca00f31c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Mon, 6 Apr 2026 14:20:43 +0200 Subject: [PATCH 13/48] Update memory management guidelines in cpp.instructions.md Clarify heap fragmentation concerns and provide guidelines for memory allocation. --- .github/cpp.instructions.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/cpp.instructions.md b/.github/cpp.instructions.md index 44a07ee86d..fe960cc31b 100644 --- a/.github/cpp.instructions.md +++ b/.github/cpp.instructions.md @@ -95,7 +95,14 @@ uint8_t gammaCorrect(uint8_t value, float gamma); - **Larger buffers** (LED data, JSON documents) should use PSRAM when available and technically feasible - **Hot-path**: some data should stay in DRAM or IRAM for performance reasons -- Heap fragmentation can lead to crashes, even when the overall amount of availeable heap is still large. Avoid frequent `d_malloc` and `d_free` inside a function, especially for small sizes. Allocate buffers early, and try to re-use them. Instead of incrementally appending to a `String`, reserve the expected max buffer upfront by using the `reserve()` method. +- Heap fragmentation is a concern + + - Fragmentation can lead to crashes, even when the overall amount of availeable heap is still large. + - Avoid frequent `d_malloc` and `d_free` inside a function, especially for small sizes. + - Avoid frequent creation / destruction of objects. + + - Allocate buffers early, and try to re-use them. + - Instead of incrementally appending to a `String`, reserve the expected max buffer upfront by using the `reserve()` method. - Memory efficiency matters, but is less critical on boards with PSRAM ## `const` and `constexpr` From ad75fa2786466f8803c29e9c7ff16aaaf9d17040 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 6 Apr 2026 15:14:38 +0200 Subject: [PATCH 14/48] a few more examples ... and making some "actionable decision rules" visible to AI for review --- .github/cpp.instructions.md | 38 ++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/.github/cpp.instructions.md b/.github/cpp.instructions.md index fe960cc31b..74f846a628 100644 --- a/.github/cpp.instructions.md +++ b/.github/cpp.instructions.md @@ -18,13 +18,13 @@ See also: [CONTRIBUTING.md](../CONTRIBUTING.md) for general style guidelines tha - Space between keyword and parenthesis: `if (...)`, `for (...)`. No space between function name and parenthesis: `doStuff(a)` - No enforced line-length limit; wrap when a line exceeds your editor width - ## Naming - **camelCase** for functions and variables: `setValuesFromMainSeg()`, `effectCurrent` - **PascalCase** for classes and structs: `PinManagerClass`, `BusConfig` - **UPPER_CASE** for macros and constants: `WLED_MAX_USERMODS`, `DEFAULT_CLIENT_SSID` + ## Header Guards Most headers use `#ifndef` / `#define` guards. Some newer headers add `#pragma once` before the guard: @@ -86,25 +86,47 @@ uint8_t gammaCorrect(uint8_t value, float gamma); - Use `const char*` for temporary/parsed strings - Avoid `String` (Arduino heap-allocated string) in hot paths; acceptable in config/setup code +```cpp + DEBUG_PRINTLN(F("WS client connected.")); // string stays in flash, not RAM + DEBUG_PRINTF_P(PSTR("initE: Ignoring attempt for invalid ethernetType (%d)\n"), ethernetType); // printf format string stays in flash +``` + ## Memory - **PSRAM-aware allocation**: use `d_malloc()` (prefer DRAM), `p_malloc()` (prefer PSRAM) from `fcn_declare.h` - **Avoid Variable Length Arrays (VLAs)**: FreeRTOS task stacks are typically 2–8 KB. A runtime-sized VLA can silently exhaust the stack. Use fixed-size arrays or heap allocation (`d_malloc` / `p_malloc`). Any VLA must be explicitly justified in source or PR. - GCC/Clang support VLAs as an extension (they are not part of the C++ standard), so they look like a legitimate feature — but they are allocated on the stack at runtime. On ESP32/ESP8266, a VLA whose size depends on a runtime parameter (segment dimensions, pixel counts, etc.) can silently exhaust the stack and cause the program to behave in unexpected ways or crash. + - GCC/Clang support VLAs as an extension (they are not part of the C++ standard), so they look like a legitimate feature — but they are allocated on the stack at runtime. On ESP32/ESP8266, a VLA whose size depends on a runtime parameter (segment dimensions, pixel counts, etc.) can silently exhaust the stack and cause the program to behave in unexpected ways or crash. - **Larger buffers** (LED data, JSON documents) should use PSRAM when available and technically feasible - **Hot-path**: some data should stay in DRAM or IRAM for performance reasons -- Heap fragmentation is a concern +- Heap fragmentation is a concern: - - Fragmentation can lead to crashes, even when the overall amount of availeable heap is still large. + - Fragmentation can lead to crashes, even when the overall amount of availeable heap is still good. (The C++ runtime doesn't do any "garbage collection") + - Avoid frequent `d_malloc` and `d_free` inside a function, especially for small sizes. - Avoid frequent creation / destruction of objects. - - Allocate buffers early, and try to re-use them. - Instead of incrementally appending to a `String`, reserve the expected max buffer upfront by using the `reserve()` method. - Memory efficiency matters, but is less critical on boards with PSRAM + +```cpp + String result; + result.reserve(65); // pre-allocate to avoid realloc fragmentation +``` + +```cpp + // prefer DRAM; falls back gracefully and enforces MIN_HEAP_SIZE guard + _ledsDirty = (byte*) d_malloc(getBitArrayBytes(_len)); +``` + +```cpp + _mode.reserve(_modeCount); // allocate memory to prevent initial fragmentation (does not increase size()) + _modeData.reserve(_modeCount); // allocate memory to prevent initial fragmentation (does not increase size() +``` + + ## `const` and `constexpr` Add `const` to cached locals in hot-path code (helps the compiler keep values in registers). Pass and store objects by `const&` to avoid copies in loops. @@ -168,6 +190,12 @@ static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit"); #error "WLED_MAX_BUSSES exceeds hard limit" #endif ``` + +```cpp + // using static_assert() to validate enumerated types (zero cost at runtime) + static_assert(0u == static_cast(PinOwner::None), + "PinOwner::None must be zero, so default array initialization works as expected"); +``` Prefer `constexpr` over `#define` for typed constants (scope-safe, debuggable). Use `static_assert` instead of `#if … #error` for compile-time validation. From b598f850fff686d2fecedaea1242f619c1f86cf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Mon, 6 Apr 2026 15:36:09 +0200 Subject: [PATCH 15/48] Update cpp.instructions.md with memory management details Clarify memory management practices and heap fragmentation concerns. Added some more examples, reformatted markup. --- .github/cpp.instructions.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/cpp.instructions.md b/.github/cpp.instructions.md index 74f846a628..b7b55dad31 100644 --- a/.github/cpp.instructions.md +++ b/.github/cpp.instructions.md @@ -96,21 +96,22 @@ uint8_t gammaCorrect(uint8_t value, float gamma); - **PSRAM-aware allocation**: use `d_malloc()` (prefer DRAM), `p_malloc()` (prefer PSRAM) from `fcn_declare.h` - **Avoid Variable Length Arrays (VLAs)**: FreeRTOS task stacks are typically 2–8 KB. A runtime-sized VLA can silently exhaust the stack. Use fixed-size arrays or heap allocation (`d_malloc` / `p_malloc`). Any VLA must be explicitly justified in source or PR. - - GCC/Clang support VLAs as an extension (they are not part of the C++ standard), so they look like a legitimate feature — but they are allocated on the stack at runtime. On ESP32/ESP8266, a VLA whose size depends on a runtime parameter (segment dimensions, pixel counts, etc.) can silently exhaust the stack and cause the program to behave in unexpected ways or crash. +GCC/Clang support VLAs as an extension (they are not part of the C++ standard), so they look like a legitimate feature — but they are allocated on the stack at runtime. On ESP32/ESP8266, a VLA whose size depends on a runtime parameter (segment dimensions, pixel counts, etc.) can silently exhaust the stack and cause the program to behave in unexpected ways or crash. - **Larger buffers** (LED data, JSON documents) should use PSRAM when available and technically feasible - **Hot-path**: some data should stay in DRAM or IRAM for performance reasons -- Heap fragmentation is a concern: - - - Fragmentation can lead to crashes, even when the overall amount of availeable heap is still good. (The C++ runtime doesn't do any "garbage collection") - - - Avoid frequent `d_malloc` and `d_free` inside a function, especially for small sizes. - - Avoid frequent creation / destruction of objects. - - Allocate buffers early, and try to re-use them. - - Instead of incrementally appending to a `String`, reserve the expected max buffer upfront by using the `reserve()` method. - Memory efficiency matters, but is less critical on boards with PSRAM +Heap fragmentation is a concern: + + - Fragmentation can lead to crashes, even when the overall amount of availeable heap is still good. The C++ runtime doesn't do any "garbage collection". + + - Avoid frequent `d_malloc` and `d_free` inside a function, especially for small sizes. + - Avoid frequent creation / destruction of objects. + - Allocate buffers early, and try to re-use them. + - Instead of incrementally appending to a `String`, reserve the expected max buffer upfront by using the `reserve()` method. + ```cpp String result; result.reserve(65); // pre-allocate to avoid realloc fragmentation @@ -125,8 +126,8 @@ uint8_t gammaCorrect(uint8_t value, float gamma); _mode.reserve(_modeCount); // allocate memory to prevent initial fragmentation (does not increase size()) _modeData.reserve(_modeCount); // allocate memory to prevent initial fragmentation (does not increase size() ``` - + ## `const` and `constexpr` Add `const` to cached locals in hot-path code (helps the compiler keep values in registers). Pass and store objects by `const&` to avoid copies in loops. From 32e960b202c68f6f8a4d315cba18b2c0e9e392aa Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 6 Apr 2026 16:23:40 +0200 Subject: [PATCH 16/48] small clarifications --- .github/copilot-instructions.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index ce61d7ea7d..d1edf30463 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -27,7 +27,7 @@ Always reference these instructions first and fallback to search or bash command -**Always run `npm ci; npm run build` before `pio run`.** The web UI build generates `wled00/html_*.h` header files required by firmware compilation. +**Always run `npm ci; npm run build` before `pio run`.** The web UI build generates required `wled00/html_*.h` and `wled00/js_*.h` headers for firmware compilation. **Build firmware to validate code changes**: `pio run -e esp32dev` — must succeed, never skip this step. Common firmware environments: `nodemcuv2`, `esp32dev`, `esp8266_2m`, `esp32c3dev`, `esp32s3dev_8MB_opi` @@ -112,6 +112,7 @@ tools/ # Build tools (Node.js), partition files, and generic uti - **Provide references** when making analyses or recommendations. Base them on the correct branch or PR. - **Highlight user-visible breaking changes and ripple effects**. Ask for confirmation that these were introduced intentionally. - **Unused / dead code must be justified or removed**. This helps to keep the codebase clean, maintainable and readable. +- **C++ formatting available**: `clang-format` is installed but not in CI - No automated linting is configured — match existing code style in files you edit. See `cpp.instructions.md` and `web.instructions.md` for language-specific conventions, and `cicd.instructions.md` for GitHub Actions workflows. ### Attribution for AI-generated code From 8a657c5b7111d1ef701822d916650a095e6e2039 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 6 Apr 2026 16:40:49 +0200 Subject: [PATCH 17/48] readability improvements collapse tl;dr section into 4 lines add bullets for some lines that were concatenated accidentially. --- .github/copilot-instructions.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index d1edf30463..a1a3ff35c5 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -27,9 +27,9 @@ Always reference these instructions first and fallback to search or bash command -**Always run `npm ci; npm run build` before `pio run`.** The web UI build generates required `wled00/html_*.h` and `wled00/js_*.h` headers for firmware compilation. -**Build firmware to validate code changes**: `pio run -e esp32dev` — must succeed, never skip this step. -Common firmware environments: `nodemcuv2`, `esp32dev`, `esp8266_2m`, `esp32c3dev`, `esp32s3dev_8MB_opi` +- **Always run `npm ci; npm run build` before `pio run`.** The web UI build generates required `wled00/html_*.h` and `wled00/js_*.h` headers for firmware compilation. +- **Build firmware to validate code changes**: `pio run -e esp32dev` — must succeed, never skip this step. +- Common firmware environments: `nodemcuv2`, `esp32dev`, `esp8266_2m`, `esp32c3dev`, `esp32s3dev_8MB_opi` For detailed build timeouts, development workflows, troubleshooting, and validation steps, see [agent-build.instructions.md](agent-build.instructions.md). @@ -64,13 +64,10 @@ main # Main development trunk (daily/nightly) 17.0.0-dev ### Repository Structure tl;dr: -* Firmware source: `wled00/` (C++). -* Build targets: `platformio.ini`. -* Web UI source: `wled00/data/`. -* Auto-generated headers: `wled00/html_*.h` — **never edit or commit**. -* ArduinoJSON + AsyncJSON: `wled00/src/dependencies/json` +* Firmware source: `wled00/` (C++). Web UI source: `wled00/data/`. Build targets: `platformio.ini`. +* Auto-generated headers: `wled00/html_*.h` and `wled00/js_*.h` — **never edit or commit**. +* ArduinoJSON + AsyncJSON: `wled00/src/dependencies/json`. CI/CD: `.github/workflows/`. * Usermods: `usermods/` (C++, with individual library.json). -* CI/CD: `.github/workflows/`. Detailed overview: @@ -113,7 +110,9 @@ tools/ # Build tools (Node.js), partition files, and generic uti - **Highlight user-visible breaking changes and ripple effects**. Ask for confirmation that these were introduced intentionally. - **Unused / dead code must be justified or removed**. This helps to keep the codebase clean, maintainable and readable. - **C++ formatting available**: `clang-format` is installed but not in CI -- No automated linting is configured — match existing code style in files you edit. See `cpp.instructions.md` and `web.instructions.md` for language-specific conventions, and `cicd.instructions.md` for GitHub Actions workflows. +- No automated linting is configured — match existing code style in files you edit. + +See `cpp.instructions.md` and `web.instructions.md` for language-specific conventions, and `cicd.instructions.md` for GitHub Actions workflows. ### Attribution for AI-generated code Using AI-generated code can hide the source of the inspiration / knowledge / sources it used. From 2d60a2d6f39218819d21865831f2d5b98644cfed Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 6 Apr 2026 16:46:15 +0200 Subject: [PATCH 18/48] minor fix --- .github/copilot-instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index a1a3ff35c5..0c5a5fd89e 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -102,7 +102,7 @@ tools/ # Build tools (Node.js), partition files, and generic uti - **Repository language is English.** Suggest translations for non-English content. - **Use VS Code with PlatformIO extension** for best development experience. -- **Never edit or commit** `wled00/html_*.h` — auto-generated from `wled00/data/`. +- **Never edit or commit** `wled00/html_*.h` and `wled00/js_*.h` — auto-generated from `wled00/data/`. - If updating Web UI files in `wled00/data/`, **make use of common functions in `wled00/data/common.js` whenever possible**. - **When unsure, say so.** Gather more information rather than guessing. - **Acknowledge good patterns** when you see them. Summarize good practices as part of your review - positive feedback always helps. From 41424b3d47b3e4782a6c7f48e0a527a9f470c977 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 6 Apr 2026 16:57:17 +0200 Subject: [PATCH 19/48] minor fixes --- .github/cpp.instructions.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/cpp.instructions.md b/.github/cpp.instructions.md index b7b55dad31..e62b0d2748 100644 --- a/.github/cpp.instructions.md +++ b/.github/cpp.instructions.md @@ -88,7 +88,7 @@ uint8_t gammaCorrect(uint8_t value, float gamma); ```cpp DEBUG_PRINTLN(F("WS client connected.")); // string stays in flash, not RAM - DEBUG_PRINTF_P(PSTR("initE: Ignoring attempt for invalid ethernetType (%d)\n"), ethernetType); // printf format string stays in flash + DEBUG_PRINTF_P(PSTR("initE: Ignoring attempt for invalid ethernetType (%d)\n"), ethernetType); // format string stays in flash ``` ## Memory @@ -104,7 +104,7 @@ GCC/Clang support VLAs as an extension (they are not part of the C++ standard), Heap fragmentation is a concern: - - Fragmentation can lead to crashes, even when the overall amount of availeable heap is still good. The C++ runtime doesn't do any "garbage collection". + - Fragmentation can lead to crashes, even when the overall amount of available heap is still good. The C++ runtime doesn't do any "garbage collection". - Avoid frequent `d_malloc` and `d_free` inside a function, especially for small sizes. - Avoid frequent creation / destruction of objects. @@ -123,8 +123,8 @@ Heap fragmentation is a concern: ``` ```cpp - _mode.reserve(_modeCount); // allocate memory to prevent initial fragmentation (does not increase size()) - _modeData.reserve(_modeCount); // allocate memory to prevent initial fragmentation (does not increase size() + _mode.reserve(_modeCount); // allocate memory to prevent initial fragmentation - does not increase size() + _modeData.reserve(_modeCount); // allocate memory to prevent initial fragmentation - does not increase size() ``` From f264d7e48a7328f7969efc446851f8c2779aedef Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 6 Apr 2026 17:27:11 +0200 Subject: [PATCH 20/48] remove dangling reference the files does not (yet) exist. --- .github/cpp.instructions.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/cpp.instructions.md b/.github/cpp.instructions.md index e62b0d2748..761ee60a22 100644 --- a/.github/cpp.instructions.md +++ b/.github/cpp.instructions.md @@ -444,7 +444,7 @@ On ESP32, `delay(ms)` calls `vTaskDelay(ms / portTICK_PERIOD_MS)`, which **suspe In arduino-esp32, `yield()` calls `vTaskDelay(0)`, which only switches to tasks at equal or higher priority — the IDLE task (priority 0) is never reached. **Do not use `yield()` to pace ESP32 tasks or assume it feeds any watchdog**. -**Custom `xTaskCreate()` tasks must call `delay(1)` in their loop, not `yield()`.** Without a real blocking call, the IDLE task is starved. The IDLE watchdog panic is the first visible symptom — but the damage starts earlier: deleted task memory leaks, software timers stop firing, light sleep is disabled, and Wi-Fi/BT idle hooks don't run. See `esp-idf.instructions.md` for a full explanation of what IDLE does. Structure custom tasks like this: +**Custom `xTaskCreate()` tasks must call `delay(1)` in their loop, not `yield()`.** Without a real blocking call, the IDLE task is starved. The IDLE watchdog panic is the first visible symptom — but the damage starts earlier: deleted task memory leaks, software timers stop firing, light sleep is disabled, and Wi-Fi/BT idle hooks don't run. Structure custom tasks like this: ```cpp // WRONG — IDLE task is never scheduled; yield() does not feed the idle task watchdog. @@ -474,6 +474,7 @@ Prefer blocking FreeRTOS primitives (`xQueueReceive`, `ulTaskNotifyTake`, `vTask - If possible, use `static` for local (C-style) variables and functions (keeps the global namespace clean) - Avoid unexplained "magic numbers". Prefer named constants (`constexpr`) or C-style `#define` constants for repeated numbers that have the same meaning - Include `"wled.h"` as the primary project header where needed + - **Float-to-unsigned conversion is undefined behavior when the value is out of range.** Converting a negative `float` directly to an unsigned integer type (`uint8_t`, `uint16_t`, …) is UB per the C++ standard — the Xtensa (ESP32) toolchain may silently wrap, but RISC-V (ESP32-C3/C5/C6/P4) can produce different results due to clamping. Cast through a signed integer first: ```cpp // Undefined behavior — avoid: From 9817d6e8156ffc0fd6c8678e47bfe180ff47ab30 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 6 Apr 2026 17:32:26 +0200 Subject: [PATCH 21/48] improve inconsistent instructions for npm run build --- .github/copilot-instructions.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 0c5a5fd89e..5b7cbddc61 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -27,7 +27,8 @@ Always reference these instructions first and fallback to search or bash command -- **Always run `npm ci; npm run build` before `pio run`.** The web UI build generates required `wled00/html_*.h` and `wled00/js_*.h` headers for firmware compilation. +- **Always run `npm run build` before any `pio run`** (and run `npm ci` first on fresh clones or when lockfile/dependencies change). +- The web UI build generates required `wled00/html_*.h` and `wled00/js_*.h` headers for firmware compilation. - **Build firmware to validate code changes**: `pio run -e esp32dev` — must succeed, never skip this step. - Common firmware environments: `nodemcuv2`, `esp32dev`, `esp8266_2m`, `esp32c3dev`, `esp32s3dev_8MB_opi` From b68b9baa3e393e18128fbb4a5ca9d5b61022f953 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 6 Apr 2026 17:56:29 +0200 Subject: [PATCH 22/48] small clarificsstions based on nitpick recommendations. --- .github/copilot-instructions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 5b7cbddc61..c5d3534af7 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -39,7 +39,7 @@ For detailed build timeouts, development workflows, troubleshooting, and validat - New custom effects can be added into the user_fx usermod. Read the [user_fx documentation](https://github.com/wled/WLED/blob/main/usermods/user_fx/README.md) for guidance. - Other usermods may be based on the [EXAMPLE usermod](https://github.com/wled/WLED/tree/main/usermods/EXAMPLE). Never edit the example, always create a copy! - New usermod IDs can be added into [wled00/const.h](https://github.com/wled/WLED/blob/main/wled00/const.h#L160). - - To activate a usermod, a custom build configuration should be used. Add the usermod name to ``custom_usermods``. + - To activate a usermod, a custom build configuration should be used. Add the usermod name to `custom_usermods`. ## Project Structure Overview @@ -67,7 +67,7 @@ main # Main development trunk (daily/nightly) 17.0.0-dev tl;dr: * Firmware source: `wled00/` (C++). Web UI source: `wled00/data/`. Build targets: `platformio.ini`. * Auto-generated headers: `wled00/html_*.h` and `wled00/js_*.h` — **never edit or commit**. -* ArduinoJSON + AsyncJSON: `wled00/src/dependencies/json`. CI/CD: `.github/workflows/`. +* ArduinoJSON + AsyncJSON: `wled00/src/dependencies/json` (included via `wled.h`). CI/CD: `.github/workflows/`. * Usermods: `usermods/` (C++, with individual library.json). From 6cae9c661101a0cde96dc2806445a66329d1e437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Tue, 7 Apr 2026 17:18:30 +0200 Subject: [PATCH 23/48] Update FreeRTOS task management instructions Clarify usage of delay() and yield() in FreeRTOS tasks, emphasizing the differences between ESP32 and ESP8266. Update instructions on task management and watchdog behavior. --- .github/cpp.instructions.md | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/.github/cpp.instructions.md b/.github/cpp.instructions.md index 761ee60a22..1b0a8329c3 100644 --- a/.github/cpp.instructions.md +++ b/.github/cpp.instructions.md @@ -435,17 +435,22 @@ if (lastKelvin != kelvin) { ### `delay()` vs `yield()` in ESP32 Tasks + +* On ESP32, `delay(ms)` calls `vTaskDelay(ms / portTICK_PERIOD_MS)`, which **suspends only the calling task**. The FreeRTOS scheduler immediately runs all other ready tasks. +* The Arduino `loop()` function runs inside `loopTask`. Calling `delay()` there does *not* block the network stack, audio FFT, LED DMA, nor any other FreeRTOS task. +* This differs from ESP8266, where `delay()` stalls the entire system unless `yield()` was called inside. + -On ESP32, `delay(ms)` calls `vTaskDelay(ms / portTICK_PERIOD_MS)`, which **suspends only the calling task**. The FreeRTOS scheduler immediately runs all other ready tasks. This differs from ESP8266, where `delay()` stalled the entire system unless `yield()` was called inside. - -**`delay()` in `loopTask` is allowed.** The Arduino `loop()` function runs inside `loopTask`. Calling `delay()` there does not block the network stack, audio FFT, LED DMA, or any other FreeRTOS task. - - -In arduino-esp32, `yield()` calls `vTaskDelay(0)`, which only switches to tasks at equal or higher priority — the IDLE task (priority 0) is never reached. -**Do not use `yield()` to pace ESP32 tasks or assume it feeds any watchdog**. +- On ESP32, `delay()` is generally allowed, as it helps to efficiently manage CPU usage of all tasks. +- On ESP8266, only use `delay()` and `yield()` in the main `loop()` context. If not sure, protect with `if (can_yield()) ...`. +- Do *not* use `delay()` in effects (FX.cpp) or in the hot pixel path. +- `delay()` on ``busses`` level is allowed, it might be needed to achieve exact timing in LED drivers. -**Custom `xTaskCreate()` tasks must call `delay(1)` in their loop, not `yield()`.** Without a real blocking call, the IDLE task is starved. The IDLE watchdog panic is the first visible symptom — but the damage starts earlier: deleted task memory leaks, software timers stop firing, light sleep is disabled, and Wi-Fi/BT idle hooks don't run. Structure custom tasks like this: +#### IDLE Watchdog and Custom Tasks on ESP32 +- In arduino-esp32, `yield()` calls `vTaskDelay(0)`, which only switches to tasks at equal or higher priority — the IDLE task (priority 0) is never reached. +- **Do not use `yield()` to pace ESP32 tasks or assume it feeds any watchdog**. +- **Custom `xTaskCreate()` tasks must call `delay(1)` in their loop, not `yield()`.** Without a real blocking call, the IDLE task is starved. The IDLE watchdog panic is the first visible symptom — but the damage starts earlier: deleted task memory leaks, software timers stop firing, light sleep is disabled, and Wi-Fi/BT idle hooks don't run. Structure custom tasks like this: ```cpp // WRONG — IDLE task is never scheduled; yield() does not feed the idle task watchdog. void myTask(void*) { @@ -464,9 +469,8 @@ void myTask(void*) { } ``` -Prefer blocking FreeRTOS primitives (`xQueueReceive`, `ulTaskNotifyTake`, `vTaskDelayUntil`) over `delay(1)` polling where precise timing or event-driven behaviour is needed. - -**Watchdog note.** WLED disables the Task Watchdog by default (`WLED_WATCHDOG_TIMEOUT 0` in `wled.h`). When enabled, `esp_task_wdt_reset()` is called at the end of each `loop()` iteration. Long blocking operations inside `loop()` — such as OTA downloads or slow file I/O — must call `esp_task_wdt_reset()` periodically, or be restructured so the main loop is not blocked for longer than the configured timeout. +- Prefer blocking FreeRTOS primitives (`xQueueReceive`, `ulTaskNotifyTake`, `vTaskDelayUntil`) over `delay(1)` polling where precise timing or event-driven behaviour is needed. +- **Watchdog note.** WLED disables the Task Watchdog by default (`WLED_WATCHDOG_TIMEOUT 0` in `wled.h`). When enabled, `esp_task_wdt_reset()` is called at the end of each `loop()` iteration. Long blocking operations inside `loop()` — such as OTA downloads or slow file I/O — must call `esp_task_wdt_reset()` periodically, or be restructured so the main loop is not blocked for longer than the configured timeout. ## General From 414854abd38e553b61d1d86053296f40bca7205f Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 7 Apr 2026 17:55:11 +0200 Subject: [PATCH 24/48] minor clarifications --- .github/copilot-instructions.md | 2 +- .github/cpp.instructions.md | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index c5d3534af7..14c350fdc1 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -20,7 +20,7 @@ Always reference these instructions first and fallback to search or bash command | Command | Purpose | Typical Time | |---|---|---| -| `npm run build` | Build web UI → generates `wled00/html_*.h` headers | ~3 s | +| `npm run build` | Build web UI → generates `wled00/html_*.h` and `wled00/js_*.h` headers | ~3 s | | `npm test` | Run test suite | ~40 s | | `npm run dev` | Watch mode — auto-rebuilds web UI on file changes | — | | `pio run -e ` | Build firmware for a hardware target | 15–20 min | diff --git a/.github/cpp.instructions.md b/.github/cpp.instructions.md index 1b0a8329c3..275733772b 100644 --- a/.github/cpp.instructions.md +++ b/.github/cpp.instructions.md @@ -137,12 +137,12 @@ This pattern enables optimizations and makes intent clear to reviewers. ### `const` locals -Adding `const` to a local variable that is only assigned once is not necessary — but it **is** required when the variable is passed to a function that takes a `const` parameter (pointer or reference). In hot-path code, `const` on cached locals helps the compiler keep values in registers: - -```cpp -const uint_fast16_t cols = vWidth(); -const uint_fast16_t rows = vHeight(); -``` +* Adding `const` to a local variable that is only assigned once is optional, but *not* strictly necessary. +* In hot-path code, `const` on cached locals may help the compiler keep values in registers: + ```cpp + const uint_fast16_t cols = vWidth(); + const uint_fast16_t rows = vHeight(); + ``` ### `const` references to avoid copies From 0cdc767b27439520e2ff924f7fd32da891fad817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Tue, 7 Apr 2026 19:36:33 +0200 Subject: [PATCH 25/48] Add code style summary to agent build instructions Added basic style guidelines for C++, Web UI, and CI/CD workflows. --- .github/agent-build.instructions.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/agent-build.instructions.md b/.github/agent-build.instructions.md index 7f3a9b39d8..b25331fd69 100644 --- a/.github/agent-build.instructions.md +++ b/.github/agent-build.instructions.md @@ -20,6 +20,11 @@ Use these timeout values when running builds: ## Development Workflow +### Code Style Summary +- **C++** files in `wled00/` and `usermods/`: 2-space indentation (no tabs), camelCase functions/variables, PascalCase classes, UPPER_CASE macros. No C++ exceptions — use return codes and debug macros. +- **Web UI** files in `wled00/data`: indent HTML and JavaScript with tabs, CSS with tabs. +- **CI/CD workflows** in `.github/workflows`: 2-space indentation, descriptive `name:` on every workflow/job/step. Third-party actions must be pinned to a specific version tag — branch pins such as @main or @master are not allowed. SHA pinning recommended. + ### Web UI Changes 1. Edit files in `wled00/data/` From 5cab25c974c4fed97e6bc2b44a977b68bbfafefa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Tue, 7 Apr 2026 19:38:40 +0200 Subject: [PATCH 26/48] Fix formatting in CI/CD workflows section --- .github/agent-build.instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/agent-build.instructions.md b/.github/agent-build.instructions.md index b25331fd69..f40d45d20f 100644 --- a/.github/agent-build.instructions.md +++ b/.github/agent-build.instructions.md @@ -23,7 +23,7 @@ Use these timeout values when running builds: ### Code Style Summary - **C++** files in `wled00/` and `usermods/`: 2-space indentation (no tabs), camelCase functions/variables, PascalCase classes, UPPER_CASE macros. No C++ exceptions — use return codes and debug macros. - **Web UI** files in `wled00/data`: indent HTML and JavaScript with tabs, CSS with tabs. -- **CI/CD workflows** in `.github/workflows`: 2-space indentation, descriptive `name:` on every workflow/job/step. Third-party actions must be pinned to a specific version tag — branch pins such as @main or @master are not allowed. SHA pinning recommended. +- **CI/CD workflows** in `.github/workflows`: 2-space indentation, descriptive `name:` on every workflow/job/step. Third-party actions must be pinned to a specific version tag — branch pins such as `@main` or `@master` are not allowed. SHA pinning recommended. ### Web UI Changes From e77497203c36e4986999b4933e0bd1bcb36e8fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Tue, 7 Apr 2026 23:06:38 +0200 Subject: [PATCH 27/48] Update C++ instructions for const and constexpr usage Clarified usage of `const` and `constexpr` in C++ instructions. Improved explanations and examples for better understanding. --- .github/cpp.instructions.md | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/.github/cpp.instructions.md b/.github/cpp.instructions.md index 275733772b..3330a5859d 100644 --- a/.github/cpp.instructions.md +++ b/.github/cpp.instructions.md @@ -129,24 +129,28 @@ Heap fragmentation is a concern: ## `const` and `constexpr` -Add `const` to cached locals in hot-path code (helps the compiler keep values in registers). Pass and store objects by `const&` to avoid copies in loops. - `const` is a promise to the compiler that a value (or object) will not change - a function declared with a `const char* message` parameter is not allowed to modify the content of `message`. This pattern enables optimizations and makes intent clear to reviewers. -### `const` locals +`constexpr` allows to define constants that are *guaranteed* to be evaluated by the compiler (zero run-time costs). + + +- For function parameters that are read-only, prefer `const &` or `const`. +### `const` locals + * Adding `const` to a local variable that is only assigned once is optional, but *not* strictly necessary. -* In hot-path code, `const` on cached locals may help the compiler keep values in registers: + +* In hot-path code, `const` on cached locals may help the compiler keep values in registers. ```cpp const uint_fast16_t cols = vWidth(); const uint_fast16_t rows = vHeight(); ``` - -### `const` references to avoid copies -Pass and store objects by `const &` (or `&`) instead of copying them implicitly. This avoids constructing temporary objects on every access — especially important in loops. +### `const` references to avoid copies +- Pass objects by `const &` (or `&`) instead of copying them implicitly. +- Use `const &` (or `&`) inside loops - This avoids constructing temporary objects on every access. ```cpp @@ -155,11 +159,13 @@ Segment& sourcesegment = strip.getSegment(sourceid); // alias — avoids creatin ``` For function parameters that are read-only, prefer `const &`: - ```cpp BusManager::add(const BusConfig &bc, bool placeholder) { ``` + +- Class **Data Members:** Avoid reference data members (`T&` or `const T&`) in a class. + A reference member can outlive the object it refers to, causing **dangling reference** bugs that are hard to diagnose. Prefer value storage or use a pointer and document the expected lifetime. ### `constexpr` over `#define` @@ -444,7 +450,7 @@ if (lastKelvin != kelvin) { - On ESP32, `delay()` is generally allowed, as it helps to efficiently manage CPU usage of all tasks. - On ESP8266, only use `delay()` and `yield()` in the main `loop()` context. If not sure, protect with `if (can_yield()) ...`. - Do *not* use `delay()` in effects (FX.cpp) or in the hot pixel path. -- `delay()` on ``busses`` level is allowed, it might be needed to achieve exact timing in LED drivers. +- `delay()` on the bus level is allowed, it might be needed to achieve exact timing in LED drivers. #### IDLE Watchdog and Custom Tasks on ESP32 From ef3030a2a17815a68eb2b9e7326c2e7b4535fbd4 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 19:02:20 +0200 Subject: [PATCH 28/48] chore: move coding guidelines from .github to docs/ (#358) * Reorganized repository documentation and updated internal configuration to reference the new docs location. Documentation * Updated contributor and instruction guides to point to the relocated documentation files so links and references remain correct. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: softhack007 <91616163+softhack007@users.noreply.github.com> --- .coderabbit.yaml | 26 ++++++++++++++++---------- .github/copilot-instructions.md | 4 +++- CONTRIBUTING.md | 7 ++++++- {.github => docs}/cicd.instructions.md | 0 {.github => docs}/cpp.instructions.md | 0 {.github => docs}/web.instructions.md | 0 6 files changed, 25 insertions(+), 12 deletions(-) rename {.github => docs}/cicd.instructions.md (100%) rename {.github => docs}/cpp.instructions.md (100%) rename {.github => docs}/web.instructions.md (100%) diff --git a/.coderabbit.yaml b/.coderabbit.yaml index 7667d0d9ae..fc29ef4856 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -3,9 +3,9 @@ # CodeRabbit configuration — references existing guideline files to avoid # duplicating conventions. See: # .github/copilot-instructions.md — project overview & general rules -# .github/cpp.instructions.md — C++ coding conventions -# .github/web.instructions.md — Web UI coding conventions -# .github/cicd.instructions.md — GitHub Actions / CI-CD conventions +# docs/cpp.instructions.md — C++ coding conventions +# docs/web.instructions.md — Web UI coding conventions +# docs/cicd.instructions.md — GitHub Actions / CI-CD conventions # # NOTE: This file must be committed (tracked by git) for CodeRabbit to read # it from the repository. If it is listed in .gitignore, CodeRabbit will @@ -17,7 +17,7 @@ reviews: path_instructions: - path: "**/*.{cpp,h,hpp,ino}" instructions: > - Follow the C++ coding conventions documented in .github/cpp.instructions.md + Follow the C++ coding conventions documented in docs/cpp.instructions.md and the general project guidelines in .github/copilot-instructions.md. Key rules: 2-space indentation (no tabs), camelCase functions/variables, @@ -29,10 +29,10 @@ reviews: - path: "wled00/data/**" instructions: > - Follow the web UI conventions documented in .github/web.instructions.md. + Follow the web UI conventions documented in docs/web.instructions.md. Key rules: indent HTML and JavaScript with tabs, CSS with tabs. - Files here are built into wled00/html_*.h by tools/cdata.js — never + Files here are built into wled00/html_*.h and wled00/js_*.h by tools/cdata.js — never edit those generated headers directly. - path: "wled00/html_*.h" @@ -41,16 +41,22 @@ reviews: They must never be manually edited or committed. Flag any PR that includes changes to these files. + - path: "wled00/js_*.h" + instructions: > + These files are auto-generated from wled00/data/ by tools/cdata.js. + They must never be manually edited or committed. Flag any PR that + includes changes to these files. + - path: "usermods/**" instructions: > Usermods are community add-ons. Each usermod lives in its own directory under usermods/ and is implemented as a .cpp file with a dedicated library.json file to manage dependencies. - Follow the same C++ conventions as the core firmware (.github/cpp.instructions.md). + Follow the same C++ conventions as the core firmware (docs/cpp.instructions.md). - path: ".github/workflows/*.{yml,yaml}" instructions: > - Follow the CI/CD conventions documented in .github/cicd.instructions.md. + Follow the CI/CD conventions documented in docs/cicd.instructions.md. Key rules: 2-space indentation, descriptive name: on every workflow/job/step. Third-party actions must be pinned to a specific version tag — branch pins @@ -59,7 +65,7 @@ reviews: into run: steps — pass them through an env: variable to prevent script injection. Do not use pull_request_target unless fully justified. - - path: ".github/*.instructions.md" + - path: "**/*.instructions.md" instructions: | This file contains both AI-facing rules and human-only reference sections. Human-only sections are enclosed in `` / @@ -73,4 +79,4 @@ reviews: 2. Flag any HUMAN_ONLY section whose content has drifted from the surrounding AI-facing rules due to edits introduced in this PR. 3. If new AI-facing rules were added without updating a related HUMAN_ONLY - reference section, note this as a suggestion (not a required fix). \ No newline at end of file + reference section, note this as a suggestion (not a required fix). diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 14c350fdc1..ccc3b2cb10 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -69,6 +69,7 @@ tl;dr: * Auto-generated headers: `wled00/html_*.h` and `wled00/js_*.h` — **never edit or commit**. * ArduinoJSON + AsyncJSON: `wled00/src/dependencies/json` (included via `wled.h`). CI/CD: `.github/workflows/`. * Usermods: `usermods/` (C++, with individual library.json). +* Contributor docs: `docs/` (coding guidelines, etc). Detailed overview: @@ -95,6 +96,7 @@ pio-scripts/ # Build tools (PlatformIO) tools/ # Build tools (Node.js), partition files, and generic utilities ├── cdata.js # Web UI build script └── cdata-test.js # Test suite +docs/ # Contributor docs, coding guidelines .github/workflows/ # CI/CD pipelines ``` @@ -113,7 +115,7 @@ tools/ # Build tools (Node.js), partition files, and generic uti - **C++ formatting available**: `clang-format` is installed but not in CI - No automated linting is configured — match existing code style in files you edit. -See `cpp.instructions.md` and `web.instructions.md` for language-specific conventions, and `cicd.instructions.md` for GitHub Actions workflows. +Refer to `docs/cpp.instructions.md` and `docs/web.instructions.md` for language-specific conventions, and `docs/cicd.instructions.md` for GitHub Actions workflows. ### Attribution for AI-generated code Using AI-generated code can hide the source of the inspiration / knowledge / sources it used. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4ebee812b0..28901bd205 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -174,7 +174,12 @@ AI tools are powerful but "often wrong" - your judgment is essential! 😊 Don't stress too much about style! When in doubt, just match the style in the files you're editing. 😊 -Here are our main guidelines: +Our review bot (coderabbit) has learned lots of detailed guides and hints - it will suggest them automatically when you submit a PR for review. +If you are curious, these are the detailed guides: +* [C++ Coding](https://github.com/wled/WLED/blob/main/docs/cpp.instructions.md) +* [WebUi: HTML, JS, CSS](https://github.com/wled/WLED/blob/main/docs/web.instructions.md) + +Below are the main rules used the WLED repository: #### Indentation diff --git a/.github/cicd.instructions.md b/docs/cicd.instructions.md similarity index 100% rename from .github/cicd.instructions.md rename to docs/cicd.instructions.md diff --git a/.github/cpp.instructions.md b/docs/cpp.instructions.md similarity index 100% rename from .github/cpp.instructions.md rename to docs/cpp.instructions.md diff --git a/.github/web.instructions.md b/docs/web.instructions.md similarity index 100% rename from .github/web.instructions.md rename to docs/web.instructions.md From 1172fdaaf15af06b4dcf8a65ef19c08247b87fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Wed, 8 Apr 2026 00:30:44 +0200 Subject: [PATCH 29/48] Update cpp.instructions.md for RISC-V bit-shift guidance Clarified instructions regarding bit-shifts and division for RISC-V targets and updated right shift examples. Mention 8266 next to -C3. --- docs/cpp.instructions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/cpp.instructions.md b/docs/cpp.instructions.md index 3330a5859d..49d1fb719d 100644 --- a/docs/cpp.instructions.md +++ b/docs/cpp.instructions.md @@ -389,7 +389,7 @@ Move invariant calculations before the loop. Pre-compute reciprocals to replace ```cpp const uint_fast16_t cols = virtualWidth(); const uint_fast16_t rows = virtualHeight(); -uint_fast8_t fadeRate = (255 - rate) >> 1; +uint_fast8_t fadeRate = (255U - rate) >> 1; float mappedRate_r = 1.0f / (float(fadeRate) + 1.1f); // reciprocal — avoid division inside loop ``` @@ -408,7 +408,7 @@ return rb | wg; ### Bit Shifts Over Division (mainly for RISC-V boards) ESP32 and ESP32-S3 (Xtensa core) have a fast "integer divide" instruction, so manual shifts rarely help. -On RISC-V targets (ESP32-C3/C6/P4), prefer explicit bit-shifts for power-of-two arithmetic — the compiler does **not** always convert divisions to shifts on RISC-V at `-O2`. Always use unsigned operands; signed right-shift is implementation-defined. +On RISC-V targets (ESP32-C3/C6/P4) and ESP8266, prefer explicit bit-shifts for power-of-two arithmetic — the compiler does **not** always convert divisions to shifts on RISC-V at `-O2`. Always use unsigned operands for right shifts; signed right-shift is implementation-defined. On RISC-V-based boards (ESP32-C3, ESP32-C6, ESP32-C5) explicit shifts can be beneficial. From edba6d7ce21fc4679459fc011c9d6b53bc5cb72d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Wed, 8 Apr 2026 00:45:16 +0200 Subject: [PATCH 30/48] Fix typo in CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 28901bd205..8fe12202d6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -179,7 +179,7 @@ If you are curious, these are the detailed guides: * [C++ Coding](https://github.com/wled/WLED/blob/main/docs/cpp.instructions.md) * [WebUi: HTML, JS, CSS](https://github.com/wled/WLED/blob/main/docs/web.instructions.md) -Below are the main rules used the WLED repository: +Below are the main rules used in the WLED repository: #### Indentation From b88415d7a703d06ca68147be857c7865e161d5d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Wed, 8 Apr 2026 00:50:50 +0200 Subject: [PATCH 31/48] Update string constant usage instructions Clarify usage of F() for string constants on 8266. --- docs/cpp.instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cpp.instructions.md b/docs/cpp.instructions.md index 49d1fb719d..c9204b31a0 100644 --- a/docs/cpp.instructions.md +++ b/docs/cpp.instructions.md @@ -82,7 +82,7 @@ uint8_t gammaCorrect(uint8_t value, float gamma); ## Strings -- Use `F("string")` for string constants (stores in PROGMEM, saves RAM) +- Use `F("string")` for string constants (saves RAM on 8266) - Use `const char*` for temporary/parsed strings - Avoid `String` (Arduino heap-allocated string) in hot paths; acceptable in config/setup code From e66885101f8cc293b29a3c1cc3938af4c8d3f1a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Wed, 8 Apr 2026 00:58:29 +0200 Subject: [PATCH 32/48] Clarify usage of F() for string constants Added clarification on using F() for string constants on ESP8266 and ESP32. --- docs/cpp.instructions.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/cpp.instructions.md b/docs/cpp.instructions.md index c9204b31a0..fc98a47be9 100644 --- a/docs/cpp.instructions.md +++ b/docs/cpp.instructions.md @@ -82,9 +82,14 @@ uint8_t gammaCorrect(uint8_t value, float gamma); ## Strings -- Use `F("string")` for string constants (saves RAM on 8266) - Use `const char*` for temporary/parsed strings - Avoid `String` (Arduino heap-allocated string) in hot paths; acceptable in config/setup code +- Use `F("string")` for string constants (saves RAM on 8266) + + + On **ESP8266** this explicitly stores the string in flash (PROGMEM), saving precious RAM — every byte counts on that platform. + On **ESP32**, `PROGMEM` is a no-op and string literals already reside in flash/rodata, so `F()` yields little RAM benefit but remains harmless (it satisfies `__FlashStringHelper*` overloads that some APIs expect). + ```cpp DEBUG_PRINTLN(F("WS client connected.")); // string stays in flash, not RAM From 7fa25f4e6e98c58250d896c6f5dba023cd650281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Wed, 8 Apr 2026 01:10:09 +0200 Subject: [PATCH 33/48] Fix usermod instructions link in CONTRIBUTING.md Updated link for usermod creation instructions in the contributing guide. --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8fe12202d6..ecbd9e1ea6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,7 +11,7 @@ Here are a few suggestions to make it easier for you to contribute: ### Important Developer Infos * [Project Structure, Files and Directories](https://github.com/wled/WLED/blob/main/.github/copilot-instructions.md#project-branch--release-structure) (in our AI instructions) -* [Instructions for creating usermods](https://github.com/wled/WLED/blob/main/.github/copilot-instructions.md#adding-a-new-usermod) (in our AI instructions) +* [Instructions for creating usermods](https://github.com/wled/WLED/blob/main/.github/copilot-instructions.md#usermod-guidelines) (in our AI instructions) * KB: [Compiling WLED](https://kno.wled.ge/advanced/compiling-wled/) - slightly outdated but still helpful :-) * Arduino IDE is not supported any more. Use VSCode with the PlatformIO extension. * [Compiling in VSCode/Platformio](https://github.com/wled/WLED-Docs/issues/161) - modern way without command line or platformio.ini changes. From 1a7e275a9cf178921c029288e93d99096feba4aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Wed, 8 Apr 2026 01:11:55 +0200 Subject: [PATCH 34/48] Fix link to project structure in CONTRIBUTING.md Updated link to project structure overview in CONTRIBUTING.md. --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ecbd9e1ea6..e4575002ea 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ We'll work with you to refine your contribution, but we'll also push back if som Here are a few suggestions to make it easier for you to contribute: ### Important Developer Infos -* [Project Structure, Files and Directories](https://github.com/wled/WLED/blob/main/.github/copilot-instructions.md#project-branch--release-structure) (in our AI instructions) +* [Project Structure, Files and Directories](https://github.com/wled/WLED/blob/main/.github/copilot-instructions.md#project-structure-overview) (in our AI instructions) * [Instructions for creating usermods](https://github.com/wled/WLED/blob/main/.github/copilot-instructions.md#usermod-guidelines) (in our AI instructions) * KB: [Compiling WLED](https://kno.wled.ge/advanced/compiling-wled/) - slightly outdated but still helpful :-) * Arduino IDE is not supported any more. Use VSCode with the PlatformIO extension. From dff8f7de9cce0766822959092ab0e168b5041fad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Wed, 8 Apr 2026 15:40:09 +0200 Subject: [PATCH 35/48] move "constexpr over define" into HUMAN_ONLY tags codebase is not compliant to this rule - too much noise in reviews. --- docs/cpp.instructions.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/cpp.instructions.md b/docs/cpp.instructions.md index fc98a47be9..d0f30a663c 100644 --- a/docs/cpp.instructions.md +++ b/docs/cpp.instructions.md @@ -172,10 +172,11 @@ BusManager::add(const BusConfig &bc, bool placeholder) { - Class **Data Members:** Avoid reference data members (`T&` or `const T&`) in a class. A reference member can outlive the object it refers to, causing **dangling reference** bugs that are hard to diagnose. Prefer value storage or use a pointer and document the expected lifetime. -### `constexpr` over `#define` + +### `constexpr` over `#define` -Prefer `constexpr` for compile-time constants. Unlike `#define`, `constexpr` respects scope and type safety, keeping the global namespace clean: +- Prefer `constexpr` for compile-time constants. Unlike `#define`, `constexpr` respects scope and type safety, keeping the global namespace clean. ```cpp // Prefer: @@ -185,13 +186,14 @@ constexpr int WLED_MAX_BUSSES = WLED_MAX_DIGITAL_CHANNELS + WLED_MAX_ANALOG_CHAN // Avoid (when possible): #define TWO_CHANNEL_MASK 0x00FF00FF ``` - -Note: `#define` is still needed for conditional compilation guards (`#ifdef`), platform macros, and values that must be overridable from build flags. + ### `static_assert` over `#error` -Use `static_assert` instead of the C-style `#if … #error … #endif` pattern when validating compile-time constants. It provides a clear message and works with `constexpr` values: +- Use `static_assert` instead of the C-style `#if … #error … #endif` pattern when validating compile-time constants. It provides a clear message and works with `constexpr` values. +- `#define` and `#if ... #else ... #end` is still needed for conditional-compilation guards and build-flag-overridable values. + ```cpp // Prefer: constexpr int WLED_MAX_BUSSES = WLED_MAX_DIGITAL_CHANNELS + WLED_MAX_ANALOG_CHANNELS; @@ -208,11 +210,8 @@ static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit"); static_assert(0u == static_cast(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected"); ``` - - -Prefer `constexpr` over `#define` for typed constants (scope-safe, debuggable). Use `static_assert` instead of `#if … #error` for compile-time validation. -Exception: `#define` is required for conditional-compilation guards and build-flag-overridable values. + ### `static` and `const` class methods #### `const` member functions From 55ea4c46a36c84697f1e64655cd8840ad06de22b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Wed, 8 Apr 2026 15:48:08 +0200 Subject: [PATCH 36/48] Fix typo --- docs/cpp.instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cpp.instructions.md b/docs/cpp.instructions.md index d0f30a663c..0f348d3e6c 100644 --- a/docs/cpp.instructions.md +++ b/docs/cpp.instructions.md @@ -191,7 +191,7 @@ constexpr int WLED_MAX_BUSSES = WLED_MAX_DIGITAL_CHANNELS + WLED_MAX_ANALOG_CHAN ### `static_assert` over `#error` - Use `static_assert` instead of the C-style `#if … #error … #endif` pattern when validating compile-time constants. It provides a clear message and works with `constexpr` values. -- `#define` and `#if ... #else ... #end` is still needed for conditional-compilation guards and build-flag-overridable values. +- `#define` and `#if ... #else ... #endif` is still needed for conditional-compilation guards and build-flag-overridable values. ```cpp From 749101e133315a0bd4409ad03200d45e1b9eb2da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Wed, 8 Apr 2026 16:01:25 +0200 Subject: [PATCH 37/48] use relative links in CONTRIBUTING.md absolute links can send contributors on non-main branches to mismatched guidance. Prefer relative links so docs stay consistent with the checked-out branch. Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e4575002ea..1ba8dc3aa6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,8 +10,8 @@ We'll work with you to refine your contribution, but we'll also push back if som Here are a few suggestions to make it easier for you to contribute: ### Important Developer Infos -* [Project Structure, Files and Directories](https://github.com/wled/WLED/blob/main/.github/copilot-instructions.md#project-structure-overview) (in our AI instructions) -* [Instructions for creating usermods](https://github.com/wled/WLED/blob/main/.github/copilot-instructions.md#usermod-guidelines) (in our AI instructions) +* [Project Structure, Files and Directories](.github/copilot-instructions.md#project-structure-overview) (in our AI instructions) +* [Instructions for creating usermods](.github/copilot-instructions.md#usermod-guidelines) (in our AI instructions) * KB: [Compiling WLED](https://kno.wled.ge/advanced/compiling-wled/) - slightly outdated but still helpful :-) * Arduino IDE is not supported any more. Use VSCode with the PlatformIO extension. * [Compiling in VSCode/Platformio](https://github.com/wled/WLED-Docs/issues/161) - modern way without command line or platformio.ini changes. From 409cc81743bd9cb7889477b382b02dae1aa622a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Wed, 8 Apr 2026 16:04:02 +0200 Subject: [PATCH 38/48] Update links in CONTRIBUTING.md to relative paths --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1ba8dc3aa6..da35b98a35 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -176,8 +176,8 @@ Don't stress too much about style! When in doubt, just match the style in the fi Our review bot (coderabbit) has learned lots of detailed guides and hints - it will suggest them automatically when you submit a PR for review. If you are curious, these are the detailed guides: -* [C++ Coding](https://github.com/wled/WLED/blob/main/docs/cpp.instructions.md) -* [WebUi: HTML, JS, CSS](https://github.com/wled/WLED/blob/main/docs/web.instructions.md) +* [C++ Coding](docs/cpp.instructions.md) +* [WebUi: HTML, JS, CSS](docs/web.instructions.md) Below are the main rules used in the WLED repository: From c9854b5435ba119c185aeaa73762a2f1db8e281b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Wed, 8 Apr 2026 16:45:04 +0200 Subject: [PATCH 39/48] small restructure and some nitpick * moved "general" section to the front, renamed last section * minor styling --- docs/cpp.instructions.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/cpp.instructions.md b/docs/cpp.instructions.md index 0f348d3e6c..97bfeac437 100644 --- a/docs/cpp.instructions.md +++ b/docs/cpp.instructions.md @@ -24,6 +24,13 @@ See also: [CONTRIBUTING.md](../CONTRIBUTING.md) for general style guidelines tha - **PascalCase** for classes and structs: `PinManagerClass`, `BusConfig` - **UPPER_CASE** for macros and constants: `WLED_MAX_USERMODS`, `DEFAULT_CLIENT_SSID` +## General + +- Follow the existing style in the file you are editing +- If possible, use `static` for local (C-style) variables and functions (keeps the global namespace clean) +- Avoid unexplained "magic numbers". Prefer named constants (`constexpr`) or C-style `#define` constants for repeated numbers that have the same meaning +- Include `"wled.h"` as the primary project header where needed + ## Header Guards @@ -412,7 +419,8 @@ return rb | wg; ### Bit Shifts Over Division (mainly for RISC-V boards) ESP32 and ESP32-S3 (Xtensa core) have a fast "integer divide" instruction, so manual shifts rarely help. -On RISC-V targets (ESP32-C3/C6/P4) and ESP8266, prefer explicit bit-shifts for power-of-two arithmetic — the compiler does **not** always convert divisions to shifts on RISC-V at `-O2`. Always use unsigned operands for right shifts; signed right-shift is implementation-defined. +On RISC-V targets (ESP32-C3/C6/P4) and ESP8266, prefer explicit bit-shifts for power-of-two arithmetic — the compiler does **not** always convert divisions to shifts. +Always use unsigned operands for right shifts; signed right-shift is implementation-defined. On RISC-V-based boards (ESP32-C3, ESP32-C6, ESP32-C5) explicit shifts can be beneficial. @@ -443,7 +451,6 @@ if (lastKelvin != kelvin) { - Move frequently-called small functions to headers for inlining (e.g. `Segment::setPixelColorRaw` is in `FX.h`) - Use `static inline` for file-local helpers - ### `delay()` vs `yield()` in ESP32 Tasks * On ESP32, `delay(ms)` calls `vTaskDelay(ms / portTICK_PERIOD_MS)`, which **suspends only the calling task**. The FreeRTOS scheduler immediately runs all other ready tasks. @@ -454,7 +461,7 @@ if (lastKelvin != kelvin) { - On ESP32, `delay()` is generally allowed, as it helps to efficiently manage CPU usage of all tasks. - On ESP8266, only use `delay()` and `yield()` in the main `loop()` context. If not sure, protect with `if (can_yield()) ...`. - Do *not* use `delay()` in effects (FX.cpp) or in the hot pixel path. -- `delay()` on the bus level is allowed, it might be needed to achieve exact timing in LED drivers. +- `delay()` on the bus-level is allowed, it might be needed to achieve exact timing in LED drivers. #### IDLE Watchdog and Custom Tasks on ESP32 @@ -482,13 +489,7 @@ void myTask(void*) { - Prefer blocking FreeRTOS primitives (`xQueueReceive`, `ulTaskNotifyTake`, `vTaskDelayUntil`) over `delay(1)` polling where precise timing or event-driven behaviour is needed. - **Watchdog note.** WLED disables the Task Watchdog by default (`WLED_WATCHDOG_TIMEOUT 0` in `wled.h`). When enabled, `esp_task_wdt_reset()` is called at the end of each `loop()` iteration. Long blocking operations inside `loop()` — such as OTA downloads or slow file I/O — must call `esp_task_wdt_reset()` periodically, or be restructured so the main loop is not blocked for longer than the configured timeout. -## General - -- Follow the existing style in the file you are editing -- If possible, use `static` for local (C-style) variables and functions (keeps the global namespace clean) -- Avoid unexplained "magic numbers". Prefer named constants (`constexpr`) or C-style `#define` constants for repeated numbers that have the same meaning -- Include `"wled.h"` as the primary project header where needed - +## Caveats and Pitfalls - **Float-to-unsigned conversion is undefined behavior when the value is out of range.** Converting a negative `float` directly to an unsigned integer type (`uint8_t`, `uint16_t`, …) is UB per the C++ standard — the Xtensa (ESP32) toolchain may silently wrap, but RISC-V (ESP32-C3/C5/C6/P4) can produce different results due to clamping. Cast through a signed integer first: ```cpp // Undefined behavior — avoid: From 1bca7864c6785286f29af65233a4ca011528221d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Wed, 8 Apr 2026 16:57:50 +0200 Subject: [PATCH 40/48] Enhance PR instructions with review support guidelines Added guidelines for supporting reviews and discussions in PRs. --- .github/copilot-instructions.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index ccc3b2cb10..038250d0ec 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -128,3 +128,8 @@ Using AI-generated code can hide the source of the inspiration / knowledge / sou - **No force-push on open PRs.** Once a pull request is open and being reviewed, do not force-push (`git push --force`) to the branch. Force-pushing rewrites history that reviewers may have already commented on, making it impossible to track incremental changes. Use regular commits or `git merge` to incorporate feedback; the branch will be squash-merged when it is accepted. - **Document your changes in the PR.** Every pull request should include a clear description of *what* changed and *why*. If the change affects user-visible behavior, describe the expected impact. Link to related issues where applicable. Provide screenshots to showcase new features. + +### Supporting Reviews and Discussions +- **For "is it worth doing?" debates** about proposed reliability, safety, or data-integrity mechanisms (CRC checks, backups, power-loss protection): suggest a software **FMEA** (Failure Mode and Effects Analysis). + Clarify the main feared events, enumerate failure modes, assess each mitigation's effectiveness per failure mode, note common-cause failures, and rate credibility for the typical WLED use case. + From a91708165a41b150464c536c6964f45ef1126e10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Wed, 8 Apr 2026 17:08:04 +0200 Subject: [PATCH 41/48] Update cpp.instructions.md with caveats Add notes on LittleFS filenames and float-to-unsigned conversion. --- docs/cpp.instructions.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/cpp.instructions.md b/docs/cpp.instructions.md index 97bfeac437..bab1a87120 100644 --- a/docs/cpp.instructions.md +++ b/docs/cpp.instructions.md @@ -490,6 +490,9 @@ void myTask(void*) { - **Watchdog note.** WLED disables the Task Watchdog by default (`WLED_WATCHDOG_TIMEOUT 0` in `wled.h`). When enabled, `esp_task_wdt_reset()` is called at the end of each `loop()` iteration. Long blocking operations inside `loop()` — such as OTA downloads or slow file I/O — must call `esp_task_wdt_reset()` periodically, or be restructured so the main loop is not blocked for longer than the configured timeout. ## Caveats and Pitfalls + +- **LittleFS filenames**: File paths passed to `file.open()` must not exceed 255 bytes (`LFS_NAME_MAX`). Validate constructed paths (e.g., `/presets/` + segment name + `.json`) stay within this limit (assume standard configurations, like WLED_MAX_SEGNAME_LEN = 64). + - **Float-to-unsigned conversion is undefined behavior when the value is out of range.** Converting a negative `float` directly to an unsigned integer type (`uint8_t`, `uint16_t`, …) is UB per the C++ standard — the Xtensa (ESP32) toolchain may silently wrap, but RISC-V (ESP32-C3/C5/C6/P4) can produce different results due to clamping. Cast through a signed integer first: ```cpp // Undefined behavior — avoid: From 66c32c888511cdcd0b5872c6d03cf9d94352c2d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Wed, 8 Apr 2026 17:09:41 +0200 Subject: [PATCH 42/48] Fix LittleFS filename path in cpp.instructions.md Updated the LittleFS filename path in instructions to reflect the correct directory structure. --- docs/cpp.instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cpp.instructions.md b/docs/cpp.instructions.md index bab1a87120..7d0d5ad187 100644 --- a/docs/cpp.instructions.md +++ b/docs/cpp.instructions.md @@ -491,7 +491,7 @@ void myTask(void*) { ## Caveats and Pitfalls -- **LittleFS filenames**: File paths passed to `file.open()` must not exceed 255 bytes (`LFS_NAME_MAX`). Validate constructed paths (e.g., `/presets/` + segment name + `.json`) stay within this limit (assume standard configurations, like WLED_MAX_SEGNAME_LEN = 64). +- **LittleFS filenames**: File paths passed to `file.open()` must not exceed 255 bytes (`LFS_NAME_MAX`). Validate constructed paths (e.g., `/littlefs/presets/` + segment name + `.json`) stay within this limit (assume standard configurations, like WLED_MAX_SEGNAME_LEN = 64). - **Float-to-unsigned conversion is undefined behavior when the value is out of range.** Converting a negative `float` directly to an unsigned integer type (`uint8_t`, `uint16_t`, …) is UB per the C++ standard — the Xtensa (ESP32) toolchain may silently wrap, but RISC-V (ESP32-C3/C5/C6/P4) can produce different results due to clamping. Cast through a signed integer first: ```cpp From 830d931255108bd0823f53b28917d3b5d9d0f924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Wed, 8 Apr 2026 17:11:49 +0200 Subject: [PATCH 43/48] Fix LittleFS filenames caveat example path example file path: don't use directories --- docs/cpp.instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cpp.instructions.md b/docs/cpp.instructions.md index 7d0d5ad187..2859b9fc24 100644 --- a/docs/cpp.instructions.md +++ b/docs/cpp.instructions.md @@ -491,7 +491,7 @@ void myTask(void*) { ## Caveats and Pitfalls -- **LittleFS filenames**: File paths passed to `file.open()` must not exceed 255 bytes (`LFS_NAME_MAX`). Validate constructed paths (e.g., `/littlefs/presets/` + segment name + `.json`) stay within this limit (assume standard configurations, like WLED_MAX_SEGNAME_LEN = 64). +- **LittleFS filenames**: File paths passed to `file.open()` must not exceed 255 bytes (`LFS_NAME_MAX`). Validate constructed paths (e.g., `/littlefs/lemdmap_` + segment name + `.json`) stay within this limit (assume standard configurations, like WLED_MAX_SEGNAME_LEN = 64). - **Float-to-unsigned conversion is undefined behavior when the value is out of range.** Converting a negative `float` directly to an unsigned integer type (`uint8_t`, `uint16_t`, …) is UB per the C++ standard — the Xtensa (ESP32) toolchain may silently wrap, but RISC-V (ESP32-C3/C5/C6/P4) can produce different results due to clamping. Cast through a signed integer first: ```cpp From 77cd575dc0e724aa2a7ee68aa9d8e739c7e54620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Wed, 8 Apr 2026 17:17:37 +0200 Subject: [PATCH 44/48] Fix typo in LittleFS filenames caveat Corrected a typo in the LittleFS filenames caveat. --- docs/cpp.instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cpp.instructions.md b/docs/cpp.instructions.md index 2859b9fc24..f77b4f164d 100644 --- a/docs/cpp.instructions.md +++ b/docs/cpp.instructions.md @@ -491,7 +491,7 @@ void myTask(void*) { ## Caveats and Pitfalls -- **LittleFS filenames**: File paths passed to `file.open()` must not exceed 255 bytes (`LFS_NAME_MAX`). Validate constructed paths (e.g., `/littlefs/lemdmap_` + segment name + `.json`) stay within this limit (assume standard configurations, like WLED_MAX_SEGNAME_LEN = 64). +- **LittleFS filenames**: File paths passed to `file.open()` must not exceed 255 bytes (`LFS_NAME_MAX`). Validate constructed paths (e.g., `/littlefs/ledmap_` + segment name + `.json`) stay within this limit (assume standard configurations, like WLED_MAX_SEGNAME_LEN = 64). - **Float-to-unsigned conversion is undefined behavior when the value is out of range.** Converting a negative `float` directly to an unsigned integer type (`uint8_t`, `uint16_t`, …) is UB per the C++ standard — the Xtensa (ESP32) toolchain may silently wrap, but RISC-V (ESP32-C3/C5/C6/P4) can produce different results due to clamping. Cast through a signed integer first: ```cpp From 0784334769279ba6828e82c42d629a2b2f82066d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Wed, 8 Apr 2026 17:20:42 +0200 Subject: [PATCH 45/48] Update F() usage explanation for string constants Avoid that AI reviews over-apply the F() rule. --- docs/cpp.instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cpp.instructions.md b/docs/cpp.instructions.md index f77b4f164d..12732dd651 100644 --- a/docs/cpp.instructions.md +++ b/docs/cpp.instructions.md @@ -91,7 +91,7 @@ uint8_t gammaCorrect(uint8_t value, float gamma); - Use `const char*` for temporary/parsed strings - Avoid `String` (Arduino heap-allocated string) in hot paths; acceptable in config/setup code -- Use `F("string")` for string constants (saves RAM on 8266) +- Use `F("string")` for string constants (major RAM win on ESP8266; mostly overload/type compatibility on ESP32) On **ESP8266** this explicitly stores the string in flash (PROGMEM), saving precious RAM — every byte counts on that platform. From 2f109f85f8e70d5511fefd98506ecadbe8699d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Wed, 8 Apr 2026 17:44:30 +0200 Subject: [PATCH 46/48] adding Revise math function instructions * Math and trigonometric function usage guidelines for WLED, replacing deprecated FastLED aliases with new function names and providing a comparison table. * Minor re-styling --- docs/cpp.instructions.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/cpp.instructions.md b/docs/cpp.instructions.md index 12732dd651..697037226a 100644 --- a/docs/cpp.instructions.md +++ b/docs/cpp.instructions.md @@ -451,7 +451,24 @@ if (lastKelvin != kelvin) { - Move frequently-called small functions to headers for inlining (e.g. `Segment::setPixelColorRaw` is in `FX.h`) - Use `static inline` for file-local helpers -### `delay()` vs `yield()` in ESP32 Tasks +### Math & Trigonometric Functions + +- WLED uses a custom `fastled_slim` library. The old FastLED trig aliases (`sin8`, `cos8`, `sin16`, `cos16`) **no longer exist and cause a compile error** — use `sin8_t()`, `cos8_t()`, `sin16_t()`, `cos16_t()` instead. For float approximations use `sin_approx()` / `cos_approx()` instead of `sinf()` / `cosf()`. Replace FastLED noise aliases (`inoise8`, `inoise16`) with `perlin8`, `perlin16`. + + +| ❌ Do not use (compile error) | ✅ Use instead | Source | +|---|---|---| +| `sin8()`, `cos8()` | `sin8_t()`, `cos8_t()` | `fastled_slim.h` → `wled_math.cpp` | +| `sin16()`, `cos16()` | `sin16_t()`, `cos16_t()` | `fastled_slim.h` → `wled_math.cpp` | +| `sinf()`, `cosf()` | `sin_approx()`, `cos_approx()` | `wled_math.cpp` | +| `atan2f()`, `atan2()` | `atan2_t()` | `wled_math.cpp` | +| `sqrt()` on integers | `sqrt32_bw()` | `fcn_declare.h` → `wled_math.cpp` | +| `sqrtf()` on floats | `sqrtf()` (acceptable) | — no WLED replacement | + + +--- + +## `delay()` vs `yield()` in ESP32 Tasks * On ESP32, `delay(ms)` calls `vTaskDelay(ms / portTICK_PERIOD_MS)`, which **suspends only the calling task**. The FreeRTOS scheduler immediately runs all other ready tasks. * The Arduino `loop()` function runs inside `loopTask`. Calling `delay()` there does *not* block the network stack, audio FFT, LED DMA, nor any other FreeRTOS task. From 2ea7a06b7465f0f00817e09a7d16aa1c0631f409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Wed, 8 Apr 2026 17:50:11 +0200 Subject: [PATCH 47/48] fix section heading jump was jumping from H2 -> H4 --- docs/cpp.instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cpp.instructions.md b/docs/cpp.instructions.md index 697037226a..2f82e86335 100644 --- a/docs/cpp.instructions.md +++ b/docs/cpp.instructions.md @@ -480,7 +480,7 @@ if (lastKelvin != kelvin) { - Do *not* use `delay()` in effects (FX.cpp) or in the hot pixel path. - `delay()` on the bus-level is allowed, it might be needed to achieve exact timing in LED drivers. -#### IDLE Watchdog and Custom Tasks on ESP32 +### IDLE Watchdog and Custom Tasks on ESP32 - In arduino-esp32, `yield()` calls `vTaskDelay(0)`, which only switches to tasks at equal or higher priority — the IDLE task (priority 0) is never reached. - **Do not use `yield()` to pace ESP32 tasks or assume it feeds any watchdog**. From 2910f72b8d8201e1c105d6e3cfcc7317e26981cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Wed, 8 Apr 2026 21:03:02 +0200 Subject: [PATCH 48/48] Fix LittleFS filename path in documentation --- docs/cpp.instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cpp.instructions.md b/docs/cpp.instructions.md index 2f82e86335..edb46f51fc 100644 --- a/docs/cpp.instructions.md +++ b/docs/cpp.instructions.md @@ -508,7 +508,7 @@ void myTask(void*) { ## Caveats and Pitfalls -- **LittleFS filenames**: File paths passed to `file.open()` must not exceed 255 bytes (`LFS_NAME_MAX`). Validate constructed paths (e.g., `/littlefs/ledmap_` + segment name + `.json`) stay within this limit (assume standard configurations, like WLED_MAX_SEGNAME_LEN = 64). +- **LittleFS filenames**: File paths passed to `file.open()` must not exceed 255 bytes (`LFS_NAME_MAX`). Validate constructed paths (e.g., `/ledmap_` + segment name + `.json`) stay within this limit (assume standard configurations, like WLED_MAX_SEGNAME_LEN = 64). - **Float-to-unsigned conversion is undefined behavior when the value is out of range.** Converting a negative `float` directly to an unsigned integer type (`uint8_t`, `uint16_t`, …) is UB per the C++ standard — the Xtensa (ESP32) toolchain may silently wrap, but RISC-V (ESP32-C3/C5/C6/P4) can produce different results due to clamping. Cast through a signed integer first: ```cpp