Compare commits
10 Commits
a8463fa510
...
95137a6d65
| Author | SHA1 | Date | |
|---|---|---|---|
| 95137a6d65 | |||
|
|
3a01c00635 | ||
|
|
e4c9fd5c62 | ||
|
|
08529a744e | ||
|
|
64529bbd84 | ||
|
|
e4c8e349da | ||
|
|
a718caf4f7 | ||
|
|
6c9922d072 | ||
|
|
231373acac | ||
|
|
eb2352774d |
@@ -3,10 +3,10 @@
|
|||||||
# CodeRabbit configuration — references existing guideline files to avoid
|
# CodeRabbit configuration — references existing guideline files to avoid
|
||||||
# duplicating conventions. See:
|
# duplicating conventions. See:
|
||||||
# .github/copilot-instructions.md — project overview & general rules
|
# .github/copilot-instructions.md — project overview & general rules
|
||||||
# .github/cpp.instructions.md — C++ coding conventions
|
# docs/cpp.instructions.md — C++ coding conventions
|
||||||
# .github/web.instructions.md — Web UI coding conventions
|
# docs/web.instructions.md — Web UI coding conventions
|
||||||
# .github/cicd.instructions.md — GitHub Actions / CI-CD conventions
|
# docs/cicd.instructions.md — GitHub Actions / CI-CD conventions
|
||||||
# .github/esp-idf.instructions.md — ESP-IDF / chip-specific coding guidelines
|
# docs/esp-idf.instructions.md — ESP-IDF / chip-specific coding guidelines
|
||||||
# (apply when code directly uses ESP-IDF APIs:
|
# (apply when code directly uses ESP-IDF APIs:
|
||||||
# esp_idf_*, I2S, RMT, ADC, GPIO, heap_caps, etc.)
|
# esp_idf_*, I2S, RMT, ADC, GPIO, heap_caps, etc.)
|
||||||
#
|
#
|
||||||
@@ -20,11 +20,11 @@ reviews:
|
|||||||
path_instructions:
|
path_instructions:
|
||||||
- path: "**/*.{cpp,h,hpp,ino}"
|
- path: "**/*.{cpp,h,hpp,ino}"
|
||||||
instructions: >
|
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.
|
and the general project guidelines in .github/copilot-instructions.md.
|
||||||
If the code under review directly uses ESP-IDF APIs (e.g. heap_caps_malloc,
|
If the code under review directly uses ESP-IDF APIs (e.g. heap_caps_malloc,
|
||||||
I2S, RMT, ADC, GPIO, esp_timer, or any esp_idf_* / soc_* symbols), also
|
I2S, RMT, ADC, GPIO, esp_timer, or any esp_idf_* / soc_* symbols), also
|
||||||
apply the guidelines in .github/esp-idf.instructions.md.
|
apply the guidelines in docs/esp-idf.instructions.md.
|
||||||
|
|
||||||
Key rules: 2-space indentation (no tabs), camelCase functions/variables,
|
Key rules: 2-space indentation (no tabs), camelCase functions/variables,
|
||||||
PascalCase classes, UPPER_CASE macros. Mark WLED-MM-specific changes with
|
PascalCase classes, UPPER_CASE macros. Mark WLED-MM-specific changes with
|
||||||
@@ -36,7 +36,7 @@ reviews:
|
|||||||
|
|
||||||
- path: "wled00/data/**"
|
- path: "wled00/data/**"
|
||||||
instructions: >
|
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 or spaces.
|
Key rules: indent HTML and JavaScript with tabs, CSS with tabs or spaces.
|
||||||
Files here are built into wled00/html_*.h by tools/cdata.js — never
|
Files here are built into wled00/html_*.h by tools/cdata.js — never
|
||||||
@@ -54,11 +54,11 @@ reviews:
|
|||||||
Each usermod lives in its own directory under usermods/ and is implemented
|
Each usermod lives in its own directory under usermods/ and is implemented
|
||||||
as a .h file that is pulled in by wled00/usermods_list.cpp (guarded by
|
as a .h file that is pulled in by wled00/usermods_list.cpp (guarded by
|
||||||
#ifdef). Usermods do not use library.json. Follow the same C++ conventions
|
#ifdef). Usermods do not use library.json. Follow the same C++ conventions
|
||||||
as the core firmware (.github/cpp.instructions.md).
|
as the core firmware (docs/cpp.instructions.md).
|
||||||
|
|
||||||
- path: ".github/workflows/*.{yml,yaml}"
|
- path: ".github/workflows/*.{yml,yaml}"
|
||||||
instructions: >
|
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.
|
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
|
Third-party actions must be pinned to a specific version tag — branch pins
|
||||||
@@ -67,7 +67,7 @@ reviews:
|
|||||||
into run: steps — pass them through an env: variable to prevent script
|
into run: steps — pass them through an env: variable to prevent script
|
||||||
injection. Do not use pull_request_target unless fully justified.
|
injection. Do not use pull_request_target unless fully justified.
|
||||||
|
|
||||||
- path: ".github/*.instructions.md"
|
- path: "**/*.instructions.md"
|
||||||
instructions: |
|
instructions: |
|
||||||
This file contains both AI-facing rules and human-only reference sections.
|
This file contains both AI-facing rules and human-only reference sections.
|
||||||
Human-only sections are enclosed in `<!-- HUMAN_ONLY_START -->` /
|
Human-only sections are enclosed in `<!-- HUMAN_ONLY_START -->` /
|
||||||
@@ -81,4 +81,4 @@ reviews:
|
|||||||
2. Flag any HUMAN_ONLY section whose content has drifted from the surrounding
|
2. Flag any HUMAN_ONLY section whose content has drifted from the surrounding
|
||||||
AI-facing rules due to edits introduced in this PR.
|
AI-facing rules due to edits introduced in this PR.
|
||||||
3. If new AI-facing rules were added without updating a related HUMAN_ONLY
|
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).
|
reference section, note this as a suggestion (not a required fix).
|
||||||
|
|||||||
18
.github/agent-build.instructions.md
vendored
18
.github/agent-build.instructions.md
vendored
@@ -11,7 +11,7 @@ Use these timeout values when running builds:
|
|||||||
|
|
||||||
| Command | Typical Time | Minimum Timeout | Notes |
|
| 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 test` | ~40 s | 2 min | Validates build system |
|
||||||
| `npm run dev` | continuous | — | Watch mode, auto-rebuilds on changes |
|
| `npm run dev` | continuous | — | Watch mode, auto-rebuilds on changes |
|
||||||
| `pio run -e <env>` | 15–20 min | 30 min | First build downloads toolchains; subsequent builds are faster |
|
| `pio run -e <env>` | 15–20 min | 30 min | First build downloads toolchains; subsequent builds are faster |
|
||||||
@@ -20,16 +20,21 @@ Use these timeout values when running builds:
|
|||||||
|
|
||||||
## Development Workflow
|
## 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
|
### Web UI Changes
|
||||||
|
|
||||||
1. Edit files in `wled00/data/`
|
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)
|
3. Test with local HTTP server (see Manual Testing below)
|
||||||
4. Run `npm test` to validate
|
4. Run `npm test` to validate
|
||||||
|
|
||||||
### Firmware Changes
|
### 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`
|
2. Ensure web UI is built first: `npm run build`
|
||||||
3. Build firmware: `pio run -e esp32_4MB_V4_M` (set timeout ≥ 30 min)
|
3. Build firmware: `pio run -e esp32_4MB_V4_M` (set timeout ≥ 30 min)
|
||||||
4. Flash to device: `pio run -e [target] --target upload`
|
4. Flash to device: `pio run -e [target] --target upload`
|
||||||
@@ -85,8 +90,8 @@ Test these scenarios after every web UI change:
|
|||||||
### Recovery Steps
|
### Recovery Steps
|
||||||
|
|
||||||
- **Force web UI rebuild**: `npm run build -- -f`
|
- **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`
|
- **Clean PlatformIO build artifacts**: `pio run --target clean`
|
||||||
- **Reinstall Node deps**: `rm -rf node_modules && npm ci`
|
- **Reinstall Node deps**: `rm -rf node_modules && npm ci`
|
||||||
|
|
||||||
## CI/CD Validation
|
## CI/CD Validation
|
||||||
@@ -106,7 +111,8 @@ Match this workflow in local development to catch failures before pushing.
|
|||||||
|
|
||||||
## Important Reminders
|
## 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
|
- Web UI rebuild is part of the PlatformIO firmware compilation pipeline
|
||||||
- Common firmware environments: `esp32_4MB_V4_M`, `esp32_16MB_V4_S_HUB75`, `esp32S3_8MB_PSRAM_M_qspi`, `esp32_16MB_V4_M_eth`, `esp8266_4MB_S` (deprecated), `esp32dev_compat`
|
- Common firmware environments: `esp32_4MB_V4_M`, `esp32_16MB_V4_S_HUB75`, `esp32S3_8MB_PSRAM_M_qspi`, `esp32_16MB_V4_M_eth`, `esp8266_4MB_S` (deprecated), `esp32dev_compat`
|
||||||
- List all PlatformIO targets: `pio run --list-targets`
|
- List all PlatformIO targets: `pio run --list-targets`
|
||||||
|
|||||||
33
.github/copilot-instructions.md
vendored
33
.github/copilot-instructions.md
vendored
@@ -33,29 +33,28 @@ Always reference these instructions first and fallback to search or bash command
|
|||||||
|
|
||||||
| Command | Purpose | Typical Time |
|
| 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 test` | Run test suite | ~40 s |
|
||||||
| `npm run dev` | Watch mode — auto-rebuilds web UI on file changes | — |
|
| `npm run dev` | Watch mode — auto-rebuilds web UI on file changes | — |
|
||||||
| `pio run -e <env>` | Build firmware for a hardware target | 15–20 min |
|
| `pio run -e <env>` | Build firmware for a hardware target | 15–20 min |
|
||||||
|
|
||||||
<!-- HUMAN_ONLY_END -->
|
<!-- HUMAN_ONLY_END -->
|
||||||
|
|
||||||
**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 run build` before any `pio run`** (and run `npm ci` first on fresh clones or when lockfile/dependencies change).
|
||||||
**Build firmware to validate code changes**: `pio run -e esp32_4MB_V4_M` — must succeed, never skip this step.
|
- The web UI build generates required `wled00/html_*.h` and `wled00/js_*.h` headers for firmware compilation.
|
||||||
Common firmware environments: `esp32_4MB_V4_M`, `esp32_16MB_V4_S_HUB75`, `esp32S3_8MB_PSRAM_M_qspi`, `esp32_16MB_V4_M_eth`, `esp32dev_compat`, `esp8266_4MB_S` (deprecated)
|
- **Build firmware to validate code changes**: `pio run -e esp32_4MB_V4_M` — must succeed, never skip this step.
|
||||||
|
- Common firmware environments: `esp32_4MB_V4_M`, `esp32_16MB_V4_S_HUB75`, `esp32S3_8MB_PSRAM_M_qspi`, `esp32_16MB_V4_M_eth`, `esp32dev_compat`, `esp8266_4MB_S` (deprecated)
|
||||||
|
|
||||||
For detailed build timeouts, development workflows, troubleshooting, and validation steps, see [agent-build.instructions.md](agent-build.instructions.md).
|
For detailed build timeouts, development workflows, troubleshooting, and validation steps, see [agent-build.instructions.md](agent-build.instructions.md).
|
||||||
|
|
||||||
## Repository Structure
|
## Repository Structure
|
||||||
|
|
||||||
tl;dr:
|
tl;dr:
|
||||||
* Firmware source: `wled00/` (C++).
|
* Firmware source: `wled00/` (C++). Web UI source: `wled00/data/`. Build targets: `platformio.ini`.
|
||||||
* Build targets: `platformio.ini`.
|
* Auto-generated headers: `wled00/html_*.h` and `wled00/js_*.h` — **never edit or commit**.
|
||||||
* Web UI source: `wled00/data/`.
|
* ArduinoJSON + AsyncJSON: `wled00/src/dependencies/json` (included via `wled.h`). CI/CD: `.github/workflows/`.
|
||||||
* Auto-generated headers: `wled00/html_*.h` — **never edit or commit**.
|
|
||||||
* ArduinoJSON + AsyncJSON: `wled00/src/dependencies/json`
|
|
||||||
* Usermods: `usermods/` (`.h` files, included via `usermods_list.cpp`).
|
* Usermods: `usermods/` (`.h` files, included via `usermods_list.cpp`).
|
||||||
* CI/CD: `.github/workflows/`.
|
* Contributor docs: `docs/` (coding guidelines, design docs).
|
||||||
|
|
||||||
Main development trunk: `mdev` branch. Make PRs against this branch.
|
Main development trunk: `mdev` branch. Make PRs against this branch.
|
||||||
|
|
||||||
@@ -79,29 +78,33 @@ tools/ # Build tools (Node.js), partition files, and generi
|
|||||||
tools/cdata.js # Web UI → header build script
|
tools/cdata.js # Web UI → header build script
|
||||||
tools/cdata-test.js # Test suite
|
tools/cdata-test.js # Test suite
|
||||||
package.json # Node.js scripts and release ID
|
package.json # Node.js scripts and release ID
|
||||||
|
docs/ # Contributor docs: coding guidelines and design documentation
|
||||||
.github/workflows/ # CI/CD pipelines
|
.github/workflows/ # CI/CD pipelines
|
||||||
```
|
```
|
||||||
<!-- HUMAN_ONLY_END -->
|
|
||||||
|
|
||||||
|
<!-- HUMAN_ONLY_END -->
|
||||||
## General Guidelines
|
## General Guidelines
|
||||||
|
|
||||||
- **Never edit or commit** `wled00/html_*.h` — auto-generated from `wled00/data/`.
|
|
||||||
- **Repository language is English.** Suggest translations for non-English content.
|
- **Repository language is English.** Suggest translations for non-English content.
|
||||||
- **Use VS Code with PlatformIO extension** for best development experience.
|
- **Use VS Code with PlatformIO extension** for best development experience.
|
||||||
|
- **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.
|
- **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.
|
- **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.
|
- **Provide references** when making analyses or recommendations. Base them on the correct branch or PR.
|
||||||
- **Look for 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.
|
- **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
|
- **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 `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.
|
||||||
|
|
||||||
### Attribution for AI-generated code
|
### Attribution for AI-generated code
|
||||||
Using AI-generated code can hide the source of the inspiration / knowledge / sources it used.
|
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.
|
- 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).
|
- 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.
|
- 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
|
### Pull Request Expectations
|
||||||
|
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -5,8 +5,11 @@
|
|||||||
.gitignore
|
.gitignore
|
||||||
.idea
|
.idea
|
||||||
.pio
|
.pio
|
||||||
|
.piohome
|
||||||
.pioenvs
|
.pioenvs
|
||||||
.piolibdeps
|
.piolibdeps
|
||||||
|
.tools
|
||||||
|
.venv
|
||||||
.vscode
|
.vscode
|
||||||
.vscode/extensions.json
|
.vscode/extensions.json
|
||||||
|
|
||||||
|
|||||||
@@ -76,9 +76,9 @@ When in doubt, it is easiest to replicate the code style you find in the files y
|
|||||||
Our review bot (coderabbit) has learned lots of detailed guides and hints - it will suggest them automatically when you submit a PR for review.
|
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:
|
If you are curious, these are the detailed guides:
|
||||||
* [C++ Coding](https://github.com/MoonModules/WLED-MM/blob/mdev/.github/cpp.instructions.md)
|
* [C++ Coding](https://github.com/MoonModules/WLED-MM/blob/mdev/docs/cpp.instructions.md)
|
||||||
* [WebUi: HTML, JS, CSS](https://github.com/MoonModules/WLED-MM/blob/mdev/.github/web.instructions.md)
|
* [WebUi: HTML, JS, CSS](https://github.com/MoonModules/WLED-MM/blob/mdev/docs/web.instructions.md)
|
||||||
* [Using ESP-IDF directly](https://github.com/MoonModules/WLED-MM/blob/mdev/.github/esp-idf.instructions.md)
|
* [Using ESP-IDF directly](https://github.com/MoonModules/WLED-MM/blob/mdev/docs/esp-idf.instructions.md)
|
||||||
|
|
||||||
Below are the main rules used the WLED-MM repository.
|
Below are the main rules used the WLED-MM repository.
|
||||||
|
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ schedule:
|
|||||||
|
|
||||||
## Security
|
## Security
|
||||||
|
|
||||||
|
Important: Several current workflows still violate parts of the baseline below - migration is in progress.
|
||||||
|
|
||||||
### Permissions — Least Privilege
|
### Permissions — Least Privilege
|
||||||
|
|
||||||
Declare explicit `permissions:` blocks. The default token permissions are broad; scope them to the minimum required:
|
Declare explicit `permissions:` blocks. The default token permissions are broad; scope them to the minimum required:
|
||||||
@@ -8,7 +8,10 @@ applyTo: "**/*.cpp,**/*.h,**/*.hpp,**/*.ino"
|
|||||||
> contributor reference material. Do **not** use that content as actionable review
|
> contributor reference material. Do **not** use that content as actionable review
|
||||||
> criteria — treat it as background context only.
|
> criteria — treat it as background context only.
|
||||||
|
|
||||||
|
<!-- HUMAN_ONLY_START -->
|
||||||
|
<!-- hiding this reference, to avoid cyclic "include" loops -->
|
||||||
See also: [CONTRIBUTING.md](../CONTRIBUTING.md) for general style guidelines that apply to all contributors.
|
See also: [CONTRIBUTING.md](../CONTRIBUTING.md) for general style guidelines that apply to all contributors.
|
||||||
|
<!-- HUMAN_ONLY_END -->
|
||||||
|
|
||||||
## Formatting
|
## Formatting
|
||||||
|
|
||||||
@@ -18,13 +21,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)`
|
- 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
|
- No enforced line-length limit; wrap when a line exceeds your editor width
|
||||||
|
|
||||||
<!-- HUMAN_ONLY_START -->
|
|
||||||
## Naming
|
## Naming
|
||||||
|
|
||||||
- **camelCase** for functions and variables: `setValuesFromMainSeg()`, `effectCurrent`
|
- **camelCase** for functions and variables: `setValuesFromMainSeg()`, `effectCurrent`
|
||||||
- **PascalCase** for classes and structs: `PinManagerClass`, `BusConfig`
|
- **PascalCase** for classes and structs: `PinManagerClass`, `BusConfig`
|
||||||
- **UPPER_CASE** for macros and constants: `WLED_MAX_USERMODS`, `DEFAULT_CLIENT_SSID`
|
- **UPPER_CASE** for macros and constants: `WLED_MAX_USERMODS`, `DEFAULT_CLIENT_SSID`
|
||||||
|
|
||||||
|
<!-- HUMAN_ONLY_START -->
|
||||||
## Header Guards
|
## Header Guards
|
||||||
|
|
||||||
Most headers use `#ifndef` / `#define` guards. Some newer headers add `#pragma once` before the guard:
|
Most headers use `#ifndef` / `#define` guards. Some newer headers add `#pragma once` before the guard:
|
||||||
@@ -64,6 +67,7 @@ void calculateCRC(const uint8_t* data, size_t len) {
|
|||||||
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.
|
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.
|
||||||
|
|
||||||
<!-- HUMAN_ONLY_START -->
|
<!-- HUMAN_ONLY_START -->
|
||||||
|
<!-- hidden from AI for now, as it created too many "please add a description" review findings in my first tests -->
|
||||||
- **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:
|
- **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
|
```cpp
|
||||||
@@ -77,7 +81,6 @@ uint8_t gammaCorrect(uint8_t value, float gamma);
|
|||||||
```
|
```
|
||||||
<!-- HUMAN_ONLY_END -->
|
<!-- HUMAN_ONLY_END -->
|
||||||
|
|
||||||
|
|
||||||
Short accessor-style functions (getters/setters, one-liners) may skip this if their purpose is obvious from the name.
|
Short accessor-style functions (getters/setters, one-liners) may skip this if their purpose is obvious from the name.
|
||||||
|
|
||||||
## Preprocessor & Feature Flags
|
## Preprocessor & Feature Flags
|
||||||
@@ -106,12 +109,38 @@ uint8_t gammaCorrect(uint8_t value, float gamma);
|
|||||||
- **PSRAM-aware allocation**: use `d_malloc()` (prefer DRAM), `p_malloc()` (prefer PSRAM) from `util.h`
|
- **PSRAM-aware allocation**: use `d_malloc()` (prefer DRAM), `p_malloc()` (prefer PSRAM) from `util.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.
|
- **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.
|
||||||
<!-- HUMAN_ONLY_START -->
|
<!-- HUMAN_ONLY_START -->
|
||||||
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.
|
||||||
<!-- HUMAN_ONLY_END -->
|
<!-- HUMAN_ONLY_END -->
|
||||||
- **Larger buffers** (LED data, JSON documents) should use PSRAM when available and technically feasible
|
- **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
|
- **Hot-path**: some data should stay in DRAM or IRAM for performance reasons
|
||||||
- Memory efficiency matters, but is less critical on boards with PSRAM
|
- Memory efficiency matters, but is less critical on boards with PSRAM
|
||||||
|
|
||||||
|
Heap fragmentation is a concern:
|
||||||
|
<!-- HUMAN_ONLY_START -->
|
||||||
|
- 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".
|
||||||
|
<!-- HUMAN_ONLY_END -->
|
||||||
|
- 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.
|
||||||
|
<!-- HUMAN_ONLY_START -->
|
||||||
|
|
||||||
|
```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()
|
||||||
|
```
|
||||||
|
<!-- HUMAN_ONLY_END -->
|
||||||
|
|
||||||
## `const` and `constexpr`
|
## `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.
|
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.
|
||||||
|
|
||||||
@@ -121,12 +150,13 @@ This pattern enables optimizations and makes intent clear to reviewers.
|
|||||||
|
|
||||||
### `const` locals
|
### `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:
|
* 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();
|
||||||
|
```
|
||||||
|
|
||||||
```cpp
|
|
||||||
const uint_fast16_t cols = virtualWidth();
|
|
||||||
const uint_fast16_t rows = virtualHeight();
|
|
||||||
```
|
|
||||||
<!-- HUMAN_ONLY_END -->
|
<!-- HUMAN_ONLY_END -->
|
||||||
### `const` references to avoid copies
|
### `const` references to avoid copies
|
||||||
|
|
||||||
@@ -175,6 +205,12 @@ static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit");
|
|||||||
#error "WLED_MAX_BUSSES exceeds hard limit"
|
#error "WLED_MAX_BUSSES exceeds hard limit"
|
||||||
#endif
|
#endif
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// using static_assert() to validate enumerated types (zero cost at runtime)
|
||||||
|
static_assert(0u == static_cast<uint8_t>(PinOwner::None),
|
||||||
|
"PinOwner::None must be zero, so default array initialization works as expected");
|
||||||
|
```
|
||||||
<!-- HUMAN_ONLY_END -->
|
<!-- HUMAN_ONLY_END -->
|
||||||
|
|
||||||
Prefer `constexpr` over `#define` for typed constants (scope-safe, debuggable). Use `static_assert` instead of `#if … #error` for compile-time validation.
|
Prefer `constexpr` over `#define` for typed constants (scope-safe, debuggable). Use `static_assert` instead of `#if … #error` for compile-time validation.
|
||||||
@@ -486,22 +522,28 @@ Always pair every `esp32SemTake` with a matching `esp32SemGive`. Choose a timeou
|
|||||||
**Important**: Not every shared resource needs a mutex. Some synchronization is guaranteed by the overall control flow. For example, `volatile bool` flags like `suspendStripService`, `doInitBusses`, `loadLedmap`, and `OTAisRunning` (declared in `wled.h`) are checked sequentially in the main loop (`wled.cpp`), so they serialize access without requiring a semaphore. Use mutexes when true concurrent access from multiple FreeRTOS tasks is possible and race-conditions can lead to unexpected behaviour. Rely on control-flow ordering when operations are sequenced within the same loop iteration.
|
**Important**: Not every shared resource needs a mutex. Some synchronization is guaranteed by the overall control flow. For example, `volatile bool` flags like `suspendStripService`, `doInitBusses`, `loadLedmap`, and `OTAisRunning` (declared in `wled.h`) are checked sequentially in the main loop (`wled.cpp`), so they serialize access without requiring a semaphore. Use mutexes when true concurrent access from multiple FreeRTOS tasks is possible and race-conditions can lead to unexpected behaviour. Rely on control-flow ordering when operations are sequenced within the same loop iteration.
|
||||||
|
|
||||||
### `delay()` vs `yield()` in FreeRTOS Tasks
|
### `delay()` vs `yield()` in FreeRTOS Tasks
|
||||||
|
<!-- HUMAN_ONLY_START -->
|
||||||
|
* 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()` stalled the entire system unless `yield()` was called inside.
|
||||||
|
<!-- HUMAN_ONLY_END -->
|
||||||
|
|
||||||
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.
|
- 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.
|
||||||
|
- **`yield()` is a no-op in WLED-MM on ESP32.** `WLEDMM_FASTPATH` redefines `yield()` to an empty macro.
|
||||||
|
```cpp
|
||||||
|
#define yield() {} // WLEDMM: yield() is completely unnecessary on ESP32
|
||||||
|
```
|
||||||
|
|
||||||
**`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.
|
### IDLE Watchdog and Custom Tasks on ESP32
|
||||||
|
|
||||||
**`yield()` is a no-op in WLED-MM on ESP32.** `WLEDMM_FASTPATH` redefines `yield()` to an empty macro:
|
- **Do NOT use `yield()` to pace ESP32 tasks or assume it feeds any watchdog**.
|
||||||
|
|
||||||
```cpp
|
- Even in stock arduino-esp32, `yield()` calls `vTaskDelay(0)`, which only switches to tasks at equal or higher priority — the IDLE task (priority 0) is never reached.
|
||||||
#define yield() {} // WLEDMM: yield() is completely unnecessary on ESP32
|
|
||||||
```
|
|
||||||
|
|
||||||
Even in stock 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. See `esp-idf.instructions.md` for a full explanation of what IDLE does. Structure custom tasks like this:
|
||||||
```cpp
|
```cpp
|
||||||
// WRONG — IDLE task is never scheduled; yield() does not feed the idle task watchdog.
|
// WRONG — IDLE task is never scheduled; yield() does not feed the idle task watchdog.
|
||||||
void myTask(void*) {
|
void myTask(void*) {
|
||||||
@@ -520,9 +562,8 @@ void myTask(void*) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Prefer blocking FreeRTOS primitives (`xQueueReceive`, `ulTaskNotifyTake`, `vTaskDelayUntil`) over `delay(1)` polling where precise timing or event-driven behaviour is needed.
|
- Prefer blocking FreeRTOS primitives (`xQueueReceive`, `ulTaskNotifyTake`, `vTaskDelayUntil`) over `delay(1)` polling where precise timing or event-driven behaviour is needed.
|
||||||
|
- **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.
|
||||||
**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.
|
|
||||||
|
|
||||||
## General
|
## General
|
||||||
|
|
||||||
@@ -530,7 +571,8 @@ Prefer blocking FreeRTOS primitives (`xQueueReceive`, `ulTaskNotifyTake`, `vTask
|
|||||||
- If possible, use `static` for local (C-style) variables and functions (keeps the global namespace clean)
|
- 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
|
- 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
|
- 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
|
```cpp
|
||||||
// Undefined behavior — avoid:
|
// Undefined behavior — avoid:
|
||||||
uint8_t angle = 40.74f * atan2f(dy, dx); // negative float → uint8_t is UB
|
uint8_t angle = 40.74f * atan2f(dy, dx); // negative float → uint8_t is UB
|
||||||
@@ -499,6 +499,9 @@ The ESP32 has an audio PLL for precise sample rates. Rules:
|
|||||||
|
|
||||||
- Not supported on ESP32-C3 (`SOC_I2S_SUPPORTS_PDM_RX` not defined).
|
- Not supported on ESP32-C3 (`SOC_I2S_SUPPORTS_PDM_RX` not defined).
|
||||||
- ESP32-S3 PDM has known issues: sample rate at 50% of expected, very low amplitude.
|
- ESP32-S3 PDM has known issues: sample rate at 50% of expected, very low amplitude.
|
||||||
|
- **16-bit data width**: Espressif's IDF documentation states that in PDM mode the data unit width is always 16 bits, regardless of the configured `bits_per_sample`.
|
||||||
|
- See [espressif/esp-idf#8660](https://github.com/espressif/esp-idf/issues/8660) for the upstream issue.
|
||||||
|
- **Flag `bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT` in PDM mode** — this causes the S3 low-amplitude symptom.
|
||||||
- No clock pin (`I2S_CKPIN = -1`) triggers PDM mode in WLED-MM.
|
- No clock pin (`I2S_CKPIN = -1`) triggers PDM mode in WLED-MM.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -579,13 +582,21 @@ if (!pinManager.allocatePin(myPin, true, PinOwner::UM_MyUsermod)) {
|
|||||||
|
|
||||||
### Microsecond timing
|
### Microsecond timing
|
||||||
|
|
||||||
For high-resolution timing, prefer `esp_timer_get_time()` (microsecond resolution, 64-bit) over `millis()` or `micros()`:
|
For high-resolution timing, prefer `esp_timer_get_time()` (microsecond resolution, 64-bit) over `millis()` or `micros()`.
|
||||||
|
<!-- HUMAN_ONLY_START -->
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <esp_timer.h>
|
#include <esp_timer.h>
|
||||||
int64_t now_us = esp_timer_get_time(); // monotonic, not affected by NTP
|
int64_t now_us = esp_timer_get_time(); // monotonic, not affected by NTP
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **Note**: In arduino-esp32, both `millis()` and `micros()` are thin wrappers around `esp_timer_get_time()` — they share the same monotonic clock source. Prefer the direct call when you need the full 64-bit value or ISR-safe access without truncation:
|
||||||
|
> ```cpp
|
||||||
|
> // arduino-esp32 internals (cores/esp32/esp32-hal-misc.c):
|
||||||
|
> // unsigned long micros() { return (unsigned long)(esp_timer_get_time()); }
|
||||||
|
> // unsigned long millis() { return (unsigned long)(esp_timer_get_time() / 1000ULL); }
|
||||||
|
> ```
|
||||||
|
<!-- HUMAN_ONLY_END -->
|
||||||
<!-- HUMAN_ONLY_START -->
|
<!-- HUMAN_ONLY_START -->
|
||||||
### Periodic timers
|
### Periodic timers
|
||||||
|
|
||||||
@@ -606,6 +617,27 @@ esp_timer_start_periodic(timer, 1000); // 1 ms period
|
|||||||
|
|
||||||
Always prefer `ESP_TIMER_TASK` dispatch over `ESP_TIMER_ISR` unless you need ISR-level latency — ISR callbacks have severe restrictions (no logging, no heap allocation, no FreeRTOS API calls).
|
Always prefer `ESP_TIMER_TASK` dispatch over `ESP_TIMER_ISR` unless you need ISR-level latency — ISR callbacks have severe restrictions (no logging, no heap allocation, no FreeRTOS API calls).
|
||||||
|
|
||||||
|
### Precision waiting: coarse delay then spin-poll
|
||||||
|
|
||||||
|
When waiting for a precise future deadline (e.g., FPS limiting, protocol timing), avoid spinning the entire duration — that wastes CPU and starves other tasks. Instead, yield to FreeRTOS while time allows, then spin only for the final window.
|
||||||
|
<!-- HUMAN_ONLY_START -->
|
||||||
|
```cpp
|
||||||
|
// Wait until 'target_us' (a micros() / esp_timer_get_time() timestamp)
|
||||||
|
long time_to_wait = (long)(target_us - micros());
|
||||||
|
// Coarse phase: yield to FreeRTOS while we have more than ~2 ms remaining.
|
||||||
|
// vTaskDelay(1) suspends the task for one RTOS tick, letting other task run freely.
|
||||||
|
while (time_to_wait > 2000) {
|
||||||
|
vTaskDelay(1);
|
||||||
|
time_to_wait = (long)(target_us - micros());
|
||||||
|
}
|
||||||
|
// Fine phase: busy-poll the last ≤2 ms for microsecond accuracy.
|
||||||
|
// micros() wraps esp_timer_get_time() so this is low-overhead.
|
||||||
|
while ((long)(target_us - micros()) > 0) { /* spin */ }
|
||||||
|
```
|
||||||
|
<!-- HUMAN_ONLY_END -->
|
||||||
|
|
||||||
|
> The threshold (2000 µs as an example) should be at least one RTOS tick (default 1 ms on ESP32) plus some margin. A value of 1500–3000 µs works well in practice.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ADC Best Practices
|
## ADC Best Practices
|
||||||
@@ -672,14 +704,15 @@ RMT drives NeoPixel LED output (via NeoPixelBus) and IR receiver input. Both use
|
|||||||
- New chips (C6, P4) have different RMT channel counts — use `SOC_RMT_TX_CANDIDATES_PER_GROUP` to check availability.
|
- New chips (C6, P4) have different RMT channel counts — use `SOC_RMT_TX_CANDIDATES_PER_GROUP` to check availability.
|
||||||
- The new RMT API requires an "encoder" object (`rmt_encoder_t`) to translate data formats — this is more flexible but requires more setup code.
|
- The new RMT API requires an "encoder" object (`rmt_encoder_t`) to translate data formats — this is more flexible but requires more setup code.
|
||||||
|
|
||||||
|
<!-- HUMAN_ONLY_END -->
|
||||||
---
|
---
|
||||||
|
|
||||||
## Espressif Best Practices (from official examples)
|
## Espressif Best Practices (from official examples)
|
||||||
|
|
||||||
### Error handling
|
### Error handling
|
||||||
|
|
||||||
Always check `esp_err_t` return values. Use `ESP_ERROR_CHECK()` in initialization code, but handle errors gracefully in runtime code:
|
Always check `esp_err_t` return values. Use `ESP_ERROR_CHECK()` in initialization code, but handle errors gracefully in runtime code.
|
||||||
|
<!-- HUMAN_ONLY_START -->
|
||||||
```cpp
|
```cpp
|
||||||
// Initialization — crash early on failure
|
// Initialization — crash early on failure
|
||||||
ESP_ERROR_CHECK(i2s_driver_install(I2S_NUM_0, &config, 0, nullptr));
|
ESP_ERROR_CHECK(i2s_driver_install(I2S_NUM_0, &config, 0, nullptr));
|
||||||
@@ -693,6 +726,17 @@ if (err != ESP_OK) {
|
|||||||
```
|
```
|
||||||
<!-- HUMAN_ONLY_END -->
|
<!-- HUMAN_ONLY_END -->
|
||||||
|
|
||||||
|
For situations between these two extremes — where you want the `ESP_ERROR_CHECK` formatted log message (file, line, error name) but must not abort — use `ESP_ERROR_CHECK_WITHOUT_ABORT()`.
|
||||||
|
|
||||||
|
<!-- HUMAN_ONLY_START -->
|
||||||
|
```cpp
|
||||||
|
// Logs in the same format as ESP_ERROR_CHECK, but returns the error code instead of aborting.
|
||||||
|
// Useful for non-fatal driver calls where you want visibility without crashing.
|
||||||
|
esp_err_t err = ESP_ERROR_CHECK_WITHOUT_ABORT(i2s_set_clk(AR_I2S_PORT, rate, bits, ch));
|
||||||
|
if (err != ESP_OK) return; // handle as needed
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- HUMAN_ONLY_END -->
|
||||||
### Logging
|
### Logging
|
||||||
|
|
||||||
WLED-MM uses its own logging macros — **not** `ESP_LOGx()`. For application-level code, always use the WLED-MM macros defined in `wled.h`:
|
WLED-MM uses its own logging macros — **not** `ESP_LOGx()`. For application-level code, always use the WLED-MM macros defined in `wled.h`:
|
||||||
@@ -706,13 +750,14 @@ All of these wrap `Serial` output through the `DEBUGOUT` / `DEBUGOUTLN` / `DEBUG
|
|||||||
|
|
||||||
**Exception — low-level driver code**: When writing code that interacts directly with ESP-IDF APIs (e.g., I2S initialization, RMT setup), use `ESP_LOGx()` macros instead. They support tag-based filtering and compile-time log level control:
|
**Exception — low-level driver code**: When writing code that interacts directly with ESP-IDF APIs (e.g., I2S initialization, RMT setup), use `ESP_LOGx()` macros instead. They support tag-based filtering and compile-time log level control:
|
||||||
|
|
||||||
|
<!-- HUMAN_ONLY_START -->
|
||||||
```cpp
|
```cpp
|
||||||
static const char* TAG = "my_module";
|
static const char* TAG = "my_module";
|
||||||
ESP_LOGI(TAG, "Initialized with %d buffers", count);
|
ESP_LOGI(TAG, "Initialized with %d buffers", count);
|
||||||
ESP_LOGW(TAG, "PSRAM not available, falling back to DRAM");
|
ESP_LOGW(TAG, "PSRAM not available, falling back to DRAM");
|
||||||
ESP_LOGE(TAG, "Failed to allocate %u bytes", size);
|
ESP_LOGE(TAG, "Failed to allocate %u bytes", size);
|
||||||
```
|
```
|
||||||
|
<!-- HUMAN_ONLY_END -->
|
||||||
### Task creation and pinning
|
### Task creation and pinning
|
||||||
|
|
||||||
<!-- HUMAN_ONLY_START -->
|
<!-- HUMAN_ONLY_START -->
|
||||||
64
docs/rfp-esp32s3-wroom1-n16r8-3x106.md
Normal file
64
docs/rfp-esp32s3-wroom1-n16r8-3x106.md
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# RFP ESP32-S3 WROOM-1 N16R8 (3 x 106)
|
||||||
|
|
||||||
|
This repository includes a tracked PlatformIO target for the RFP ESP32-S3 WROOM-1 N16R8 nodes with three LED outputs and 106 pixels per output.
|
||||||
|
|
||||||
|
Build target:
|
||||||
|
|
||||||
|
- `rfp_esp32s3_wroom1_n16r8_3x106`
|
||||||
|
|
||||||
|
Default output pins:
|
||||||
|
|
||||||
|
- Output 1: `GPIO4`
|
||||||
|
- Output 2: `GPIO5`
|
||||||
|
- Output 3: `GPIO6`
|
||||||
|
|
||||||
|
Pins intentionally avoided:
|
||||||
|
|
||||||
|
- `GPIO0`, `GPIO3`, `GPIO45`, `GPIO46` for boot / strapping
|
||||||
|
- `GPIO19`, `GPIO20` for USB
|
||||||
|
- `GPIO33` to `GPIO37` because they are reserved by octal PSRAM / flash on `N16R8`
|
||||||
|
- `GPIO48` because it is used as the onboard status pixel
|
||||||
|
|
||||||
|
Build:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python -m platformio run -e rfp_esp32s3_wroom1_n16r8_3x106
|
||||||
|
```
|
||||||
|
|
||||||
|
Build and upload:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\tools\flash_rfp_s3.ps1 -ComPort COM7
|
||||||
|
```
|
||||||
|
|
||||||
|
Build only with the helper script:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\tools\flash_rfp_s3.ps1 -BuildOnly
|
||||||
|
```
|
||||||
|
|
||||||
|
Local Wi-Fi defaults:
|
||||||
|
|
||||||
|
- Keep SSID and password in the ignored file `wled00/my_config.h`.
|
||||||
|
- If the file does not exist yet, create it with your local values:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define CLIENT_SSID "your-ssid"
|
||||||
|
#define CLIENT_PASS "your-password"
|
||||||
|
```
|
||||||
|
|
||||||
|
Important:
|
||||||
|
|
||||||
|
- The `3 x 106` bus layout is used as the default when the device has no saved `cfg.json`.
|
||||||
|
- If a board already has a saved WLED config, do a factory reset or erase settings once so the default bus layout is recreated.
|
||||||
|
|
||||||
|
Onboard status pixel on `GPIO48`:
|
||||||
|
|
||||||
|
- `green blinking`: DDP realtime active
|
||||||
|
- `green solid`: network connected
|
||||||
|
- `blue blinking`: AP / setup mode active
|
||||||
|
- `red fast blinking`: Wi-Fi configured but currently disconnected
|
||||||
|
- `amber fast blinking`: network connected, MQTT configured, but MQTT not connected
|
||||||
|
- `off`: idle / no status to show
|
||||||
@@ -24,5 +24,6 @@ applyTo: "wled00/data/**"
|
|||||||
|
|
||||||
## Build Integration
|
## Build Integration
|
||||||
|
|
||||||
Files in this directory are processed by `tools/cdata.js` into `wled00/html_*.h` headers.
|
Files in this directory are processed by `tools/cdata.js` into generated headers
|
||||||
Run `npm run build` after any change. **Never edit the generated `html_*.h` files directly.**
|
(`wled00/html_*.h`, `wled00/js_*.h`).
|
||||||
|
Run `npm run build` after any change. **Never edit generated headers directly.**
|
||||||
@@ -107,6 +107,7 @@ default_envs =
|
|||||||
;; === esp32-S3 === with 16MB flash
|
;; === esp32-S3 === with 16MB flash
|
||||||
esp32S3_16MB_PSRAM_M_HUB75 ;; for S3 with 16MB flash, HUB75 supported (MOONHUB HUB75 adapter board)
|
esp32S3_16MB_PSRAM_M_HUB75 ;; for S3 with 16MB flash, HUB75 supported (MOONHUB HUB75 adapter board)
|
||||||
esp32S3_WROOM-2_M ;; for S3 WROOM-2; HUB75 supported
|
esp32S3_WROOM-2_M ;; for S3 WROOM-2; HUB75 supported
|
||||||
|
; rfp_esp32s3_wroom1_n16r8_3x106 ;; RFP ESP32-S3 WROOM-1 N16R8, 3x106 pixels on GPIO 4/5/6
|
||||||
;;
|
;;
|
||||||
;; === esp32-S2 boards ===
|
;; === esp32-S2 boards ===
|
||||||
esp32s2_PSRAM_S ;; OTA-compatible with upstream
|
esp32s2_PSRAM_S ;; OTA-compatible with upstream
|
||||||
@@ -2438,6 +2439,32 @@ build_flags = ${env:esp32S3_8MB_PSRAM_M_opi.build_flags}
|
|||||||
[env:esp32S3_8MB_PSRAM_M] ;; legacy alias
|
[env:esp32S3_8MB_PSRAM_M] ;; legacy alias
|
||||||
extends = env:esp32S3_8MB_PSRAM_M_opi
|
extends = env:esp32S3_8MB_PSRAM_M_opi
|
||||||
|
|
||||||
|
[env:rfp_esp32s3_wroom1_n16r8_3x106]
|
||||||
|
;; RFP ESP32-S3 WROOM-1 N16R8, 16MB flash / 8MB OPI PSRAM, 3 outputs x 106 pixels
|
||||||
|
extends = env:esp32S3_8MB_PSRAM_M_opi
|
||||||
|
board_upload.flash_size = 16MB
|
||||||
|
board_upload.maximum_size = 16777216
|
||||||
|
board_build.partitions = tools/WLED_ESP32_16MB.csv
|
||||||
|
build_unflags = ${env:esp32S3_8MB_PSRAM_M_opi.build_unflags}
|
||||||
|
-D WLED_RELEASE_NAME=esp32S3_8MB_PSRAM_M_opi
|
||||||
|
-D LEDPIN=21
|
||||||
|
-D BTNPIN=0
|
||||||
|
-D RLYPIN=1
|
||||||
|
-D IRPIN=-1
|
||||||
|
-D AUDIOPIN=-1
|
||||||
|
build_flags = ${env:esp32S3_8MB_PSRAM_M_opi.build_flags}
|
||||||
|
-D WLED_RELEASE_NAME=RFP_ESP32S3_N16R8_3x106
|
||||||
|
-D LEDPIN=4
|
||||||
|
-D DATA_PINS=4,5,6
|
||||||
|
-D PIXEL_COUNTS=106,106,106
|
||||||
|
-D DEFAULT_LED_COUNT=106
|
||||||
|
-D STATUSPIXELPIN=48
|
||||||
|
-D STATUSPIXELCOLORORDER=COL_ORDER_GRB
|
||||||
|
-D BTNPIN=-1
|
||||||
|
-D RLYPIN=-1
|
||||||
|
-D IRPIN=-1
|
||||||
|
-D AUDIOPIN=-1
|
||||||
|
|
||||||
[env:esp32S3_8MB_S]
|
[env:esp32S3_8MB_S]
|
||||||
;; MM for ESP32-S3 boards - FASTPATH + optimize for speed; ; HUB75 support included (may still have pin conflicts)
|
;; MM for ESP32-S3 boards - FASTPATH + optimize for speed; ; HUB75 support included (may still have pin conflicts)
|
||||||
extends = esp32_4MB_V4_M_base
|
extends = esp32_4MB_V4_M_base
|
||||||
|
|||||||
44
tools/flash_rfp_s3.ps1
Normal file
44
tools/flash_rfp_s3.ps1
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
param(
|
||||||
|
[string]$ComPort,
|
||||||
|
|
||||||
|
[switch]$BuildOnly
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
$repoRoot = Split-Path -Parent $scriptDir
|
||||||
|
$envName = "rfp_esp32s3_wroom1_n16r8_3x106"
|
||||||
|
$venvPython = Join-Path $repoRoot ".venv\Scripts\python.exe"
|
||||||
|
|
||||||
|
if (-not $BuildOnly -and [string]::IsNullOrWhiteSpace($ComPort)) {
|
||||||
|
throw "ComPort is required unless -BuildOnly is used."
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Test-Path $venvPython) {
|
||||||
|
$pythonCommand = $venvPython
|
||||||
|
$pythonArgs = @()
|
||||||
|
} elseif (Get-Command py -ErrorAction SilentlyContinue) {
|
||||||
|
$pythonCommand = "py"
|
||||||
|
$pythonArgs = @("-3")
|
||||||
|
} elseif (Get-Command python -ErrorAction SilentlyContinue) {
|
||||||
|
$pythonCommand = "python"
|
||||||
|
$pythonArgs = @()
|
||||||
|
} else {
|
||||||
|
throw "No Python runtime found. Install Python or create .venv first."
|
||||||
|
}
|
||||||
|
|
||||||
|
$pioHome = Join-Path $repoRoot ".piohome"
|
||||||
|
$env:PLATFORMIO_CORE_DIR = $pioHome
|
||||||
|
$env:PLATFORMIO_PACKAGES_DIR = Join-Path $pioHome "packages"
|
||||||
|
$env:PLATFORMIO_PLATFORMS_DIR = Join-Path $pioHome "platforms"
|
||||||
|
$env:PLATFORMIO_CACHE_DIR = Join-Path $pioHome ".cache"
|
||||||
|
$env:PLATFORMIO_BUILD_CACHE_DIR = Join-Path $pioHome "buildcache"
|
||||||
|
|
||||||
|
$args = @("-m", "platformio", "run", "-e", $envName)
|
||||||
|
if (-not $BuildOnly) {
|
||||||
|
$args += @("-t", "upload", "--upload-port", $ComPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
& $pythonCommand @pythonArgs @args
|
||||||
|
exit $LASTEXITCODE
|
||||||
@@ -168,6 +168,9 @@ String PinManagerClass::getPinSpecialText(int gpio) { // special purpose PIN in
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(STATUSPIXELPIN)
|
||||||
|
if (gpio == STATUSPIXELPIN) return(F("WLED Status Pixel"));
|
||||||
|
#endif
|
||||||
#if defined(STATUSLED)
|
#if defined(STATUSLED)
|
||||||
if (gpio == STATUSLED) return(F("WLED Status LED"));
|
if (gpio == STATUSLED) return(F("WLED Status LED"));
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
171
wled00/wled.cpp
171
wled00/wled.cpp
@@ -71,6 +71,73 @@
|
|||||||
#endif
|
#endif
|
||||||
// WLEDMM end
|
// WLEDMM end
|
||||||
|
|
||||||
|
#if defined(STATUSLED) || defined(STATUSPIXELPIN)
|
||||||
|
static inline void writeStatusIndicator(uint32_t color) {
|
||||||
|
#if defined(STATUSPIXELPIN)
|
||||||
|
if (statusPixelBus != nullptr) {
|
||||||
|
if (statusPixelBus->canShow()) {
|
||||||
|
statusPixelBus->setPixelColor(0, color);
|
||||||
|
statusPixelBus->show();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(STATUSLED)
|
||||||
|
#if STATUSLED >= 0
|
||||||
|
#ifdef STATUSLEDINVERTED
|
||||||
|
digitalWrite(STATUSLED, color ? LOW : HIGH);
|
||||||
|
#else
|
||||||
|
digitalWrite(STATUSLED, color ? HIGH : LOW);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
busses.setStatusPixel(color);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(STATUSPIXELPIN)
|
||||||
|
#ifndef STATUSPIXELCOLORORDER
|
||||||
|
#define STATUSPIXELCOLORORDER COL_ORDER_GRB
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void initStatusPixelBus() {
|
||||||
|
if (statusPixelBus != nullptr) return;
|
||||||
|
if (pinManager.isPinAllocated(STATUSPIXELPIN)) {
|
||||||
|
USER_PRINTF("Skipping status pixel on GPIO %u because the pin is already in use.\n", STATUSPIXELPIN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
|
constexpr uint8_t maxStatusPixelBusses = 4;
|
||||||
|
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||||
|
constexpr uint8_t maxStatusPixelBusses = 2;
|
||||||
|
#else
|
||||||
|
constexpr uint8_t maxStatusPixelBusses = 8;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint8_t busIndex = busses.getNumBusses();
|
||||||
|
if (busIndex >= maxStatusPixelBusses) {
|
||||||
|
USER_PRINTLN(F("Skipping status pixel because no free hardware LED channel is left."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t pins[] = {STATUSPIXELPIN};
|
||||||
|
BusConfig statusCfg(TYPE_WS2812_RGB, pins, 0, 1, STATUSPIXELCOLORORDER, false, 0, RGBW_MODE_MANUAL_ONLY);
|
||||||
|
statusPixelBus = new BusDigital(statusCfg, busIndex, busses.getColorOrderMap());
|
||||||
|
if (statusPixelBus == nullptr || !statusPixelBus->isOk()) {
|
||||||
|
delete statusPixelBus;
|
||||||
|
statusPixelBus = nullptr;
|
||||||
|
USER_PRINTLN(F("Failed to initialize onboard status pixel."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
statusPixelBus->setBrightness(255, true);
|
||||||
|
writeStatusIndicator(0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#if INCLUDE_xTaskGetHandle && defined(ARDUINO_ARCH_ESP32) && (defined(WLED_DEBUG) || defined(WLED_DEBUG_HEAP))
|
#if INCLUDE_xTaskGetHandle && defined(ARDUINO_ARCH_ESP32) && (defined(WLED_DEBUG) || defined(WLED_DEBUG_HEAP))
|
||||||
// WLEDMM stack debug tool - find async_tcp task, and queries it's free stack
|
// WLEDMM stack debug tool - find async_tcp task, and queries it's free stack
|
||||||
@@ -883,6 +950,9 @@ void WLED::setup()
|
|||||||
|
|
||||||
DEBUG_PRINTLN(F("Initializing strip"));
|
DEBUG_PRINTLN(F("Initializing strip"));
|
||||||
beginStrip();
|
beginStrip();
|
||||||
|
#if defined(STATUSPIXELPIN)
|
||||||
|
initStatusPixelBus();
|
||||||
|
#endif
|
||||||
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(getFreeHeapSize());
|
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(getFreeHeapSize());
|
||||||
|
|
||||||
USER_PRINTLN(F("\nUsermods setup ..."));
|
USER_PRINTLN(F("\nUsermods setup ..."));
|
||||||
@@ -1568,58 +1638,79 @@ void WLED::handleConnection()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If status LED pin is allocated for other uses, does nothing
|
// If status LED pin is allocated for other uses, does nothing
|
||||||
// else blink at 1Hz when WLED_CONNECTED is false (no WiFi, ?? no Ethernet ??)
|
// green blink = DDP realtime active
|
||||||
// else blink at 2Hz when MQTT is enabled but not connected
|
// blue blink = AP active
|
||||||
// else turn the status LED off
|
// green solid = network connected
|
||||||
|
// amber fast blink = MQTT configured but disconnected
|
||||||
|
// red fast blink = WiFi configured but currently disconnected
|
||||||
|
// off = idle / no status to show
|
||||||
void WLED::handleStatusLED()
|
void WLED::handleStatusLED()
|
||||||
{
|
{
|
||||||
#if defined(STATUSLED)
|
#if defined(STATUSLED) || defined(STATUSPIXELPIN)
|
||||||
[[maybe_unused]] uint32_t c = 0;
|
uint32_t c = 0;
|
||||||
|
uint8_t nextType = 0;
|
||||||
|
uint16_t blinkIntervalMs = 0;
|
||||||
|
|
||||||
#if STATUSLED>=0
|
#if defined(STATUSLED) && STATUSLED>=0
|
||||||
if (pinManager.isPinAllocated(STATUSLED)) {
|
if (pinManager.isPinAllocated(STATUSLED)) {
|
||||||
return; //lower priority if something else uses the same pin
|
return; //lower priority if something else uses the same pin
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (WLED_CONNECTED) {
|
if (realtimeMode == REALTIME_MODE_DDP) {
|
||||||
c = RGBW32(0,255,0,0);
|
c = RGBW32(0,255,0,0);
|
||||||
ledStatusType = 2;
|
nextType = 2;
|
||||||
} else if (WLED_MQTT_CONNECTED) {
|
blinkIntervalMs = 250;
|
||||||
c = RGBW32(0,128,0,0);
|
|
||||||
ledStatusType = 4;
|
|
||||||
} else if (apActive) {
|
} else if (apActive) {
|
||||||
c = RGBW32(0,0,255,0);
|
c = RGBW32(0,0,255,0);
|
||||||
ledStatusType = 1;
|
nextType = 2;
|
||||||
}
|
blinkIntervalMs = 500;
|
||||||
if (ledStatusType) {
|
} else if (WLED_CONNECTED) {
|
||||||
if (millis() - ledStatusLastMillis >= (1000/ledStatusType)) {
|
#ifndef WLED_DISABLE_MQTT
|
||||||
ledStatusLastMillis = millis();
|
if (mqttEnabled && mqttServer[0] != 0 && !WLED_MQTT_CONNECTED) {
|
||||||
#if 1
|
c = RGBW32(255,96,0,0);
|
||||||
// WLEDMM un-comment this to stop the blinking
|
nextType = 3;
|
||||||
if ((ledStatusType != 2) && (ledStatusType != 4))
|
blinkIntervalMs = 250;
|
||||||
ledStatusState = !ledStatusState;
|
} else
|
||||||
else
|
|
||||||
ledStatusState = HIGH;
|
|
||||||
#else
|
|
||||||
ledStatusState = !ledStatusState;
|
|
||||||
#endif
|
|
||||||
#if STATUSLED>=0
|
|
||||||
digitalWrite(STATUSLED, ledStatusState);
|
|
||||||
#else
|
|
||||||
busses.setStatusPixel(ledStatusState ? c : 0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
#if STATUSLED>=0
|
|
||||||
#ifdef STATUSLEDINVERTED
|
|
||||||
digitalWrite(STATUSLED, HIGH);
|
|
||||||
#else
|
|
||||||
digitalWrite(STATUSLED, LOW);
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
busses.setStatusPixel(0);
|
|
||||||
#endif
|
#endif
|
||||||
|
{
|
||||||
|
c = RGBW32(0,255,0,0);
|
||||||
|
nextType = 1;
|
||||||
|
}
|
||||||
|
} else if (WLED_WIFI_CONFIGURED) {
|
||||||
|
c = RGBW32(255,0,0,0);
|
||||||
|
nextType = 3;
|
||||||
|
blinkIntervalMs = 250;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextType != ledStatusType) {
|
||||||
|
ledStatusType = nextType;
|
||||||
|
ledStatusLastMillis = millis();
|
||||||
|
ledStatusState = (nextType == 1);
|
||||||
|
writeStatusIndicator(ledStatusState ? c : 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextType == 0) {
|
||||||
|
if (ledStatusState) {
|
||||||
|
ledStatusState = false;
|
||||||
|
writeStatusIndicator(0);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextType == 1) {
|
||||||
|
if (!ledStatusState) {
|
||||||
|
ledStatusState = true;
|
||||||
|
writeStatusIndicator(c);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (millis() - ledStatusLastMillis >= blinkIntervalMs) {
|
||||||
|
ledStatusLastMillis = millis();
|
||||||
|
ledStatusState = !ledStatusState;
|
||||||
|
writeStatusIndicator(ledStatusState ? c : 0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -751,12 +751,15 @@ WLED_GLOBAL bool doSerializeConfig _INIT(false); // flag to initiate savi
|
|||||||
WLED_GLOBAL bool doReboot _INIT(false); // flag to initiate reboot from async handlers
|
WLED_GLOBAL bool doReboot _INIT(false); // flag to initiate reboot from async handlers
|
||||||
WLED_GLOBAL bool doPublishMqtt _INIT(false);
|
WLED_GLOBAL bool doPublishMqtt _INIT(false);
|
||||||
|
|
||||||
// status led
|
// status led / status pixel
|
||||||
#if defined(STATUSLED)
|
#if defined(STATUSLED) || defined(STATUSPIXELPIN)
|
||||||
WLED_GLOBAL unsigned long ledStatusLastMillis _INIT(0);
|
WLED_GLOBAL unsigned long ledStatusLastMillis _INIT(0);
|
||||||
WLED_GLOBAL uint8_t ledStatusType _INIT(0); // current status type - corresponds to number of blinks per second
|
WLED_GLOBAL uint8_t ledStatusType _INIT(0); // 0=off, 1=solid, 2=slow blink, 3=fast blink
|
||||||
WLED_GLOBAL bool ledStatusState _INIT(false); // the current LED state
|
WLED_GLOBAL bool ledStatusState _INIT(false); // the current LED state
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(STATUSPIXELPIN)
|
||||||
|
WLED_GLOBAL BusDigital* statusPixelBus _INIT(nullptr);
|
||||||
|
#endif
|
||||||
|
|
||||||
// server library objects
|
// server library objects
|
||||||
WLED_GLOBAL AsyncWebServer server _INIT_N(((80)));
|
WLED_GLOBAL AsyncWebServer server _INIT_N(((80)));
|
||||||
|
|||||||
Reference in New Issue
Block a user