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
-**Repository language is English.** Suggest translations for non-English content.
89
89
-**Use VS Code with PlatformIO extension** for best development experience.
90
90
-**Never edit or commit**`wled00/html_*.h` and `wled00/js_*.h` — auto-generated from `wled00/data/`.
91
-
- If updating Web UI files in `wled00/data/`, **make use of common functions in `wled00/data/common.js` whenever possible**.
92
91
-**When unsure, say so.** Gather more information rather than guessing.
93
-
-**Acknowledge good patterns** when you see them. Summarize good practices as part of your review - positive feedback always helps.
92
+
-**Acknowledge good patterns** when you see them. Positive feedback always helps.
94
93
-**Provide references** when making analyses or recommendations. Base them on the correct branch or PR.
95
94
-**Highlight user-visible breaking changes and ripple effects**. Ask for confirmation that these were introduced intentionally.
96
95
-**Unused / dead code must be justified or removed**. This helps to keep the codebase clean, maintainable and readable.
97
96
-**C++ formatting available**: `clang-format` is installed but not in CI
98
97
- No automated linting is configured — match existing code style in files you edit.
99
98
100
-
See `docs/cpp.instructions.md`, `docs/esp-idf.instructions.md` and `docs/web.instructions.md` for language-specific conventions, and `docs/cicd.instructions.md` for GitHub Actions workflows.
99
+
Refer to `docs/cpp.instructions.md`, `docs/esp-idf.instructions.md` and `docs/web.instructions.md` for language-specific conventions, and `docs/cicd.instructions.md` for GitHub Actions workflows.
100
+
101
+
### Feature Flags
102
+
-**Verify feature-flag names.** Every `WLED_ENABLE_*` / `WLED_DISABLE_*` flag must exactly match one of the names below — misspellings are silently ignored by the preprocessor (e.g. `WLED_IR_DISABLE` instead of `WLED_DISABLE_INFRARED`), causing silent build variations. Flag unrecognised names as likely typos and suggest the correct spelling.
#ifdef WLED_DMX_ENABLED // wrong flag - initialization silently skipped
111
+
initDMX();
112
+
#endif
113
+
114
+
#ifdef WLED_ENABLE_DMX // correct flag - initialization performed only when the build supports DMX
115
+
initDMX();
116
+
#endif
117
+
```
118
+
<!-- HUMAN_ONLY_END -->
119
+
101
120
102
121
### Attribution for AI-generated code
103
122
Using AI-generated code can hide the source of the inspiration / knowledge / sources it used.
104
123
- Document attribution of inspiration / knowledge / sources used in the code, e.g. link to GitHub repositories or other websites describing the principles / algorithms used.
105
-
- 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).
124
+
- When a larger block of code is generated by an AI tool, embed it into `// AI: below section was generated by an AI`... `// AI: end` comments (see C++ guidelines).
106
125
- 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.
107
126
- 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.
108
127
109
128
### Pull Request Expectations
110
129
111
130
-**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.
131
+
-**Modifications to ``platformio.ini`` MUST be approved explicitly** by a *maintainer* or *WLED organisation Member*. Modifications to the global build environment may break github action builds. Always flag them.
112
132
-**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.
133
+
134
+
### Supporting Reviews and Discussions
135
+
-**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).
136
+
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.
Copy file name to clipboardExpand all lines: docs/cpp.instructions.md
+39-25Lines changed: 39 additions & 25 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -25,8 +25,16 @@ See also: [CONTRIBUTING.md](../CONTRIBUTING.md) for general style guidelines tha
25
25
26
26
-**camelCase** for functions and variables: `setValuesFromMainSeg()`, `effectCurrent`
27
27
-**PascalCase** for classes and structs: `PinManagerClass`, `BusConfig`
28
+
-**PascalCase** for enum values: `PinOwner::BusDigital`
28
29
-**UPPER_CASE** for macros and constants: `WLED_MAX_USERMODS`, `DEFAULT_CLIENT_SSID`
29
30
31
+
## General
32
+
33
+
- Follow the existing style in the file you are editing
34
+
- If possible, use `static` for local (C-style) variables and functions (keeps the global namespace clean)
35
+
- Avoid unexplained "magic numbers". Prefer named constants (`constexpr`) or C-style `#define` constants for repeated numbers that have the same meaning
36
+
- Include `"wled.h"` as the primary project header where needed
37
+
30
38
<!-- HUMAN_ONLY_START -->
31
39
## Header Guards
32
40
@@ -61,7 +69,7 @@ Most headers use `#ifndef` / `#define` guards. Some newer headers add `#pragma o
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.
@@ -142,25 +150,28 @@ Heap fragmentation is a concern:
142
150
<!-- HUMAN_ONLY_END -->
143
151
144
152
## `const` and `constexpr`
145
-
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.
146
-
147
153
<!-- HUMAN_ONLY_START -->
148
154
`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`.
149
155
This pattern enables optimizations and makes intent clear to reviewers.
150
156
151
-
### `const` locals
157
+
`constexpr` allows to define constants that are *guaranteed* to be evaluated by the compiler (zero run-time costs).
158
+
159
+
<!-- HUMAN_ONLY_END -->
160
+
- For function parameters that are read-only, prefer `const &` or `const`.
152
161
162
+
### `const` locals
163
+
<!-- HUMAN_ONLY_START -->
153
164
* Adding `const` to a local variable that is only assigned once is optional, but *not* strictly necessary.
154
-
* In hot-path code, `const` on cached locals may help the compiler keep values in registers:
165
+
<!-- HUMAN_ONLY_END -->
166
+
* In hot-path code, `const` on cached locals may help the compiler keep values in registers.
155
167
```cpp
156
168
constuint_fast16_t cols = vWidth();
157
169
constuint_fast16_t rows = vHeight();
158
170
```
159
171
160
-
<!-- HUMAN_ONLY_END -->
161
172
### `const` references to avoid copies
162
-
163
-
Pass and store objects by `const &` (or `&`) instead of copying them implicitly. This avoids constructing temporary objects on every access — especially important in loops.
173
+
- Pass objects by `const &` (or `&`) instead of copying them implicitly.
174
+
- Use `const &` (or `&`) inside loops - This avoids constructing temporary objects on every access.
164
175
165
176
<!-- HUMAN_ONLY_START -->
166
177
```cpp
@@ -174,11 +185,14 @@ For function parameters that are read-only, prefer `const &`:
- Class **Data Members:** Avoid reference data members (`T&` or `const T&`) in a class.
189
+
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.
177
190
178
-
### `constexpr` over `#define`
179
191
<!-- HUMAN_ONLY_START -->
192
+
<!-- hidden from AI for now - codebase is not compliant to this rule (slowly migrating) -->
193
+
### `constexpr` over `#define`
180
194
181
-
Prefer `constexpr` for compile-time constants. Unlike `#define`, `constexpr` respects scope and type safety, keeping the global namespace clean:
195
+
- Prefer `constexpr` for compile-time constants. Unlike `#define`, `constexpr` respects scope and type safety, keeping the global namespace clean.
Note: `#define` is still needed for conditional compilation guards (`#ifdef`), platform macros, and values that must be overridable from build flags.
207
+
<!-- HUMAN_ONLY_END -->
193
208
194
209
### `static_assert` over `#error`
195
210
196
-
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:
211
+
- 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.
212
+
-`#define` and `#if ... #else ... #endif` is still needed for conditional-compilation guards and build-flag-overridable values.
"PinOwner::None must be zero, so default array initialization works as expected");
213
230
```
214
231
<!-- HUMAN_ONLY_END -->
215
-
216
-
Prefer `constexpr` over `#define` for typed constants (scope-safe, debuggable). Use `static_assert` instead of `#if … #error` for compile-time validation.
217
-
Exception: `#define` is required for conditional-compilation guards and build-flag-overridable values.
218
-
219
232
### `static` and `const` class methods
220
233
221
234
#### `const` member functions
@@ -425,7 +438,7 @@ Move invariant calculations before the loop. Pre-compute reciprocals to replace
### Bit Shifts Over Division (mainly for RISC-V boards)
457
+
### Bit Shifts Over Division (mainly for RISC-V and ESP8266 boards)
445
458
446
459
ESP32 and ESP32-S3 (Xtensa core) have a fast "integer divide" instruction, so manual shifts rarely help.
447
-
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.
460
+
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.
461
+
Always use unsigned operands for right shifts; signed right-shift is implementation-defined.
448
462
449
463
<!-- HUMAN_ONLY_START -->
450
-
On RISC-V-based boards (ESP32-C3, ESP32-C6, ESP32-C5) explicit shifts can be beneficial.
464
+
Explicit shifts can be beneficial on RISC-V-based boards (ESP32-C3, ESP32-C6, ESP32-C5) and on ESP8266 boards.
451
465
```cpp
452
466
position >> 3// instead of position / 8
453
467
(255U - rate) >> 1// instead of (255 - rate) / 2
@@ -488,6 +502,9 @@ if (lastKelvin != kelvin) {
488
502
```
489
503
490
504
<!-- HUMAN_ONLY_END -->
505
+
506
+
---
507
+
491
508
## Multi-Task Synchronization
492
509
493
510
ESP32 runs multiple FreeRTOS tasks concurrently (e.g. network handling, LED output, JSON parsing). Use the WLED-MM mutex macros for synchronization — they expand to FreeRTOS recursive semaphore calls on ESP32 and compile to no-ops on ESP8266:
@@ -525,7 +542,7 @@ Always pair every `esp32SemTake` with a matching `esp32SemGive`. Choose a timeou
525
542
<!-- HUMAN_ONLY_START -->
526
543
* 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.
527
544
* 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.
528
-
* This differs from ESP8266, where `delay()`stalled the entire system unless `yield()` was called inside.
545
+
* This differs from ESP8266, where `delay()`stalls the entire system unless `yield()` was called inside.
529
546
<!-- HUMAN_ONLY_END -->
530
547
531
548
- On ESP32, `delay()` is generally allowed, as it helps to efficiently manage CPU usage of all tasks.
@@ -565,12 +582,9 @@ void myTask(void*) {
565
582
- Prefer blocking FreeRTOS primitives (`xQueueReceive`, `ulTaskNotifyTake`, `vTaskDelayUntil`) over `delay(1)` polling where precise timing or event-driven behaviour is needed.
566
583
-**Watchdog note.** WLED-MM 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.
567
584
568
-
## General
585
+
## Caveats and Pitfalls
569
586
570
-
- Follow the existing style in the file you are editing
571
-
- If possible, use `static` for local (C-style) variables and functions (keeps the global namespace clean)
572
-
- Avoid unexplained "magic numbers". Prefer named constants (`constexpr`) or C-style `#define` constants for repeated numbers that have the same meaning
573
-
- Include `"wled.h"` as the primary project header where needed
587
+
-**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).
574
588
575
589
-**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