You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
For detailed build timeouts, development workflows, troubleshooting, and validation steps, see [agent-build.instructions.md](agent-build.instructions.md).
48
48
49
49
## Repository Structure
50
50
51
51
tl;dr:
52
-
* Firmware source: `wled00/` (C++).
53
-
* Build targets: `platformio.ini`.
54
-
* Web UI source: `wled00/data/`.
55
-
* Auto-generated headers: `wled00/html_*.h` — **never edit or commit**.
* Usermods: `usermods/` (`.h` files, included via `usermods_list.cpp`).
58
-
* CI/CD: `.github/workflows/`.
59
56
60
57
Main development trunk: `mdev` branch. Make PRs against this branch.
61
58
@@ -81,27 +78,30 @@ tools/cdata-test.js # Test suite
81
78
package.json # Node.js scripts and release ID
82
79
.github/workflows/ # CI/CD pipelines
83
80
```
84
-
<!-- HUMAN_ONLY_END -->
85
81
82
+
<!-- HUMAN_ONLY_END -->
86
83
## General Guidelines
87
84
88
-
-**Never edit or commit**`wled00/html_*.h` — auto-generated from `wled00/data/`.
89
85
-**Repository language is English.** Suggest translations for non-English content.
90
86
-**Use VS Code with PlatformIO extension** for best development experience.
87
+
-**Never edit or commit**`wled00/html_*.h` and `wled00/js_*.h` — auto-generated from `wled00/data/`.
88
+
- If updating Web UI files in `wled00/data/`, **make use of common functions in `wled00/data/common.js` whenever possible**.
91
89
-**When unsure, say so.** Gather more information rather than guessing.
92
90
-**Acknowledge good patterns** when you see them. Summarize good practices as part of your review - positive feedback always helps.
93
91
-**Provide references** when making analyses or recommendations. Base them on the correct branch or PR.
94
-
-**Look for user-visible breakingchanges and ripple effects**. Ask for confirmation that these were introduced intentionally.
92
+
-**Highlight user-visible breakingchanges and ripple effects**. Ask for confirmation that these were introduced intentionally.
95
93
-**Unused / dead code must be justified or removed**. This helps to keep the codebase clean, maintainable and readable.
96
94
-**C++ formatting available**: `clang-format` is installed but not in CI
97
-
- 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.
95
+
- No automated linting is configured — match existing code style in files you edit.
96
+
97
+
See `cpp.instructions.md`, `esp-idf.instructions.md` and `web.instructions.md` for language-specific conventions, and `cicd.instructions.md` for GitHub Actions workflows.
98
98
99
99
### Attribution for AI-generated code
100
100
Using AI-generated code can hide the source of the inspiration / knowledge / sources it used.
101
101
- Document attribution of inspiration / knowledge / sources used in the code, e.g. link to GitHub repositories or other websites describing the principles / algorithms used.
102
102
- 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).
103
103
- 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.
104
-
- 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.
104
+
- 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.
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.
65
65
66
66
<!-- HUMAN_ONLY_START -->
67
+
<!-- hidden from AI for now, as it created too many "please add a description" review findings in my first tests -->
67
68
- **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:
-**PSRAM-aware allocation**: use `d_malloc()` (prefer DRAM), `p_malloc()` (prefer PSRAM) from `util.h`
107
107
-**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.
108
108
<!-- HUMAN_ONLY_START -->
109
-
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.
109
+
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.
110
110
<!-- HUMAN_ONLY_END -->
111
111
-**Larger buffers** (LED data, JSON documents) should use PSRAM when available and technically feasible
112
112
-**Hot-path**: some data should stay in DRAM or IRAM for performance reasons
113
113
- Memory efficiency matters, but is less critical on boards with PSRAM
114
114
115
+
Heap fragmentation is a concern:
116
+
<!-- HUMAN_ONLY_START -->
117
+
- 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".
118
+
<!-- HUMAN_ONLY_END -->
119
+
- Avoid frequent `d_malloc` and `d_free` inside a function, especially for small sizes.
120
+
- Avoid frequent creation / destruction of objects.
121
+
- Allocate buffers early, and try to re-use them.
122
+
- Instead of incrementally appending to a `String`, reserve the expected max buffer upfront by using the `reserve()` method.
123
+
<!-- HUMAN_ONLY_START -->
124
+
125
+
```cpp
126
+
String result;
127
+
result.reserve(65); // pre-allocate to avoid realloc fragmentation
128
+
```
129
+
130
+
```cpp
131
+
// prefer DRAM; falls back gracefully and enforces MIN_HEAP_SIZE guard
_mode.reserve(_modeCount); // allocate memory to prevent initial fragmentation - does not increase size()
137
+
_modeData.reserve(_modeCount); // allocate memory to prevent initial fragmentation - does not increase size()
138
+
```
139
+
<!-- HUMAN_ONLY_END -->
140
+
115
141
## `const` and `constexpr`
116
142
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.
"PinOwner::None must be zero, so default array initialization works as expected");
209
+
```
178
210
<!-- HUMAN_ONLY_END -->
179
211
180
212
Prefer `constexpr` over `#define` for typed constants (scope-safe, debuggable). Use `static_assert` instead of `#if … #error` for compile-time validation.
- If possible, use `static` for local (C-style) variables and functions (keeps the global namespace clean)
531
563
- Avoid unexplained "magic numbers". Prefer named constants (`constexpr`) or C-style `#define` constants for repeated numbers that have the same meaning
532
564
- Include `"wled.h"` as the primary project header where needed
533
-
-**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:
565
+
-**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:
0 commit comments