Compare commits

...

49 Commits

Author SHA1 Message Date
Will Tatam 4efb02de58 docs: refresh README to reflect current feature set and project state (#5626)
* docs: refresh README to reflect current feature set and project state

* docs: fix HUB75 support note — available on all ESP32 variants, not just S3

* docs: mention network audio sources in AudioReactive feature description

* docs: remove ESP8266 references — EOL platform, not recommended for new installs

* docs: make PayPal donation link clearly attributed to Aircoookie

* docs: add cross-references to kno.wled.ge docs throughout feature list
2026-06-14 08:17:54 +01:00
Will Tatam a4b14c0191 ci: discover and build custom PlatformIO envs from usermods platformi… (#5649)
* ci: discover and build custom PlatformIO envs from usermods platformio_override.ini.sample files

Adds two new jobs to the Usermod CI workflow to address issue #5648:
- get_custom_build_envs: scans all usermods/*/platformio_override.ini.sample files
  and emits a matrix of {usermod, env} pairs by extracting [env:*] section names
- build_custom: builds each discovered environment by copying the .ini.sample as
  platformio_override.ini and running pio run -e <env>

This allows PRs introducing usermods with custom build environments (such as
pixels_dice_tray) to have those environments validated in CI without committing
platformio_override.ini to the repository.

* ci: consolidate usermod build envs into per-usermod platformio_override.ini.sample files

Move usermod-specific PlatformIO environments out of the root
platformio_override.sample.ini and into dedicated files within each
usermod's own directory, making them discoverable by CI:


* run on push

* Fix AHT10_v2 example

* no d1_mini env

* no d1_mini env

* ci: filter usermod matrix to only build changed usermod directories

Instead of building every usermod with a library.json on each PR, use
git diff to identify which usermods/ subdirectories were actually touched
and intersect that with the known-good library.json list.

This reduces CI time significantly for PRs that only modify one or two
usermods (previously every PR triggered ~40 usermods × 4 chipsets).

Also removes the unnecessary PlatformIO install from get_usermod_envs
(the step only uses shell/jq, not pio) and adds fetch-depth: 0 to
ensure the base branch is available for the diff.

* SN_Photoresistor

* fix(ci): use PR base SHA for diff and guard jobs against push events

github.base_ref is empty on push events, causing 'ambiguous argument
origin/...HEAD'. Fix by:
- Adding github.event_name == 'pull_request' guard to both jobs so
  they never run on push events where pull_request context is absent
- Replacing origin/${{ github.base_ref }}...HEAD with
  ${{ github.event.pull_request.base.sha }} HEAD which uses the
  concrete base commit SHA provided by GitHub directly

* ESP32 builds all V4

* fix: rename/fix platformio_override sample files across usermods

- Rename *.ini (gitignored) and *.sample.ini (wrong extension order) to
  the correct platformio_override.ini.sample convention so CI discovers them
- Fix AHT10_v2: custom_usermods AHT10 → AHT10_v2 (match library.json name)
- Fix INA226_v2: custom_usermods INA226 → INA226_v2 (both envs)
- Fix TTGO-T-Display: replace direct [env:esp32dev] override with a named
  env that extends esp32dev; add note that library.json is absent so
  custom_usermods is not available for this usermod

Affected usermods: AHT10_v2, DHT, INA226_v2, SN_Photoresistor,
TTGO-T-Display, Temperature, four_line_display_ALT, rotary_encoder_ui_ALT

* perf: filter custom build matrix to changed usermods on PRs

On pull_request events, get_custom_build_envs now only scans usermod
directories that changed in the PR (matching the behaviour of the
get_usermod_envs job). On push events (e.g. merging a PR) it still
scans all usermods to validate the full set.

* fix release name

* fix release name

* fix: resolve 8 CI build failures in custom usermod environments

- BME280_v2: fix default_envs typo (usermod_bme280_esp8266_2m →
  usermod_esp8266_2m to match actual [env:] section name)
- DHT: replace extends env:custom32_LEDPIN_16 (not in this fork) with
  env:esp32dev + -D LEDPIN=16; rename env accordingly
- SN_Photoresistor: replace ${common.build_flags_esp8266} (key absent in
  this fork) with ${env:esp8266_2m.build_flags} / lib_deps equivalent
- sht: guard ESP.getChipModel() behind #ifdef ARDUINO_ARCH_ESP32 — method
  does not exist on ESP8266 (fixes custom_esp8266_2m_usermod_sht build)
- EleksTube_IPS: delete platformio_override.ini.sample — library.json is
  disabled so the env cannot build; delete rather than ship a broken sample
- pixels_dice_tray: exclude from build_custom matrix (same as standard
  matrix) — BLE library incompatibility with current IDF causes build failure
- workflow: add PWM_fan / BME68X_v2 to exclusion list in get_custom_build_envs
  for consistency with get_usermod_envs

* Do not add platformio_override.ini.sample just because example was in readme

---------

Co-authored-by: Frank Möhle <91616163+softhack007@users.noreply.github.com>
2026-06-12 08:58:09 +01:00
Frank Möhle d8e3578084 disable fb infer in coderabbit tools list 2026-06-09 14:12:50 +02:00
Frank Möhle 30abc01c0c Remove outdated Wiki link from CONTRIBUTING.md
Removed link to WIKI for submitting a PR. The information in the wiki is outdated, and it contradicts the "no force-push" rule.
2026-06-08 17:42:40 +02:00
Frank Möhle dea6ec0e4c Update links in CONTRIBUTING.md to reference AGENTS.md 2026-06-08 10:29:55 +02:00
Frank Möhle dfd78f4903 Clarify branch maintenance status in instructions
Updated branch descriptions for clarity and maintenance status.
2026-06-08 10:23:57 +02:00
Frank Möhle 5f7910742e Clarify repository language requirement
Emphasize that the repository language is English for all aspects including code, comments, and documentation.
2026-06-05 22:22:46 +02:00
Frank Möhle 3a5026886f Disable auto_apply_labels in coderabbit configuration
has produced too many random-looking labels..
2026-06-05 00:26:20 +02:00
Frank Möhle 1dbceed9dc Update recommendations for synchronization methods
added std::atomic
2026-06-04 23:50:53 +02:00
Frank Möhle be264fb210 Update verification instructions for AI-generated code
limit "existence" check to preprocessor macros etc.
If a global variable or function does not exist, this would lead to a compilation error.
2026-06-04 16:42:17 +02:00
Frank Möhle 7bd387444d Merge pull request #5665 from wled/hub75_gamma_hotfix
HUB75 hotfix: prevents double-applying color corrections (WLED gamma + HUB75 internal CIE correction), by disabling driver-internal color corrections.
2026-06-04 15:03:25 +02:00
Frank 174ff022cb HUB75 hotfix: prevent double-applying gamma correction
disables the driver-internal CIE color correction.

partial solution for #5664
2026-06-04 14:53:20 +02:00
Will Tatam bd0ebf90c0 Merge pull request #5657 from Will-wastelander/main
Adding support for Waveshare ESP32-S3-RGB-Matrix
2026-06-02 19:52:36 +01:00
Damian Schneider 0a1a7fc26f update version name to Kagayaki 2026-05-31 20:36:49 +02:00
Dimitry 659f698144 Refactor ADS1115 usermod loop logic and update documentation (#5655) 2026-06-01 06:29:21 +02:00
Frank bf948fadb5 platformio: allow building with espressif framework (instead of tasmota)
* add necessary platform and platform_packages entries (commented out)
* compatibility patch for ADC_ATTEN_DB_11
2026-05-31 22:19:07 +02:00
Copilot d884a3e692 Pin andelf/nightly-release tospecific commit SHA instead of @main branch (#5386)
The nightly release GitHub Action is pinned from the mutable main branch to a specific immutable commit SHA

we should update the pin when v2 comes out.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: netmindz <442066+netmindz@users.noreply.github.com>
Co-authored-by: Frank Möhle <91616163+softhack007@users.noreply.github.com>
2026-05-30 23:38:04 +02:00
Frank 82c4ce580e move new buildenv into HUB75 section 2026-05-30 22:06:00 +02:00
Frank bb495f7879 buildenv bugfix, better release_name
* un-set previous RELEASE_NAME before setting a new one
* align REALEASE_NAME with naming scheme used by other HUB75 builds
2026-05-30 22:02:24 +02:00
Will 6bd90abac7 Clean up build flags in platformio.ini
Removed unused build unflags.
2026-05-30 12:37:41 -07:00
Will d6482e99bb Remove upload settings from waveshare_esp32s3_32MB_hub75
Removed upload speed and port settings for the waveshare_esp32s3_32MB_hub75 environment.
2026-05-30 12:20:02 -07:00
Will c399d712c9 Remove unused build flags from platformio.ini
Removed flags for fast functions and CIE1931 from build configuration.
2026-05-30 12:19:10 -07:00
Will fd162cee43 Modify build flags in platformio.ini for ESP32-S3
Updated build flags for the waveshare_esp32s3_32MB_hub75 environment, removing some flags and adding new ones.
2026-05-30 08:28:34 -07:00
Frank Möhle 43278a5d0f Update frequency details for usermod loop()
Clarified frequency of calls for usermod loop() based on system load.
2026-05-30 13:04:20 +02:00
Will bd8a16a6f3 Update upload port and build flags in platformio.ini 2026-05-29 21:14:18 -07:00
Will 73898ddf5d Add new environment for Waveshare ESP32-S3 hub75
https://www.waveshare.com/esp32-s3-rgb-matrix.htm
https://docs.waveshare.com/ESP32-S3-RGB-Matrix
2026-05-29 21:00:20 -07:00
Will cdf1f80a81 Add support for Waveshare S3 Matrix Driver board
https://www.waveshare.com/esp32-s3-rgb-matrix.htm
https://docs.waveshare.com/ESP32-S3-RGB-Matrix
2026-05-29 20:55:36 -07:00
Frank Möhle 1825ce7fec Remove duplicate sentence in secure code instructions
Removed redundant text regarding destination capacity and length safety.
2026-05-30 02:35:56 +02:00
Frank Möhle b4747a827b Restructure usermod documentation for AI, and clarify details 2026-05-30 02:26:59 +02:00
Frank e2708be4a9 bugfix
don't modify tpmPayloadFrameSize, its a global value.
2026-05-30 01:21:13 +02:00
Frank 79bfc8b223 harden UDP packet parsing with bounds checks
some offsets are calculated from the incoming packet (untrusted ingress) - make sure that these do not lead to out-of-bounds array indices.
2026-05-30 01:15:07 +02:00
Frank f98e19c965 more robustness for improv - gracefully handle broken input 2026-05-30 00:16:07 +02:00
Frank Möhle 5d00dd0394 Update caveats and pitfalls in cpp.instructions.md
WLED-MM backport: Clarify operator precedence in C/C++.
2026-05-29 20:26:34 +02:00
Frank Möhle beaa5001b6 small update 2026-05-29 19:54:52 +02:00
Frank Möhle 33fe82c207 Modify verification step for numerical accuracy
small clarifications:
* not only applicable to fixed-point
* added sentence to explain why this rule is needed.
2026-05-29 19:53:52 +02:00
Frank Möhle 36fa42d7ea Update auth checks for '/reset' endpoint clarification
Clarified the handling of the '/reset' endpoint in the auth checks section - not security critical because it will not alter any user data.
2026-05-29 18:30:15 +02:00
Jordan W. Cobb 555d0cfbf7 animated staircase: support for inverted logic PIR pins (#5300)
* Added support for inverting logic of PIR pins
* Fixed indentations and removed whitespace
* Added newline to EOF
2026-05-29 16:54:23 +02:00
Frank Möhle 090ab59a9c HUB75: use FM6124 driver for 4-scan panels, remove 64x64 limitation if board has PSRAM (partial solution for #5628) (#5647)
* HUB75: use FM6124 driver for 4-scan panels
* Remove 1-panel limit if the board has PSRAM
2026-05-29 16:48:00 +02:00
kaibae19 c0a90ea470 fix(FX): restore palette wrap in color_wheel() (regression since 0.15.x) (#5646)
color_wheel() historically called color_from_palette() with moving=true, so
under the default palette blend mode (paletteBlend=0, "wrap if moving") the
palette's end->start seam was interpolated. Commit ee9ac947 changed that call
to moving=false, dropping the wrap; effects that scroll a palette (e.g. the
Palette effect with Retro Clown) then shows the unblended seam travelling
visibly along the strip light-by-light.

Restore the original moving=true (per maintainer review). This fixes the seam
for the Palette effect and every other color_wheel() caller under the default
blend mode, without adding any new function parameters.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 16:39:42 +02:00
Benjam Welker 7a37579ef8 fix button label regression (#5636)
* fix button label regression, also add switch notation
2026-05-29 16:32:27 +02:00
Copilot 58bf4c83e9 add secure coding guides for AI reviews (#5572)
Added security review guidelines and a short checklist covering critical security areas including buffer safety, input validation, authentication, secure defaults, and protection against common vulnerabilities. Refined rule wording and priorities to better fit WLED’s technical constraints and realistic deployment model.

The lists are based on the OWASP "top 10" from https://github.com/github/awesome-copilot/blob/main/instructions/security-and-owasp.instructions.md, and on lessons learned from past reviews.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: softhack007 <91616163+softhack007@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-05-29 13:59:35 +02:00
Frank 69c2b2d5c2 human-readable wifi events (WLED_DEBUG)
added some missing event names.
2026-05-27 23:55:23 +02:00
Frank 830f5df465 extra debug info for wifi disconnect / teardown / setTxPower
should help with debugging connection problems.
2026-05-27 23:25:43 +02:00
Frank ce249aef64 small addition to anti-slop rules
AI tools often delete code, remove comments, revert recent changes, or modify files that are out-of-scope w.r.t. the PR objectives.
2026-05-27 20:37:34 +02:00
Will Miles ff97ad5fe0 Merge pull request #5644 from DedeHai/overridesample_fixes
change -D USERMOD_x to custom_usermods = xxx
2026-05-26 16:13:51 -04:00
Frank 68b61f0831 adding coderabbit checks for typical AI slop 2026-05-25 22:17:08 +02:00
Frank 6cdf650535 update usermod guidelines, update example with "be nice but not too nice" pattern 2026-05-25 21:38:03 +02:00
Frank 0a0800d98d move some generic AI rules from copilot-instructions.md to AGENTS.md 2026-05-25 21:37:18 +02:00
Damian Schneider cc26179e56 change -D USERMOD_x to custom_usermods = 2026-05-23 09:30:56 +02:00
39 changed files with 881 additions and 258 deletions
+151 -10
View File
@@ -6,6 +6,8 @@
# docs/cpp.instructions.md — C++ coding conventions
# docs/web.instructions.md — Web UI coding conventions
# docs/cicd.instructions.md — GitHub Actions / CI-CD conventions
# docs/hardening.instructions.md — basic rules for code hardening and robustness
# docs/securecode.instructions.md — more detailed checklists for common vulnerabilities
#
# 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
@@ -15,7 +17,7 @@ language: en-US
reviews:
# generic review setting, see https://docs.coderabbit.ai/reference/configuration#reference
auto_apply_labels: true
auto_apply_labels: false
# abort_on_close: false
high_level_summary: true
review_status: true
@@ -31,12 +33,19 @@ reviews:
- V5
ignore_title_keywords:
- WIP
tools:
fbinfer:
enabled: false # Arduino.h not available on Linux analysis host
cppcheck:
enabled: true # cppcheck works fine without Arduino headers
clang:
enabled: true # clang tidy likewise works
path_instructions:
- path: "**/*.{cpp,h,hpp,ino}"
instructions: >
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 AGENTS.md and .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.
@@ -44,8 +53,37 @@ reviews:
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.
# disabled - the below instruction has no effect
# When initially reviewing a PR, summarize good practices (top 5) and create a prioritized list of suggested improvements (focus on major ones).
When reviewing PRs labeled "AI" or when source code appears to be AI-generated, perform these additional checks:
1. VERIFY all referenced preprocessor macros, constants and flags exist by searching the codebase - do not trust the AI's claims about what exists.
2. CHECK for reinvention: search for existing functions/patterns that already solve the same problem.
3. CHECK for singleton data (defined but never used) and for dead/disabled code, and suggest to remove them.
4. VERIFY comments match code behavior - AI frequently generates plausible but incorrect comments.
5. VERIFY numerical stability / accuracy of arithmetic expressions. AI is often wrong when it comes to math and numbers.
6. CHECK for implied but weakly justified assumptions - like usermod loop() call frequency - and ask for clarification.
7. FLAG changes that appear unrelated: deleted comments, unnecessary re-formatting or re-factoring, and modifications in files that seem unrelated to the PR description.
# ── Security hardening — firmware (trust-boundary-aware) ────────────────
- path: "wled00/**/*.{cpp,h,hpp,ino}"
instructions: >
Apply the WLED security hardening rules from docs/hardening.instructions.md,
and consult docs/securecode.instructions.md when more details are needed for actionable recommendations.
Trust Boundary Model — enforce input-validation and bounds-checking rules
ONLY at the first untrusted ingress point. Untrusted ingress points are:
- HTTP/JSON API request bodies and query parameters (/json/*, /win, etc.)
- WebSocket message payloads
- UDP datagrams (parsePacket() / recvfrom() and protocol wrappers for
E1.31, DDP, Art-Net, TPM2.net)
- TCP socket reads
- Serial/UART command input
- ESP-NOW raw messages input
A value that has been validated and range-clamped at its ingress handler is
considered TRUSTED for all subsequent WLED core processing. Do NOT flag or suggest
repeated bounds/range checks or internal uses of already-sanitized data.
When it is unclear whether a value has been sanitized upstream, prefer
requesting clarification over raising a false-positive finding.
- path: "wled00/data/**"
instructions: >
@@ -54,8 +92,21 @@ reviews:
Key rules: indent HTML and JavaScript with tabs, CSS with tabs.
Files here are built into wled00/html_*.h and wled00/js_*.h by tools/cdata.js — never
edit those generated headers directly.
# disabled - the below instruction has no effect
# When initially reviewing a PR, summarize good practices (top 5) and create a prioritized list of suggested improvements (focus on major ones).
# ── Security hardening — WebUI (always an ingress/output surface) ────────
- path: "wled00/data/**"
instructions: >
Apply the WLED web UI security rules from docs/securecode.instructions.md
(sections WEB1-WEB7).
The Trust Boundary Model does NOT reduce scope here: the WebUI is both
an ingress point (user input, postMessage, fetched config data) and an
output/rendering surface. Always flag DOM XSS risks, unsafe
innerHTML / document.write / insertAdjacentHTML / outerHTML assignments,
postMessage handlers without origin validation, eval() / new Function(),
unsafe location.href or location.replace() assignments, and DOM insertion
from fetched or config-derived data — regardless of where the data
originates.
- path: "wled00/html_*.h"
instructions: >
@@ -75,8 +126,33 @@ reviews:
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 (docs/cpp.instructions.md).
# disabled - the below instruction has no effect
# When initially reviewing a PR, summarize good practices (top 6) and create a prioritized list of suggested improvements (skip minor ones).
# ── Security hardening — usermods (trust-boundary-aware, narrow scope) ───
- path: "usermods/**/*.{cpp,h,hpp}"
instructions: >
For usermods, the untrusted ingress points are:
- readFromConfig(JsonObject& root) and calls to getJsonValue()
- readFromJsonState(JsonObject& obj) — JSON is parsed, but values are client-supplied
- onMqttMessage(char* topic, char* payload) — raw network strings, no core sanitization
- onEspNowMessage(uint8_t* sender, uint8_t* payload, uint8_t len) — raw radio bytes
- onUdpPacket(uint8_t* payload, size_t len) — raw UDP buffer, no core filtering
Values retrieved at these ingress points are considered trusted only after the
usermod itself has validated and range-clamped them.
Flag ONLY downstream uses of ingress-derived values where an out-of-range or
unexpected value can cause misbehaviour that is not already guarded, for example:
- `switch` statements on an ingress-derived value with no `default` branch,
or with a missing `break` where fall-through is unintentional
- array or buffer indexing with an ingress-derived value where the index is
not clamped before use
- arithmetic with an ingress-derived value that can overflow or produce a
negative result used as a size or count
Do NOT flag:
- getJsonValue() call sites themselves (type coercion is handled by ArduinoJson)
- Internal logic that operates on values already confirmed safe at ingress
- Repeated range checks on values that have already been clamped
- General memory-safety patterns unrelated to ingress-derived data flow
- path: ".github/workflows/*.{yml,yaml}"
instructions: >
@@ -88,8 +164,6 @@ reviews:
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.
# disabled - the below instruction has no effect
# When initially reviewing a PR, summarize good practices (top 6) and create a prioritized list of suggested improvements.
- path: "**/*.instructions.md"
instructions: |
@@ -107,6 +181,73 @@ reviews:
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).
# ── Secrets / sensitive information scanning ────────────────────────────
- path: "platformio*.ini*"
instructions: >
Scan for secrets, passwords, and other sensitive information accidentally
committed to PlatformIO configuration files (platformio.ini,
platformio_override.ini, platformio_override.ini.sample).
Flag any of the following:
- build_flags entries that define credentials as literal values, e.g.:
-DWIFI_SSID=\"<YOUR_SSID>\" -DWIFI_PASS=\"<YOUR_PASSWORD>\"
-DOTA_PASS=\"<OTA_PASSWORD>\" -DMQTT_PASS=\"<MQTT_PASSWORD>\"
Flag only when the value is not a recognisable placeholder (see below).
- upload_flags or upload_port values that embed a password or auth token (e.g., --auth=<PASSWORD> or any URL using credential-bearing userinfo).
- Any key = <value> pair whose key name contains "pass", "password",
"secret", "token", "key", "credential", or "auth" where the value is
a non-empty, non-placeholder literal string.
- Hardcoded IP addresses or hostnames paired with credentials in the
same environment section.
- API keys or access tokens as literal strings in any field.
Do NOT flag:
- Values that are clearly template placeholders (e.g., YOUR_SSID,
<YOUR_PASSWORD>, changeme, example_token, your_password_here).
- Values that use PlatformIO environment variable substitution (${sysenv.WIFI_PASS} or ${env:WIFI_PASS}).
- Comments that only explain what a field should contain.
- platformio_override.ini.sample entries that contain only
placeholder/example values.
- path: "usermods/**/library.json"
instructions: >
Scan for secrets and sensitive information in usermod dependency manifests.
Flag any of the following:
- Dependency URLs that embed credentials in the URL itself (e.g., any URL containing credential-bearing userinfo).
- Personal access tokens, OAuth tokens, or API keys as literal strings
anywhere in the file.
- Values matching well-known secret patterns: GitHub PATs (ghp_...,
github_pat_...), AWS access keys (AKIA...), or similarly structured
high-entropy tokens.
Do NOT flag:
- Plain HTTPS or SSH URLs without embedded credentials.
- Version specifiers, semver ranges, or commit SHA references that
contain no credential prefix.
- Repository owner/name path segments (not credential material).
- path: "usermods/**/{readme,README,Readme}.md"
instructions: >
Scan for secrets, passwords, and sensitive information in usermod
documentation files, including inside code blocks, inline code, and prose.
Flag any of the following:
- Hardcoded Wi-Fi SSID or password values that appear to be real (non-placeholder)
strings in configuration or installation examples.
- Hardcoded OTA, AP, or MQTT passwords in code snippets or step-by-step
instructions.
- API keys, bearer tokens, or access tokens shown as literal values.
- Example platformio_override.ini snippets that contain real-looking
credential values instead of placeholders.
- Hardcoded IP addresses combined with credentials in the same example.
Do NOT flag:
- Values that are clearly template placeholders (e.g., YOUR_SSID,
<password>, my_secret, changeme, ****).
- Generic prose describing what a field means without supplying a value.
- Asterisk-masked values (e.g., ******, ••••••).
finishing_touches:
# Docstrings | Options for generating Docstrings for your PRs/MRs.
docstrings:
+2 -13
View File
@@ -50,7 +50,7 @@ For detailed build timeouts, development workflows, troubleshooting, and validat
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
16_x # maintenance for release 16.x.y
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)
@@ -60,6 +60,7 @@ main # 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``.
- ``16_x``: maintenance for release 16.x.y
- ``0_15_x``: bugfixing / maintenance for release 0.15.x
### Repository Structure
@@ -120,20 +121,8 @@ docs/ # Contributor docs, coding guidelines
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.
- 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, embed it into `// AI: below section was generated by an AI` ... `// AI: end` comments (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 with meaningful comments that explain intent, assumptions, and non-obvious logic. Do not rephrase source code; explain concepts and reasoning.
### 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.
- **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.
- **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.
+1 -1
View File
@@ -34,7 +34,7 @@ jobs:
# Exclude issues that were closed without resolution from changelog
excludeLabels: 'stale,wontfix,duplicate,invalid,external,question,use-as-is,not_planned'
- name: Update Nightly Release
uses: andelf/nightly-release@main
uses: andelf/nightly-release@5834076edc55cc05975561c9722043f072ac5c26
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
+106 -10
View File
@@ -4,6 +4,9 @@ on:
pull_request:
paths:
- usermods/**
push:
paths:
- usermods/**
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
@@ -12,28 +15,40 @@ jobs:
get_usermod_envs:
# Only run for pull requests from forks (not from branches within wled/WLED)
if: github.event.pull_request.head.repo.full_name != github.repository
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
name: Gather Usermods
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- name: Install PlatformIO
run: pip install -r requirements.txt
- name: Get default environments
fetch-depth: 0
- name: Get changed usermod environments
id: envs
run: |
echo "usermods=$(find usermods/ -name library.json | xargs dirname | xargs -n 1 basename | jq -R | grep -v PWM_fan | grep -v BME68X_v2| grep -v pixels_dice_tray | jq --slurp -c)" >> $GITHUB_OUTPUT
# Usermods whose directories changed in this PR
changed=$(git diff --name-only ${{ github.event.pull_request.base.sha }} HEAD \
| grep '^usermods/' | cut -d/ -f2 | sort -u || true)
# All usermods with a library.json (excluding known-incompatible ones)
all=$(find usermods/ -name library.json \
| xargs dirname | xargs -n 1 basename \
| grep -v PWM_fan | grep -v BME68X_v2 | grep -v pixels_dice_tray \
| sort || true)
if [ -z "$changed" ] || [ -z "$all" ]; then
echo "usermods=[]" >> $GITHUB_OUTPUT
else
usermods=$(comm -12 <(echo "$all") <(echo "$changed") | jq -R | jq --slurp -c)
echo "usermods=$usermods" >> $GITHUB_OUTPUT
fi
outputs:
usermods: ${{ steps.envs.outputs.usermods }}
build:
# Only run for pull requests from forks (not from branches within wled/WLED)
if: github.event.pull_request.head.repo.full_name != github.repository
# Skip when no changed usermods were found (e.g. only non-library changes)
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && needs.get_usermod_envs.outputs.usermods != '[]'
name: Build Enviornments
runs-on: ubuntu-latest
needs: get_usermod_envs
@@ -74,4 +89,85 @@ jobs:
cat platformio_override.ini
- name: Build firmware
run: pio run -e ${{ matrix.environment }}
run: pio run -e ${{ matrix.environment }}
get_custom_build_envs:
name: Gather Custom Build Environments
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Find usermods with custom build environments
id: custom_envs
run: |
# On PRs: only scan usermods whose directories changed.
# On push: scan all usermods (validates the full set on merge).
if [ "${{ github.event_name }}" = "pull_request" ]; then
changed=$(git diff --name-only ${{ github.event.pull_request.base.sha }} HEAD \
| grep '^usermods/' | cut -d/ -f2 | sort -u || true)
if [ -z "$changed" ]; then
echo "matrix=[]" >> $GITHUB_OUTPUT
exit 0
fi
samples=$(for mod in $changed; do
f="usermods/$mod/platformio_override.ini.sample"
[ -f "$f" ] && echo "$f"
done | sort)
else
samples=$(find usermods/ -name "platformio_override.ini.sample" | sort)
fi
result='[]'
for sample in $samples; do
usermod=$(dirname "$sample" | xargs basename)
# Skip usermods known to be incompatible (same list as get_usermod_envs)
case "$usermod" in PWM_fan|BME68X_v2|pixels_dice_tray) continue ;; esac
envs=$(grep -E '^\[env:[^]]+\]' "$sample" | sed 's/^\[env:\(.*\)\]$/\1/')
for env in $envs; do
result=$(echo "$result" | jq --arg u "$usermod" --arg e "$env" '. + [{usermod: $u, env: $e}]')
done
done
echo "matrix=$(echo "$result" | jq -c '.')" >> $GITHUB_OUTPUT
outputs:
matrix: ${{ steps.custom_envs.outputs.matrix }}
build_custom:
name: Build Custom Env (${{ matrix.usermod }} / ${{ matrix.env }})
runs-on: ubuntu-latest
needs: get_custom_build_envs
if: needs.get_custom_build_envs.outputs.matrix != '[]'
strategy:
fail-fast: false
matrix:
include: ${{ fromJSON(needs.get_custom_build_envs.outputs.matrix) }}
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- run: npm ci
- name: Cache PlatformIO
uses: actions/cache@v4
with:
path: |
~/.platformio/.cache
~/.buildcache
build_output
key: pio-${{ runner.os }}-${{ matrix.env }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-${{ hashFiles('wled00/**', 'usermods/**') }}
restore-keys: pio-${{ runner.os }}-${{ matrix.env }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- name: Install PlatformIO
run: pip install -r requirements.txt
- name: Apply custom build environment
run: cp -v "usermods/${{ matrix.usermod }}/platformio_override.ini.sample" platformio_override.ini
- name: Build firmware
run: pio run -e ${{ matrix.env }}
+54 -17
View File
@@ -4,7 +4,8 @@ WLED is C++ firmware for ESP32/ESP8266 microcontrollers controlling addressable
with a web UI (HTML/JS/CSS). Built with PlatformIO (Arduino framework) and Node.js tooling.
See also: `.github/copilot-instructions.md`, `.github/agent-build.instructions.md`,
`docs/cpp.instructions.md`, `docs/web.instructions.md`, `docs/cicd.instructions.md`.
`docs/cpp.instructions.md`, `docs/web.instructions.md`, `docs/cicd.instructions.md`,
`docs/hardening.instructions.md`, `docs/securecode.instructions.md`.
Always reference these instructions - including coding guidelines in `docs/` - first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.
@@ -27,7 +28,7 @@ required C headers for firmware compilation.
Tests use Node.js built-in test runner (`node:test`). The single test file is
`tools/cdata-test.js`. Run it with:
```sh
```bash
npm test # runs all tests via `node --test`
node --test tools/cdata-test.js # run just that file directly
```
@@ -41,7 +42,7 @@ target environments. Always build after code changes: `pio run -e esp32dev`.
### Recovery / Troubleshooting
```sh
```bash
npm run build -- -f # force web UI rebuild
rm -f wled00/html_*.h wled00/js_*.h && npm run build # clean + rebuild UI
pio run --target clean # clean PlatformIO build artifacts
@@ -50,7 +51,7 @@ rm -rf node_modules && npm ci # reinstall Node.js deps
## Project Structure
```
```text
wled00/ # Main firmware source (C++)
data/ # Web UI source (HTML/JS/CSS) — tabs for indentation
html_*.h, js_*.h # Auto-generated (NEVER edit or commit)
@@ -132,6 +133,7 @@ main # Main development trunk (daily/nightly) 17.0.0-dev. Target
- **Performance**: Prefer DRAM (or IRAM) for hot-path data that is *frequently* used. Prefer PSRAM for capacity-oriented buffers where slightly slower access times can be tolerated.
Background Info:
- PSRAM access is up to 18× slower than DRAM on ESP32 (dual-SPI bus), 310× slower than DRAM on ESP32-S3/-S2 with quad-SPI bus. On ESP32-S3 with octal PSRAM (`CONFIG_SPIRAM_MODE_OCT`), the penalty is smaller (~2×) because the 8-line DTR bus can transfer 8 bits in parallel. On ESP32-P4 with hex PSRAM (`CONFIG_SPIRAM_MODE_HEX`), the 16-line bus runs at 200 MHz which brings it on-par with DRAM.
- Consider that ESP32 often crashes when the largest DRAM chunk gets below 10 KB.
@@ -158,10 +160,10 @@ Background Info:
#### ESP32 Task Synchronization
* Use FreeRTOS mutexes, semaphores or queues when true concurrent access from multiple FreeRTOS tasks is possible, and race-conditions can lead to unexpected behaviour.
* **Avoid `portENTER_CRITICAL()` / `portEXIT_CRITICAL()`**, as these functions stall the complete system and may cause LEDs flickering. Prefer FreeRTOS mutexes, semaphores or queues.
* **Important**: Not every shared resource needs a mutex. Some synchronization is guaranteed by the overall control flow, for example when function calls are sequenced within the same loop iteration.
* Consider RAII as an alternative to mutexes or semaphores.
- Use FreeRTOS mutexes, semaphores or queues when true concurrent access from multiple FreeRTOS tasks is possible, and race-conditions can lead to unexpected behaviour.
- **Avoid `portENTER_CRITICAL()` / `portEXIT_CRITICAL()`**, as these functions stall the complete system and may cause LEDs flickering. Prefer FreeRTOS mutexes, semaphores or queues.
- **Important**: Not every shared resource needs a mutex. Some synchronization is guaranteed by the overall control flow, for example when function calls are sequenced within the same loop iteration.
- Consider using `std::atomic` or RAII scoped guards as alternatives to mutexes, semaphores or queues.
## Web UI Code Style (wled00/data/)
@@ -181,17 +183,26 @@ class MyUsermod : public Usermod {
bool enabled = false;
static const char _name[];
public:
void setup() override { /* ... */ }
void loop() override { /* ... */ }
void addToConfig(JsonObject& root) override { /* ... */ }
bool readFromConfig(JsonObject& root) override { /* ... */ }
void setup() override { /* ... */ } // runs once at start-up
void loop() override { /* ... */ } // runs once per main loop iteration
void addToConfig(JsonObject& root) override { /* ... */ } // create/add persistent settings (usermod settings)
bool readFromConfig(JsonObject& root) override { /* ... */ } // read from persistent settings (usermod settings UI)
uint16_t getId() override { return USERMOD_ID_MYMOD; }
void addToJsonInfo(JsonObject& root) override { /* ... */ } // Add custom items to the "info" page and to /json/info
void appendConfigData() override { /* ... */ } // Customize the settings page: dropdowns, checkboxes, extra text, etc. Buffer size is limited!
};
const char MyUsermod::_name[] PROGMEM = "MyUsermod";
static MyUsermod myUsermod;
REGISTER_USERMOD(myUsermod);
```
refer to detailed examples in `usermods/EXAMPLE/`, `usermods/user_fx/` and [in the user documentation for custom features](https://kno.wled.ge/advanced/custom-features/).
- Activate via `custom_usermods = ` in platformio build config. The `usermod_v2_` prefix or `_v2` suffix can be omitted.
- Base new usermods on `usermods/EXAMPLE/` (never edit the example directly)
- Store repeated strings as `static const char[] PROGMEM`
- Add usermod IDs to `wled00/const.h` **only when a unique ID is required** (see below)
### Usermod IDs
A unique ID (registered in `wled00/const.h` and overriding `getId()`) is **only required** when a usermod needs one or more of the following:
@@ -202,14 +213,18 @@ A unique ID (registered in `wled00/const.h` and overriding `getId()`) is **only
If none of the above apply, the usermod may omit `getId()` (or return the default `USERMOD_ID_UNSPECIFIED`) and does **not** need an entry in `const.h`.
- Add usermod IDs to `wled00/const.h` **only when a unique ID is required** (see above)
- Activate via `custom_usermods` in platformio build config
- Base new usermods on `usermods/EXAMPLE/` (never edit the example directly)
- Store repeated strings as `static const char[] PROGMEM`
### Usermod `loop()`
- Called once per main loop iteration. Usermods should simply `return` when `!enabled`.
- Frequency of calls varies with system load:
* up to 2000 times/sec with few LEDs and little background activity,
* between 20 and 300 times/second during high workload from effects and other usermods,
* (worst case) down to 1-3 times/sec during FS activity or when serving lots of network API requests.
## CI/CD
CI runs on every push/PR via GitHub Actions (`.github/workflows/wled-ci.yml`):
1. `npm test` (web UI build validation)
2. Firmware compilation for all default environments (~22 targets)
3. Post-link validation of usermod linkage (`validate_modules.py`)
@@ -218,7 +233,7 @@ No automated linting is configured. Match existing code style in files you edit.
## General Rules
- Repository language is English.
- Important: Repository language is **English**. This applies to source code (including comments), commit messages and any kind of documentation for developer or users.
- The `docs/` folder is for developer/contributor information (coding conventions, architecture, etc.). User documentation is maintained in the [wled/WLED-Docs](https://github.com/wled/WLED-Docs) repository.
- Never edit or commit auto-generated `wled00/html_*.h` / `wled00/js_*.h`.
- When updating an existing PR, retain the original description. Only modify it to ensure technical accuracy. Add change logs after the existing description.
@@ -228,3 +243,25 @@ No automated linting is configured. Match existing code style in files you edit.
- Remove dead/unused code — justify or delete it.
- Verify feature-flag spelling exactly (misspellings are silently ignored by preprocessor).
- Provide references when making analyses or recommendations. Support factual claims with verifiable citations, references or concrete evidence; **never fabricate citations**.
- **Highlight user-visible breaking changes and ripple effects** during reviews. Ask for confirmation that these were introduced intentionally.
### Security Hardening
When writing or reviewing code in `wled00/`, `usermods/`, `wled00/data/`, or `.github/workflows/`,
consult `docs/hardening.instructions.md` (concise checklist) and `docs/securecode.instructions.md` (detailed rules with examples).
These files define WLED's threat model, trust boundary model, and WLED-specific constraints (no TLS baseline, no UDP authentication for protocol-defined
multicast/broadcast, firewall-isolated deployment assumed).
### 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, embed it into `// AI: below section was generated by an AI` ... `// AI: end` comments (see Comments section).
- 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 with meaningful comments that explain intent, assumptions, and non-obvious logic. Do not rephrase source code; explain concepts and reasoning.
### 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.
+2 -4
View File
@@ -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](.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)
* [Project Structure, Files and Directories](AGENTS.md#project-structure) (in our AI instructions)
* [Instructions for creating usermods](AGENTS.md#usermod-pattern) (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.
@@ -141,8 +141,6 @@ Sometimes you might hit merge conflicts with `main` that are harder to solve. He
### Additional Resources
Want to know more? Check out:
- 📚 [GitHub Desktop documentation](https://docs.github.com/en/desktop) - if you prefer GUI tools
- 🎓 [How to properly submit a PR](https://github.com/wled-dev/WLED/wiki/How-to-properly-submit-a-PR) - detailed tips and tricks
## After Approval
Once approved, a maintainer will merge your PR (possibly squashing commits).
+2
View File
@@ -515,6 +515,8 @@ void myTask(void*) {
- **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).
- In C/C++, additive operators (`+`, `-`) have HIGHER precedence than shift operators (`<<`, `>>`). Therefore `x - edge0 << 8` correctly parses as `(x - edge0) << 8`. Do NOT flag this pattern as a precedence bug. When reviewing WLED fixed-point code or any C/C++ shift expressions, verify against cppreference before claiming precedence issues with mixed `-`/`+` and `<<`/`>>` expressions.
- **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:
+58
View File
@@ -0,0 +1,58 @@
---
applyTo: "**/*.{cpp,h,hpp,ino,js,htm,html,css,yml,yaml}"
description: "WLED strict-mode security review: low-noise checklist."
---
# WLED Security Review — Low Noise Mode
Use these code hardening rules for automated reviews with minimal false positives.
## WLED Constraints (apply to all rules)
- Assume firewall/DMZ/VPN deployment; focus on realistic LAN-local and supply-chain risks.
- Do **not** require TLS/HTTPS as a baseline control.
- Do **not** require authentication for standards-based UDP multicast/broadcast paths where authentication is not defined in the protocol specification.
> **Trust boundary model**: Apply input-validation rules **only at the first untrusted ingress point**
> (HTTP/JSON API body or query string, WebSocket payload, UDP datagram, TCP read, serial command, ESP-NOW raw messages).
> Values that have been validated and range-clamped at ingress are **trusted** for internal WLED
> processing. Do not flag subsequent uses or internal copies of already-sanitized data.
## CRITICAL Rules
1. **No unchecked buffer copies** (`memcpy`, `memmove`, `strcpy`) in firmware paths when source buffer or size comes from an untrusted origin; prefer bounded alternatives (`strncpy`, `strlcpy`); require length validation before copying.
2. **No user-controlled format strings** in `DEBUG_PRINTF*` and similar logging APIs.
3. **Validate all untrusted external input** (HTTP/JSON/UDP/serial) before index/length/pin usage.
4. **Auth required for state-changing control endpoints where feasible** (for example HTTP/JSON); do not flag protocol-defined unauthenticated UDP multicast/broadcast channels solely for missing auth.
5. **No fail-open on parse/allocation errors** for config/state updates.
6. **No DOM XSS sinks with untrusted data** (`innerHTML`, unsafe HTML insertion). Server-side generation of JavaScript property-assignment statements (as used in WLED's printSetForm* helpers) is exempt.
7. **No dynamic code execution** (`eval`, `new Function`, string timers).
8. **No hardcoded secrets/credentials/tokens/keys** in committed files.
9. **No sensitive data in logs** (passwords, tokens, Wi-Fi secrets, auth headers).
10. **No secret exposure in workflows/log output, or in LittleFS files other than `wsec.json`**.
11. **No unsafe third-party GitHub Action pinning** (`@main`/`@master` disallowed).
12. **No untrusted expression interpolation in workflow shell commands**.
## IMPORTANT Rules
13. Avoid potentially unbounded string/memory operations (`strcmp`, `strchr`, `strlen`, `sprintf`) in firmware paths; prefer bounded alternatives (`strnlen`, `strncmp`, `snprintf`).
14. Check integer overflow risks in size/index arithmetic, but consider that unsigned wrap-around on small types might be intentional.
15. Reject repeated heap allocation churn in hot render/effect loops.
16. Avoid repeated `String` growth in hot paths; prefer bounded/pre-allocated buffers.
17. Ensure UI validation is mirrored by firmware-side validation.
18. Require strict origin checks for `postMessage` listeners.
19. Disallow untrusted redirect/navigation targets.
20. Prevent verbose error responses that leak internals.
21. Review new dependencies for typosquatting and known vulnerability risk.
22. Keep workflow `permissions` least-privilege.
23. Verify new `WLED_ENABLE_*` / `WLED_DISABLE_*` names are valid known flags.
24. New privileged behavior must not be enabled by insecure defaults; first-use default-credential change required where applicable.
25. OTA paths (Update.begin(), Update.write()) must verify firmware integrity (checksum/hash); TLS not required.
26. Flag xTaskCreate/xTaskCreatePinnedToCore tasks with insufficient stack for String/JSON use; flag MDNS.begin() / ArduinoOTA.setHostname() with unsanitized hostnames.
27. Flag API/config serialization that exposes Wi-Fi/AP/MQTT password fields to unauthenticated clients.
28. Treat fetched and config-derived strings as untrusted when inserting into the DOM; explicit sanitization required for HTML contexts.
## Reviewer Output Format
- Include severity, exact file and line, and one concrete fix direction.
- Prioritize CRITICAL findings before IMPORTANT findings.
+227
View File
@@ -0,0 +1,227 @@
---
applyTo: "**/*.{cpp,h,hpp,ino,js,htm,html,css,yml,yaml}"
description: "WLED-focused security review guide based on OWASP Top 10 for embedded firmware and web UI."
---
# WLED Security Review Standards (Embedded + Web UI)
Use this guide for AI-assisted code reviews in:
- `wled00/`
- `usermods/`
- `.github/workflows/`
## WLED Constraints and Threat Model Assumptions
- Assume typical deployment behind a firewall/DMZ/VPN; prioritize LAN-local and supply-chain risks.
- Do **not** require TLS/HTTPS as a baseline control for findings in this repo.
- Do **not** require authentication for standards-based UDP multicast/broadcast protocols where auth is not part of the spec.
- Do not propose mitigations that break protocol compliance just to add authentication.
### Trust Boundary Model
**Untrusted data** enters WLED only at the following explicit ingress points:
- HTTP/JSON API request bodies and query parameters (e.g., `/json/*`, `/win`)
- WebSocket message payloads
- UDP datagrams (`parsePacket()` / `recvfrom()` and higher-level protocol wrappers)
- TCP socket reads
- Serial/UART input used as commands
- ESP-NOW raw messages input
**Validation and range-clamping applied at the ingress point renders data trusted** for all subsequent use within the WLED core.
**Do not flag:**
- Repeated bounds or range checks on a value that has already been validated and clamped at its ingress handler.
- Internal WLED core logic that operates on values confirmed safe by the ingress layer.
If it is unclear whether a value has been sanitized upstream (e.g., passed through multiple function calls without a clear annotation), prefer asking for clarification over raising a false-positive finding.
### Locally-Stored Configuration Files (Robustness, not a primary trust boundary)
Files read from LittleFS (`presets.json`, `cfg.json`, `ledmap.json`, `ir.json`, etc.) are written only via privileged access (`/edit`) and are considered trusted in the threat model.
However, parse them defensively (validate structure, clamp array sizes, handle missing keys gracefully) to avoid bootloops from filesystem corruption or accidental malformation.
## Severity
- **CRITICAL** — exploitable vulnerability; block merge.
- **IMPORTANT** — meaningful risk; fix before or with merge when practical.
- **SUGGESTION** — defense-in-depth; track for follow-up.
## Scope (WLED-relevant)
Prioritize:
- C++ memory safety and input validation
- Auth and access checks for state-changing HTTP/JSON APIs
- XSS and DOM safety in `wled00/data/*`
- Secrets handling (`wsec.json`) and secure logging
- Dependency and GitHub Actions supply-chain hygiene
- Fail-safe behavior on constrained devices
De-prioritize unless explicitly introduced by a PR:
- SQL/NoSQL checks, JWT/OAuth flows, GraphQL-specific checks, generic backend framework checks not used by WLED.
## Firmware Security (C++, OWASP A01/A04/A05/A10)
### FW1: Unsafe buffer operations
- **Severity**: CRITICAL
- Flag `strcpy`, `sprintf`, unchecked memory access (`memcpy`, `memmove`, `memcmp`, `strcmp`, `strlen`), unchecked pointer arithmetic.
- Require explicit bounds checks and length validation.
- Prefer bounded alternatives for string operations (`strnlen`, `strncmp`, `strncpy`, `strlcpy`, `snprintf`).
- Treat a finding against FW1 as **suggestion** only when the operation is provably bounded
and both the destination capacity and copied/compared length are known safe.
### FW2: Format-string injection
- **Severity**: CRITICAL
- Do not pass untrusted input as a format string to `DEBUG_PRINTF*` or similar APIs.
### FW3: Integer overflow in length and offset math
- **Severity**: IMPORTANT
- Review `count * size`, index math, narrowing casts before allocations or copies.
### FW4: Unvalidated external input
- **Severity**: CRITICAL
- At each **untrusted ingress point** (see Trust Boundary Model above), validate and clamp values from HTTP/JSON/UDP/serial before use as lengths, indices, IDs, or pin references.
- Do not flag repeated range checks on values that have already been validated at their ingress point.
- In UDP handlers (`parsePacket()`, `read()`, and any lower-level socket wrappers), validate `packetSize` before buffer writes and clamp protocol-specific universe/channel ranges to valid limits.
### FW5: Missing auth checks on state-changing endpoints (where auth is feasible)
- **Severity**: CRITICAL
- HTTP/JSON and other control paths that support auth must enforce configured auth policy.
- Do not flag the HTTP endpoint `/reset` as state-changing. This endpoint triggers a reboot, causing a short interruption without loss of user data.
- Do not flag standards-based UDP multicast/broadcast paths solely for lacking authentication when authentication is not defined in the protocol specification.
### FW6: Fail-open behavior after parse or allocation errors
- **Severity**: IMPORTANT
- On error, reject update and preserve safe previous state.
- Explicitly check parse status (`DeserializationError error = deserializeJson(...); if (error) return/reject;`) and avoid silently applying unsafe zero/default values to safety-relevant fields (for example LED count and pin assignment).
### FW7: Heap churn in hot paths
- **Severity**: IMPORTANT
- Avoid repeated dynamic allocation in render/effect loops; prefer pre-allocation and reuse.
- Flag allocation patterns in loop and ISR-adjacent paths that can trigger fragmentation or timing instability.
### FW8: Unsafe use of `String` in performance-critical paths
- **Severity**: IMPORTANT
- In hot paths, avoid repeated `String` growth; reserve or use fixed buffers.
- Flag repeated `String` concatenation inside loop-heavy or ISR-adjacent code.
### FW9: Unsafe feature flag names
- **Severity**: IMPORTANT
- Verify all new `WLED_ENABLE_*`/`WLED_DISABLE_*` names are valid known flags; typos silently alter build behavior.
### FW10: OTA integrity verification (without TLS requirement)
- **Severity**: IMPORTANT
- OTA update flows should validate firmware integrity using the checksum/hash/signature mechanism available in the firmware/platform implementation.
- Do not require TLS/certificate pinning as a mandatory review criterion.
- In OTA paths (`Update.begin()`, `Update.write()`, and related flows), flag flashing without integrity verification.
### FW11: FreeRTOS task stack and recursion safety
- **Severity**: IMPORTANT
- In `xTaskCreate`/`xTaskCreatePinnedToCore` tasks that process `String`/JSON-heavy data, verify stack-size sufficiency and avoid unbounded recursion.
### FW12: mDNS and hostname sanitization
- **Severity**: IMPORTANT
- For `MDNS.begin()`, `MDNS.addService()`, and `ArduinoOTA.setHostname()`, ensure user-provided hostnames are RFC-compliant (letters/digits/hyphen, no leading/trailing hyphen) and clamped to 63 characters.
### FW13: Outbound URL validation (no HTTPS requirement)
- **Severity**: SUGGESTION
- When using user-provided URL strings with `HTTPClient.begin()`/equivalent, validate scheme/format and constrain host targets (allowlist or equivalent policy).
- Do not require HTTPS/TLS as a baseline review rule.
### FW14: Optional unicast UDP source filtering
- **Severity**: SUGGESTION
- For unicast UDP receive paths, prefer optional user-configurable source filtering.
- Do not require this for multicast/broadcast protocol flows.
## Web UI Security (`wled00/data/*`, OWASP A01/A02/A05)
### WEB1: DOM XSS through `innerHTML`
- **Severity**: CRITICAL
- Prefer `textContent`; if HTML is required, sanitize trusted content path explicitly.
### WEB2: Dynamic code execution
- **Severity**: CRITICAL
- Reject `eval`, `new Function`, and string-based timer execution.
### WEB3: `postMessage` without origin validation
- **Severity**: IMPORTANT
- Require strict origin allowlist checks before processing payloads.
### WEB4: Unsafe redirects/navigation
- **Severity**: IMPORTANT
- Do not navigate directly from untrusted query/input without relative-path or allowlist checks.
### WEB5: Client-only validation
- **Severity**: IMPORTANT
- UI validation is not sufficient; equivalent firmware-side validation is required.
### WEB6: Direct DOM insertion from fetched/config data
- **Severity**: IMPORTANT
- Treat fetched and config-derived strings as untrusted unless proven otherwise.
### WEB7: CSRF checks for state-changing HTTP routes (advisory)
- **Severity**: SUGGESTION
- For state-changing HTTP routes (for example `/json/state`, `/win`), prefer `Origin`/`Referer` header validation as low-cost defense-in-depth for deployments that are not directly internet-exposed.
- Treat this as advisory only, since some legitimate clients may omit these headers.
## Secrets and Logging (OWASP A04/A09/A10)
### SEC1: Hardcoded secrets and credentials
- **Severity**: CRITICAL
- Reject committed API keys, passwords, tokens, private keys, or test backdoors with potential security impact.
### SEC2: Sensitive values in logs
- **Severity**: CRITICAL
- Do not log passwords, tokens, Wi-Fi keys, auth headers, or full sensitive payloads.
### SEC3: Insecure defaults
- **Severity**: IMPORTANT
- Reject new default credentials or insecure auto-enable behavior for privileged functions.
- For setup/onboarding flows, require first-change behavior for default credentials where applicable.
### SEC4: Overly detailed error responses
- **Severity**: IMPORTANT
- Avoid exposing stack traces or internal details to API/UI consumers.
### SEC5: Credential exposure in API/config responses
- **Severity**: IMPORTANT
- Flag API/config serialization that exposes password-like fields (for example Wi-Fi/AP/MQTT passwords) to unauthenticated or untrusted clients.
### SEC6: Security-relevant event logging coverage
- **Severity**: SUGGESTION
- Prefer explicit logging for auth failures, OTA attempts, config resets, and AP activation events, without logging secret values.
## Supply Chain and CI/CD (OWASP A03/A08)
### SC1: New dependency risk
- **Severity**: IMPORTANT
- Review new npm/pip/PlatformIO dependencies for legitimacy, pinning, and known vulnerabilities.
### SC2: Workflow permission hardening regressions
- **Severity**: IMPORTANT
- Check for broad `permissions`, unpinned third-party actions, or unsafe secret exposure.
- Flag mutable third-party action refs (`@main`, `@master`, broad tags) where SHA pinning is expected by project policy.
- Flag overly broad permissions such as `write-all` without clear need.
### SC3: Script injection in workflows
- **Severity**: IMPORTANT
- Avoid direct interpolation of untrusted `${{ github.event.* }}` values in `run` commands.
## Reviewer Checklist
- [ ] No new memory-safety hazards (bounds, overflow, unsafe copies/format strings)
- [ ] External input is validated and range-clamped at ingress points (HTTP/JSON, WebSocket, UDP, TCP, serial, ESP-NOW)
- [ ] State-changing API paths enforce auth policy
- [ ] OTA paths enforce integrity verification (without requiring TLS baseline)
- [ ] Suggested rule patterns are checked where relevant (UDP bounds, hostname sanitization, workflow pinning/permissions)
- [ ] Web UI changes avoid unsafe DOM execution/injection patterns
- [ ] No secrets added; no sensitive logging introduced
- [ ] Error handling remains fail-safe and non-leaky
- [ ] Dependency/workflow changes are supply-chain safe
- [ ] Feature-flag names are valid and not typoed
## AI Review Behavior
- Prefer concrete, file/line-specific findings over generic guidance.
- Prioritize **CRITICAL** and **IMPORTANT** findings.
- Skip irrelevant framework checks not used by WLED.
- If control-flow trust is unclear, ask for clarification instead of guessing.
+26
View File
@@ -298,8 +298,18 @@ AR_lib_deps = ;; for pre-usermod-library platformio_override compatibility
;; please note that you can NOT update existing ESP32 installs with a "V4" build. Also updating by OTA will not work properly.
;; You need to completely erase your device (esptool erase_flash) first, then install the "V4" build from VSCode+platformio.
;; tasmota platform (default)
platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.06.00/platform-espressif32.zip ;; Tasmota Arduino Core 2.0.18 with IPv6 support, based on IDF 4.4.8
platform_packages =
;; espressif platform (optional - needs 300KB extra flash)
;;; arduino-esp32 2.0.17 + esp-idf 4.4.7
;; platform = espressif32@ ~6.13.0
;; platform_packages =
;;; arduino-esp32 2.0.14 + esp-idf 4.4.6
;; platform = espressif32@ ~6.6.0
;; platform_packages = platformio/framework-arduinoespressif32 @ 3.20014.231204
build_unflags = ${common.build_unflags}
build_flags = -g
-Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one
@@ -720,6 +730,7 @@ board_build.partitions = ${esp32.extreme_partitions} ; We're gonna need a bigge
;; Core HUB75 flags - common to every HUB75 build
build_flags =
-D WLED_ENABLE_HUB75MATRIX -D NO_GFX
-D NO_CIE1931 ;; disable driver-internal gamma correction
-D WLED_DEBUG_BUS
-D LED_TYPES=TYPE_HUB75MATRIX_HS
; -D WLED_DEBUG
@@ -779,6 +790,21 @@ lib_deps = ${esp32s3.lib_deps}
${hub75.lib_deps}
;; board_build.partitions = tools/partitions-8MB_spiffs-tinyuf2.csv ;; supports adafruit UF2 bootloader
[env:waveshare_esp32s3_32MB_hub75]
;; Waveshare ESP32-S3-RGB-Matrix (memory_type: opi_opi); see https://docs.waveshare.com/ESP32-S3-RGB-Matrix
extends = env:esp32S3_wroom2_32MB
monitor_filters = esp32_exception_decoder
build_unflags = ${env:esp32S3_wroom2_32MB.build_unflags}
-D WLED_RELEASE_NAME=\"ESP32-S3_WROOM-2_32MB\" ;; need to un-set the relese name before setting a new one
build_flags = ${common.build_flags} ${esp32s3.build_flags} ${hub75.build_flags} ${hub75.s3_build_flags} ${hub75.i2s_disable_flags}
-D WLED_RELEASE_NAME=\"ESP32-S3_Waveshare_HUB75\"
-D WAVESHARE_S3_PINOUT
; -D WLED_USE_SD_SPI
; -D SD_PRINT_HOME_DIR
; -D WLED_DEBUG
lib_deps = ${esp32s3.lib_deps}
${hub75.lib_deps}
[env:esp32s3dev_16MB_opi_hub75]
;; MOONHUB HUB75 adapter board (lilygo T7-S3 with 16MB flash and octal PSRAM)
extends = env:esp32s3dev_8MB_opi
+21 -45
View File
@@ -33,7 +33,12 @@ build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags}
;
; *** To use the below defines/overrides, copy and paste each onto its own line just below build_flags in the section above.
;
; *** Note: on adding custom usermods
; custom_usermods entries are a separate key in the env section (not inside build_flags). Place them on their own line in the env.
; Multiple usermods are separated by a space, the names can be found in the library.json file of the usermod.
; Example: use the default usermods from the esp32dev env and add the Temperature and four_line_display_ALT usermods
; custom_usermods = ${env:esp32dev.custom_usermods} Temperature four_line_display_ALT
;
; Set a release name that may be used to distinguish required binary for flashing
; -D WLED_RELEASE_NAME=\"ESP32_MULTI_USREMODS\"
;
@@ -98,17 +103,17 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
; -D WLED_DEBUG_PORT=7868
;
; Use Autosave usermod and set it to do save after 90s
; -D USERMOD_AUTO_SAVE
; custom_usermods = ${env:esp32dev.custom_usermods} auto_save
; -D AUTOSAVE_AFTER_SEC=90
;
; Use AHT10/AHT15/AHT20 usermod
; -D USERMOD_AHT10
; custom_usermods = ${env:esp32dev.custom_usermods} AHT10_v2
;
; Use INA226 usermod
; -D USERMOD_INA226
; custom_usermods = ${env:esp32dev.custom_usermods} INA226_v2
;
; Use 4 Line Display usermod with SPI display
; -D USERMOD_FOUR_LINE_DISPLAY
; custom_usermods = ${env:esp32dev.custom_usermods} four_line_display_ALT
; -DFLD_SPI_DEFAULT
; -D FLD_TYPE=SSD1306_SPI64
; -D FLD_PIN_CLOCKSPI=14
@@ -118,22 +123,22 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
; -D FLD_PIN_RESET=27
;
; Use Rotary encoder usermod (in conjunction with 4LD)
; -D USERMOD_ROTARY_ENCODER_UI
; custom_usermods = ${env:esp32dev.custom_usermods} rotary_encoder_ui_ALT
; -D ENCODER_DT_PIN=5
; -D ENCODER_CLK_PIN=18
; -D ENCODER_SW_PIN=19
;
; Use Dallas DS18B20 temperature sensor usermod and configure it to use GPIO13
; -D USERMOD_DALLASTEMPERATURE
; custom_usermods = ${env:esp32dev.custom_usermods} Temperature
; -D TEMPERATURE_PIN=13
;
; Use Multi Relay usermod and configure it to use 6 relays and appropriate GPIO
; -D USERMOD_MULTI_RELAY
; custom_usermods = ${env:esp32dev.custom_usermods} multi_relay
; -D MULTI_RELAY_MAX_RELAYS=6
; -D MULTI_RELAY_PINS=12,23,22,21,24,25
;
; Use PIR sensor usermod and configure it to use GPIO4 and timer of 60s
; -D USERMOD_PIRSWITCH
; custom_usermods = ${env:esp32dev.custom_usermods} PIR_sensor_switch
; -D PIR_SENSOR_PIN=4 # use -1 to disable usermod
; -D PIR_SENSOR_OFF_SEC=60
; -D PIR_SENSOR_MAX_SENSORS=2 # max allowable sensors (uses OR logic for triggering)
@@ -146,12 +151,12 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
; -D I2S_CKPIN=19
;
; Use PWM fan usermod
; -D USERMOD_PWM_FAN
; custom_usermods = ${env:esp32dev.custom_usermods} PWM_fan
; -D TACHO_PIN=33
; -D PWM_PIN=32
;
; Use POV Display usermod
; -D USERMOD_POV_DISPLAY
; custom_usermods = ${env:esp32dev.custom_usermods} pov_display
; Use built-in or custom LED as a status indicator (assumes LED is connected to GPIO16)
; -D STATUSLED=16
;
@@ -375,6 +380,7 @@ board_upload.maximum_size = 2097152
extends = esp32 ;; use default esp32 platform
board = esp32dev
upload_speed = 460800
custom_usermods = ${env:esp32dev.custom_usermods} Temperature four_line_display_ALT
build_flags = ${common.build_flags} ${esp32.build_flags}
-D WLED_RELEASE_NAME=\"ESP32_wemos_shield\"
-D DATA_PINS=16
@@ -382,12 +388,8 @@ build_flags = ${common.build_flags} ${esp32.build_flags}
-D BTNPIN=17
-D IRPIN=18
-UWLED_USE_MY_CONFIG
-D USERMOD_DALLASTEMPERATURE
-D USERMOD_FOUR_LINE_DISPLAY
-D TEMPERATURE_PIN=23
lib_deps = ${esp32.lib_deps}
OneWire@~2.3.5 ;; needed for USERMOD_DALLASTEMPERATURE
olikraus/U8g2 @ ^2.28.8 ;; needed for USERMOD_FOUR_LINE_DISPLAY
board_build.partitions = ${esp32.default_partitions}
[env:esp32_pico-D4]
@@ -478,7 +480,8 @@ platform = ${esp8266.platform_wled_default}
platform_packages = ${esp8266.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_DISABLE_OTA -D USERMOD_MY9291
custom_usermods = ${env:esp01_1m_full.custom_usermods} MY9291
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_DISABLE_OTA
lib_deps = ${esp8266.lib_deps}
# ------------------------------------------------------------------------------
@@ -506,42 +509,15 @@ lib_deps = ${esp8266.lib_deps}
# ------------------------------------------------------------------------------
# EleksTube-IPS
# See usermods/EleksTube_IPS/platformio_override.ini.sample
# ------------------------------------------------------------------------------
[env:elekstube_ips]
extends = esp32 ;; use default esp32 platform
board = esp32dev
upload_speed = 921600
custom_usermods = ${env:esp32dev.custom_usermods} RTC EleksTube_IPS
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED
-D DATA_PINS=12
-D RLYPIN=27
-D BTNPIN=34
-D PIXEL_COUNTS=6
# Display config
-D ST7789_DRIVER
-D TFT_WIDTH=135
-D TFT_HEIGHT=240
-D CGRAM_OFFSET
-D TFT_SDA_READ
-D TFT_MOSI=23
-D TFT_SCLK=18
-D TFT_DC=25
-D TFT_RST=26
-D SPI_FREQUENCY=40000000
-D USER_SETUP_LOADED
monitor_filters = esp32_exception_decoder
# ------------------------------------------------------------------------------
# Usermod examples
# ------------------------------------------------------------------------------
# 433MHz RF remote example for esp32dev
[env:esp32dev_usermod_RF433]
extends = env:esp32dev
custom_usermods =
${env:esp32dev.custom_usermods}
RF433
# 433MHz RF remote example: see usermods/usermod_v2_RF433/platformio_override.ini.sample
# External usermod from a git repository.
# The library's `library.json` must include `"build": {"libArchive": false}`.
+63 -47
View File
@@ -7,81 +7,97 @@
<a href="https://kno.wled.ge"><img src="https://img.shields.io/badge/quick_start-wiki-blue.svg?style=flat-square"></a>
<a href="https://github.com/Aircoookie/WLED-App"><img src="https://img.shields.io/badge/app-wled-blue.svg?style=flat-square"></a>
<a href="https://gitpod.io/#https://github.com/wled-dev/WLED"><img src="https://img.shields.io/badge/Gitpod-ready--to--code-blue?style=flat-square&logo=gitpod"></a>
</p>
</p>
# Welcome to WLED! ✨
A fast and feature-rich implementation of an ESP32 and ESP8266 webserver to control NeoPixel (WS2812B, WS2811, SK6812) LEDs or also SPI based chipsets like the WS2801 and APA102!
A fast and feature-rich firmware for ESP32 microcontrollers to control addressable LEDs — from simple strips to large 2D matrices and HUB75 panels.
Originally created by [Aircoookie](https://github.com/Aircoookie)
Originally created by [Aircoookie](https://github.com/Aircoookie), now maintained by a community of contributors.
## ⚙️ Features
- WS2812FX library with more than 100 special effects
- FastLED noise effects and 50 palettes
- Modern UI with color, effect and segment controls
- Segments to set different effects and colors to user defined parts of the LED string
- Settings page - configuration via the network
- Access Point and station mode - automatic failsafe AP
- [Up to 10 LED outputs](https://kno.wled.ge/features/multi-strip/#esp32) per instance
- Support for RGBW strips
- Up to 250 user presets to save and load colors/effects easily, supports cycling through them.
- Presets can be used to automatically execute API calls
- Nightlight function (gradually dims down)
- Full OTA software updateability (HTTP + ArduinoOTA), password protectable
- Configurable analog clock (Cronixie, 7-segment and EleksTube IPS clock support via usermods)
- Configurable Auto Brightness limit for safe operation
- Filesystem-based config for easier backup of presets and settings
## 💡 Supported light control interfaces
- WLED app for [Android](https://play.google.com/store/apps/details?id=ca.cgagnier.wlednativeandroid) and [iOS](https://apps.apple.com/gb/app/wled-native/id6446207239)
- JSON and HTTP request APIs
- MQTT
- E1.31, Art-Net, DDP and TPM2.net
- [diyHue](https://github.com/diyhue/diyHue) (Wled is supported by diyHue, including Hue Sync Entertainment under udp. Thanks to [Gregory Mallios](https://github.com/gmallios))
- [Hyperion](https://github.com/hyperion-project/hyperion.ng)
- UDP realtime
- Alexa voice control (including dimming and color)
- Sync to Philips hue lights
- Adalight (PC ambilight via serial) and TPM2
- Sync color of multiple WLED devices (UDP notifier)
- Infrared remotes (24-key RGB, receiver required)
- Simple timers/schedules (time from NTP, timezones/DST supported)
### Effects & Visuals
- [**200+ built-in effects**](https://kno.wled.ge/features/effects/) including classic animations, audio-reactive, and 2D/matrix effects
- [50+ color palettes](https://kno.wled.ge/features/palettes/) plus a built-in **custom palette editor** (PixelForge)
- [**2D LED matrix support**](https://kno.wled.ge/advanced/mapping/) with dedicated 2D effects and flexible panel mapping
- [**HUB75 RGB matrix panel support**](https://kno.wled.ge/advanced/HUB75/) (ESP32)
- [**AudioReactive**](https://kno.wled.ge/advanced/audio-reactive/) effects — included by default, responding to sound via microphone, line-in, or network audio source
- Effect blending for smooth transitions between animations
- Antialiased drawing functions for smooth graphics
### Segments & Control
- [**Segments**](https://kno.wled.ge/features/segments/) — apply different effects, colors and palettes to independent parts of your LED setup simultaneously
- Up to **250 presets** to save and recall colors, effects and segment configurations — supports [playlists](https://kno.wled.ge/features/presets/) for automated cycling
- Nightlight function with configurable dimming curve
- Configurable **Auto Brightness Limiter** (per output) for safe operation
### Hardware Support
- **ESP32** (all variants: original, S2, S3, C3)
- [**Up to 17 LED outputs**](https://kno.wled.ge/features/multi-strip/) on ESP32 using parallel I2S + RMT
- [Addressable LED support](https://kno.wled.ge/basics/compatible-led-strips/): WS2812B, WS2811, WS2815, SK6812, WS2805, TM1914, APA102, WS2801, LPD8806, and many more
- RGBW, [RGB+CCT](https://kno.wled.ge/features/cct/) and white-only strips
- PWM outputs for analog LEDs and dimmers
- [**Ethernet** support](https://kno.wled.ge/features/ethernet-lan/) for a wide range of boards (QuinLED, LILYGO, Olimex, and more)
- Filesystem-based config for easy backup and restore of presets and settings
- Full OTA firmware updates (HTTP + ArduinoOTA), password-protectable
### Connectivity & Integrations
- **WLED app** for [Android](https://play.google.com/store/apps/details?id=ca.cgagnier.wlednativeandroid) and [iOS](https://apps.apple.com/gb/app/wled-native/id6446207239)
- [JSON](https://kno.wled.ge/interfaces/json-api/) and [HTTP request](https://kno.wled.ge/interfaces/http-api/) APIs
- **Multi-WiFi** — connect to up to 3 networks with automatic AP fallback
- **ESP-NOW** wireless sync between devices (no WiFi router required)
- [**MQTT**](https://kno.wled.ge/interfaces/mqtt/) with Home Assistant discovery
- [**E1.31, Art-Net**](https://kno.wled.ge/interfaces/e1.31-dmx/), [DDP](https://kno.wled.ge/interfaces/ddp/) and [TPM2.net](https://kno.wled.ge/interfaces/udp-realtime/) for DMX/professional lighting control
- [UDP realtime sync](https://kno.wled.ge/interfaces/udp-notifier/) across multiple WLED devices
- Alexa voice control (on/off, brightness, color)
- [Philips Hue sync](https://kno.wled.ge/interfaces/philips-hue/)
- [diyHue](https://github.com/diyhue/diyHue) and [Hyperion](https://github.com/hyperion-project/hyperion.ng) integration
- [Adalight / TPM2](https://kno.wled.ge/interfaces/serial/) (PC ambilight via serial)
- [Infrared remote control](https://kno.wled.ge/interfaces/infrared/) (24-key RGB, receiver required)
- Timers and schedules (NTP time sync, full timezone and DST support)
### Developer-Friendly
- **Usermod system** — extend WLED with community or custom modules without modifying core code
- Large and active [usermod library](https://kno.wled.ge/advanced/community-usermods/) including AudioReactive, temperature sensors, rotary encoders, displays, and much more
- Well-documented [JSON API](https://kno.wled.ge/interfaces/json-api/)
- Licensed under the **EUPL v1.2**
## 📲 Quick start guide and documentation
See the [documentation on our official site](https://kno.wled.ge)!
See the [documentation at kno.wled.ge](https://kno.wled.ge)!
[On this page](https://kno.wled.ge/basics/tutorials/) you can find excellent tutorials and tools to help you get your new project up and running!
[Tutorials and getting-started guides](https://kno.wled.ge/basics/tutorials/) to help you get your project running quickly.
## 🖼️ User interface
<img src="/images/macbook-pro-space-gray-on-the-wooden-table.jpg" width="50%"><img src="/images/walking-with-iphone-x.jpg" width="50%">
## 💾 Compatible hardware
See [here](https://kno.wled.ge/basics/compatible-hardware)!
See the [compatible hardware list](https://kno.wled.ge/basics/compatible-hardware) on the wiki.
## ✌️ Other
Licensed under the EUPL v1.2 license
Credits [here](https://kno.wled.ge/about/contributors/)!
CORS proxy by [Corsfix](https://corsfix.com/)
Licensed under the [EUPL v1.2](https://raw.githubusercontent.com/wled-dev/WLED/main/LICENSE).
Credits to all [contributors](https://kno.wled.ge/about/contributors/)!
CORS proxy by [Corsfix](https://corsfix.com/).
Join the Discord server to discuss everything about WLED!
<a href="https://discord.gg/QAh7wJHrRM"><img src="https://discordapp.com/api/guilds/473448917040758787/widget.png?style=banner2" width="25%"></a>
Check out the WLED [Discourse forum](https://wled.discourse.group)!
Check out the WLED [Discourse forum](https://wled.discourse.group)!
You can also send me mails to [dev.aircoookie@gmail.com](mailto:dev.aircoookie@gmail.com), but please, only do so if you want to talk to me privately.
If you'd like to reach the original creator privately: [dev.aircoookie@gmail.com](mailto:dev.aircoookie@gmail.com).
If WLED really brightens up your day, you can [![](https://img.shields.io/badge/send%20me%20a%20small%20gift-paypal-blue.svg?style=flat-square)](https://paypal.me/aircoookie)
If WLED brightens up your day, you can [send a gift to Aircoookie via PayPal](https://paypal.me/aircoookie).
---
*Disclaimer:*
*Disclaimer:*
If you are prone to photosensitive epilepsy, we recommended you do **not** use this software.
If you still want to try, don't use strobe, lighting or noise modes or high effect speed settings.
As per the EUPL license, I assume no liability for any damage to you or any other person or equipment.
If you are prone to photosensitive epilepsy, we recommend you do **not** use this software.
If you still want to try, avoid strobe, lightning or noise modes and high effect speed settings.
As per the EUPL license, no liability is assumed for any damage to you or any other person or equipment.
+12 -11
View File
@@ -28,18 +28,19 @@ class ADS1115Usermod : public Usermod {
}
void loop() {
if (isEnabled && millis() - lastTime > loopInterval) {
lastTime = millis();
if (!isEnabled || strip.isUpdating() || millis() - lastTime <= loopInterval)
return;
// If we don't have new data, skip this iteration.
if (!ads.conversionComplete()) {
return;
}
lastTime = millis();
updateResult();
moveToNextChannel();
startReading();
// If we don't have new data, skip this iteration.
if (!ads.conversionComplete()) {
return;
}
updateResult();
moveToNextChannel();
startReading();
}
void addToJsonInfo(JsonObject& root)
@@ -69,6 +70,8 @@ class ADS1115Usermod : public Usermod {
{
JsonObject top = root.createNestedObject(F("ADC ADS1115"));
top[F("Loop Interval")] = loopInterval;
for (uint8_t i = 0; i < channelsCount; i++) {
ChannelSettings* settingsPtr = &(channelSettings[i]);
JsonObject channel = top.createNestedObject(settingsPtr->settingName);
@@ -79,8 +82,6 @@ class ADS1115Usermod : public Usermod {
channel[F("Offset")] = settingsPtr->offset;
channel[F("Decimals")] = settingsPtr->decimals;
}
top[F("Loop Interval")] = loopInterval;
}
bool readFromConfig(JsonObject& root)
+2 -2
View File
@@ -2,7 +2,7 @@
"name": "ADS1115_v2",
"build": { "libArchive": false },
"dependencies": {
"Adafruit BusIO": "https://github.com/adafruit/Adafruit_BusIO#1.13.2",
"Adafruit ADS1X15": "https://github.com/adafruit/Adafruit_ADS1X15#2.4.0"
"Adafruit BusIO": "https://github.com/adafruit/Adafruit_BusIO#1.17.4",
"Adafruit ADS1X15": "https://github.com/adafruit/Adafruit_ADS1X15#2.6.2"
}
}
+24 -5
View File
@@ -1,10 +1,29 @@
# ADS1115 16-Bit ADC with four inputs
# ADS1115 Usermod
This usermod will read from an ADS1115 ADC. The voltages are displayed in the Info section of the web UI.
Reads values from an ADS1115 16-bit ADC and exposes them in the `Info` tab.
Configuration is performed via the Usermod menu. There are no parameters to set in code!
## Features
- Reads values from an ADS1115 over I2C.
- Supports 8 ADS1115 input modes:
- 4 single-ended inputs (`AIN0` to `AIN3`)
- 4 differential pairs (`AIN0-AIN1`, `AIN0-AIN3`, `AIN1-AIN3`, `AIN2-AIN3`)
- Per-channel configuration in the Usermod settings:
- Enable/disable
- Display name
- Units
- Multiplier and offset
- Decimal precision
- Configurable measurement loop interval.
- Publishes configured channel values to the `Info` tab.
## Installation
## Compatibility
- Requires an ADS1115 module connected via I2C.
- Works on targets with I2C support.
- Default ADC gain is `1x` (input range `+/-4.096V`).
Add 'ADS1115' to `custom_usermods` in your platformio environment.
## Installation
- Add `ADS1115` to `custom_usermods` in your `platformio.ini` (or `platformio_override.ini`).
## Author
- Dima Zhemkov [@dima-zhemkov](https://github.com/dima-zhemkov)
@@ -1,5 +0,0 @@
[env:aht10_example]
extends = env:esp32dev
build_flags =
${common.build_flags} ${esp32.build_flags}
; -D USERMOD_AHT10_DEBUG ; -- add a debug status to the info modal
@@ -25,6 +25,8 @@ class Animated_Staircase : public Usermod {
unsigned int topMaxDist = 50; // default maximum measured distance in cm, top
unsigned int bottomMaxDist = 50; // default maximum measured distance in cm, bottom
bool togglePower = false; // toggle power on/off with staircase on/off
bool topAPinInvert = false; // invert output of top sensor
bool bottomAPinInvert = false; // invert output of bottom sensor
/* runtime variables */
bool initDone = false;
@@ -91,6 +93,8 @@ class Animated_Staircase : public Usermod {
static const char _topEchoCm[];
static const char _bottomEchoCm[];
static const char _togglePower[];
static const char _topPIRorTrigger_pin_invert[];
static const char _bottomPIRorTrigger_pin_invert[];
void publishMqtt(bool bottom, const char* state) {
#ifndef WLED_DISABLE_MQTT
@@ -156,6 +160,12 @@ class Animated_Staircase : public Usermod {
return pulseIn(echoPin, HIGH, maxTimeUs) > 0;
}
bool readPIRPin(int8_t pin, bool invert) {
if (pin < 0) return false;
bool v = digitalRead(pin);
return invert ? !v : v;
}
bool checkSensors() {
bool sensorChanged = false;
@@ -164,15 +174,15 @@ class Animated_Staircase : public Usermod {
bottomSensorRead = bottomSensorWrite ||
(!useUSSensorBottom ?
(bottomPIRorTriggerPin<0 ? false : digitalRead(bottomPIRorTriggerPin)) :
(bottomPIRorTriggerPin<0 ? false : readPIRPin(bottomPIRorTriggerPin, bottomAPinInvert)) :
ultrasoundRead(bottomPIRorTriggerPin, bottomEchoPin, bottomMaxDist*59) // cm to us
);
topSensorRead = topSensorWrite ||
(!useUSSensorTop ?
(topPIRorTriggerPin<0 ? false : digitalRead(topPIRorTriggerPin)) :
(topPIRorTriggerPin<0 ? false : readPIRPin(topPIRorTriggerPin, topAPinInvert)) :
ultrasoundRead(topPIRorTriggerPin, topEchoPin, topMaxDist*59) // cm to us
);
if (bottomSensorRead != bottomSensorState) {
bottomSensorState = bottomSensorRead; // change previous state
sensorChanged = true;
@@ -439,18 +449,20 @@ class Animated_Staircase : public Usermod {
if (staircase.isNull()) {
staircase = root.createNestedObject(FPSTR(_name));
}
staircase[FPSTR(_enabled)] = enabled;
staircase[FPSTR(_segmentDelay)] = segment_delay_ms;
staircase[FPSTR(_onTime)] = on_time_ms / 1000;
staircase[FPSTR(_useTopUltrasoundSensor)] = useUSSensorTop;
staircase[FPSTR(_topPIRorTrigger_pin)] = topPIRorTriggerPin;
staircase[FPSTR(_topEcho_pin)] = useUSSensorTop ? topEchoPin : -1;
staircase[FPSTR(_useBottomUltrasoundSensor)] = useUSSensorBottom;
staircase[FPSTR(_bottomPIRorTrigger_pin)] = bottomPIRorTriggerPin;
staircase[FPSTR(_bottomEcho_pin)] = useUSSensorBottom ? bottomEchoPin : -1;
staircase[FPSTR(_topEchoCm)] = topMaxDist;
staircase[FPSTR(_bottomEchoCm)] = bottomMaxDist;
staircase[FPSTR(_togglePower)] = togglePower;
staircase[FPSTR(_enabled)] = enabled;
staircase[FPSTR(_segmentDelay)] = segment_delay_ms;
staircase[FPSTR(_onTime)] = on_time_ms / 1000;
staircase[FPSTR(_useTopUltrasoundSensor)] = useUSSensorTop;
staircase[FPSTR(_topPIRorTrigger_pin)] = topPIRorTriggerPin;
staircase[FPSTR(_topEcho_pin)] = useUSSensorTop ? topEchoPin : -1;
staircase[FPSTR(_useBottomUltrasoundSensor)] = useUSSensorBottom;
staircase[FPSTR(_bottomPIRorTrigger_pin)] = bottomPIRorTriggerPin;
staircase[FPSTR(_bottomEcho_pin)] = useUSSensorBottom ? bottomEchoPin : -1;
staircase[FPSTR(_topEchoCm)] = topMaxDist;
staircase[FPSTR(_bottomEchoCm)] = bottomMaxDist;
staircase[FPSTR(_togglePower)] = togglePower;
staircase[FPSTR(_topPIRorTrigger_pin_invert)] = topAPinInvert;
staircase[FPSTR(_bottomPIRorTrigger_pin_invert)] = bottomAPinInvert;
DEBUG_PRINTLN(F("Staircase config saved."));
}
@@ -462,11 +474,13 @@ class Animated_Staircase : public Usermod {
bool readFromConfig(JsonObject& root) {
bool oldUseUSSensorTop = useUSSensorTop;
bool oldUseUSSensorBottom = useUSSensorBottom;
bool oldTopAPinInvert = topAPinInvert;
bool oldBottomAPinInvert = bottomAPinInvert;
int8_t oldTopAPin = topPIRorTriggerPin;
int8_t oldTopBPin = topEchoPin;
int8_t oldBottomAPin = bottomPIRorTriggerPin;
int8_t oldBottomBPin = bottomEchoPin;
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
DEBUG_PRINT(FPSTR(_name));
@@ -485,10 +499,12 @@ class Animated_Staircase : public Usermod {
useUSSensorTop = top[FPSTR(_useTopUltrasoundSensor)] | useUSSensorTop;
topPIRorTriggerPin = top[FPSTR(_topPIRorTrigger_pin)] | topPIRorTriggerPin;
topEchoPin = top[FPSTR(_topEcho_pin)] | topEchoPin;
topAPinInvert = top[FPSTR(_topPIRorTrigger_pin_invert)] | topAPinInvert;
useUSSensorBottom = top[FPSTR(_useBottomUltrasoundSensor)] | useUSSensorBottom;
bottomPIRorTriggerPin = top[FPSTR(_bottomPIRorTrigger_pin)] | bottomPIRorTriggerPin;
bottomEchoPin = top[FPSTR(_bottomEcho_pin)] | bottomEchoPin;
bottomAPinInvert = top[FPSTR(_bottomPIRorTrigger_pin_invert)] | bottomAPinInvert;
topMaxDist = top[FPSTR(_topEchoCm)] | topMaxDist;
topMaxDist = min(150,max(30,(int)topMaxDist)); // max distance ~1.5m (a lag of 9ms may be expected)
@@ -554,14 +570,15 @@ const char Animated_Staircase::_segmentDelay[] PROGMEM = "segment-d
const char Animated_Staircase::_onTime[] PROGMEM = "on-time-s";
const char Animated_Staircase::_useTopUltrasoundSensor[] PROGMEM = "useTopUltrasoundSensor";
const char Animated_Staircase::_topPIRorTrigger_pin[] PROGMEM = "topPIRorTrigger_pin";
const char Animated_Staircase::_topPIRorTrigger_pin_invert[] PROGMEM = "topPIRorTrigger_pin_invert";
const char Animated_Staircase::_topEcho_pin[] PROGMEM = "topEcho_pin";
const char Animated_Staircase::_useBottomUltrasoundSensor[] PROGMEM = "useBottomUltrasoundSensor";
const char Animated_Staircase::_bottomPIRorTrigger_pin[] PROGMEM = "bottomPIRorTrigger_pin";
const char Animated_Staircase::_bottomPIRorTrigger_pin_invert[] PROGMEM = "bottomPIRorTrigger_pin_invert";
const char Animated_Staircase::_bottomEcho_pin[] PROGMEM = "bottomEcho_pin";
const char Animated_Staircase::_topEchoCm[] PROGMEM = "top-dist-cm";
const char Animated_Staircase::_bottomEchoCm[] PROGMEM = "bottom-dist-cm";
const char Animated_Staircase::_togglePower[] PROGMEM = "toggle-on-off";
static Animated_Staircase animated_staircase;
REGISTER_USERMOD(animated_staircase);
@@ -8,13 +8,13 @@
; USERMOD_DHT_MQTT - publish measurements to the MQTT broker
; USERMOD_DHT_STATS - For debug, report delay stats
[env:d1_mini_usermod_dht_C]
extends = env:d1_mini
custom_usermods = ${env:d1_mini.custom_usermods} DHT
build_flags = ${env:d1_mini.build_flags} -D USERMOD_DHT_CELSIUS
[env:esp8266_2m_usermod_dht_C]
extends = env:esp8266_2m
custom_usermods = ${env:esp8266_2m.custom_usermods} DHT
build_flags = ${env:esp8266_2m.build_flags} -D USERMOD_DHT_CELSIUS
[env:custom32_LEDPIN_16_usermod_dht_C]
extends = env:custom32_LEDPIN_16
custom_usermods = ${env:custom32_LEDPIN_16.custom_usermods} DHT
build_flags = ${env:custom32_LEDPIN_16.build_flags} -D USERMOD_DHT_CELSIUS -D USERMOD_DHT_STATS
[env:esp32dev_LEDPIN_16_usermod_dht_C]
extends = env:esp32dev
custom_usermods = ${env:esp32dev.custom_usermods} DHT
build_flags = ${env:esp32dev.build_flags} -D LEDPIN=16 -D USERMOD_DHT_CELSIUS -D USERMOD_DHT_STATS
+2 -2
View File
@@ -114,7 +114,7 @@ class MyExampleUsermod : public Usermod {
void loop() override {
// if usermod is disabled or called during strip updating just exit
// NOTE: on very long strips strip.isUpdating() may always return true so update accordingly
if (!enabled || strip.isUpdating()) return;
if (!enabled || (strip.isUpdating() && (millis() - lastTime < 200))) return; // adjust "200" (in millisecond) to your needs - prevents starvation with very long strips
// do your magic here
if (millis() - lastTime > 1000) {
@@ -176,7 +176,7 @@ class MyExampleUsermod : public Usermod {
JsonObject usermod = root[FPSTR(_name)];
if (!usermod.isNull()) {
// expect JSON usermod data in usermod name object: {"ExampleUsermod:{"user0":10}"}
userVar0 = usermod["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value
userVar0 = usermod["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value (userVar0 is defined in wled.h)
}
// you can as well check WLED state JSON keys
//if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
+1 -1
View File
@@ -1,5 +1,5 @@
{
"name:": "EleksTube_IPS",
"name": "EleksTube_IPS",
"build": { "libArchive": false },
"dependencies": {
"TFT_eSPI" : "2.5.33"
@@ -1,6 +0,0 @@
[env:ina226_example]
extends = env:esp32dev
custom_usermods = ${env:esp32dev.custom_usermods} INA226_v2
build_flags =
${env:esp32dev.build_flags}
; -D USERMOD_INA226_DEBUG ; -- add a debug status to the info modal
@@ -1,6 +1,5 @@
; Options
; -------
; USERMOD_SN_PHOTORESISTOR - define this to have this user mod included wled00\usermods_list.cpp
; USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL - the number of milliseconds between measurements, defaults to 60 seconds
; USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT - the number of milliseconds after boot to take first measurement, defaults to 20 seconds
; USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE - the voltage supplied to the sensor, defaults to 5v
@@ -8,9 +7,10 @@
; USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE - the resistor size, defaults to 10000.0 (10K hms)
; USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE - the offset value to report on, defaults to 25
;
[env:usermod_sn_photoresistor_d1_mini]
extends = env:d1_mini
[env:usermod_sn_photoresistor_esp8266_2m]
extends = env:esp8266_2m
custom_usermods = ${env:esp8266_2m.custom_usermods} SN_Photoresistor
build_flags =
${common.build_flags_esp8266}
-D USERMOD_SN_PHOTORESISTOR
lib_deps = ${env.lib_deps}
${env:esp8266_2m.build_flags}
-D USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL=60
lib_deps = ${env:esp8266_2m.lib_deps}
@@ -1,4 +1,4 @@
{
"name:": "ST7789_display",
"name": "ST7789_display",
"build": { "libArchive": false }
}
@@ -1,8 +0,0 @@
[env:esp32dev]
build_flags = ${common.build_flags_esp32}
; PIN defines - uncomment and change, if needed:
; -D LEDPIN=2
-D BTNPIN=35
; -D IRPIN=4
; -D RLYPIN=12
; -D RLYMDE=1
@@ -1,5 +0,0 @@
; Options
; -------
; USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL - the number of milliseconds between measurements, defaults to 60 seconds
;
@@ -13,7 +13,7 @@ board_build.partitions = ${esp32.large_partitions}
board_build.f_flash = 80000000L
board_build.flash_mode = qio
monitor_filters = esp32_exception_decoder
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=T-QT-PRO-8MB
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"T-QT-PRO-8MB_dice\"
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
-D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
@@ -75,7 +75,7 @@ board_build.partitions = ${esp32.large_partitions}
board_build.f_flash = 80000000L
board_build.flash_mode = qio
monitor_filters = esp32_exception_decoder
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_8MB_qspi
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_8MB_qspi_dice\"
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
-D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
@@ -105,7 +105,7 @@ lib_deps = ${esp32s3.lib_deps}
# https://github.com/wled-dev/WLED/issues/1382
; [env:esp32dev_dice]
; extends = env:esp32dev
; build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=ESP32
; build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32_dice\"
; ; Enable Pixels dice mod
; -D USERMOD_PIXELS_DICE_TRAY
; lib_deps = ${esp32.lib_deps}
+1 -1
View File
@@ -1,5 +1,5 @@
{
"name:": "pov_display",
"name": "pov_display",
"build": { "libArchive": false},
"platforms": ["espressif32"]
}
+6
View File
@@ -143,7 +143,13 @@ void ShtUsermod::appendDeviceToMqttDiscoveryMessage(JsonDocument& root) {
device[F("ids")] = escapedMac.c_str();
device[F("name")] = serverDescription;
device[F("sw")] = versionString;
// AI: below section was generated by an AI
#ifdef ARDUINO_ARCH_ESP32
device[F("mdl")] = ESP.getChipModel();
#else
device[F("mdl")] = F("ESP8266");
#endif
// AI: end
device[F("mf")] = F("espressif");
}
@@ -2,10 +2,10 @@
default_envs = esp32dev_fld
[env:esp32dev_fld]
extends = env:esp32dev_V4
custom_usermods = ${env:esp32dev_V4.custom_usermods} four_line_display_ALT
extends = env:esp32dev
custom_usermods = ${env:esp32dev.custom_usermods} four_line_display_ALT
build_flags =
${env:esp32dev_V4.build_flags}
${env:esp32dev.build_flags}
-D FLD_TYPE=SH1106
-D I2CSCLPIN=27
-D I2CSDAPIN=26
@@ -2,10 +2,10 @@
default_envs = esp32dev_re
[env:esp32dev_re]
extends = env:esp32dev_V4
custom_usermods = ${env:esp32dev_V4.custom_usermods} rotary_encoder_ui_ALT
extends = env:esp32dev
custom_usermods = ${env:esp32dev.custom_usermods} rotary_encoder_ui_ALT
build_flags =
${env:esp32dev_V4.build_flags}
${env:esp32dev.build_flags}
-D USERMOD_ROTARY_ENCODER_GPIO=INPUT
-D ENCODER_DT_PIN=21
-D ENCODER_CLK_PIN=23
+1 -1
View File
@@ -1117,7 +1117,7 @@ void Segment::blur(uint8_t blur_amount, bool smear) const {
* Rotates the color in HSV space, where pos is H. (0=0deg, 256=360deg)
*/
uint32_t Segment::color_wheel(uint8_t pos) const {
if (palette) return color_from_palette(pos, false, false, 0); // only wrap if "always wrap" is set
if (palette) return color_from_palette(pos, false, true, 0); // color_wheel is a continuous (moving) wheel, so wrap end->start (restores pre-0.16 behaviour)
uint8_t w = W(getCurrentColor(0));
CRGBW rgb;
rgb = CHSV32(static_cast<uint16_t>(pos << 8), 255, 255);
+17 -6
View File
@@ -813,6 +813,7 @@ BusHub75Matrix::BusHub75Matrix(const BusConfig &bc) : Bus(bc.type, bc.start, bc.
// mxconfig.driver = HUB75_I2S_CFG::ICN2038S; // experimental - use specific shift register driver
// mxconfig.driver = HUB75_I2S_CFG::FM6124; // try this driver in case you panel stays dark, or when colors look too pastel
// Other possible shiftreg drivers: HUB75_I2S_CFG::FM6126A, HUB75_I2S_CFG::ICN2038S, HUB75_I2S_CFG::MBI5124, HUB75_I2S_CFG::DP3246
// mxconfig.latch_blanking = 3;
// mxconfig.i2sspeed = HUB75_I2S_CFG::HZ_10M; // experimental - 5MHZ should be enugh, but colours looks slightly better at 10MHz
@@ -824,8 +825,13 @@ BusHub75Matrix::BusHub75Matrix(const BusConfig &bc) : Bus(bc.type, bc.start, bc.
mxconfig.chain_length = max((uint8_t) 1, min(chainLength, (uint8_t) 4));
if (mxconfig.mx_height >= 64 && (mxconfig.chain_length > 1)) {
DEBUGBUS_PRINTLN(F("WARNING, only single panel can be used of 64 pixel boards due to memory"));
mxconfig.chain_length = 1;
#if defined(BOARD_HAS_PSRAM) // limitation to one panel only applies to boards without PSRAM
if (!psramFound() || ESP.getPsramSize() == 0) // PSRAM sanity check
#endif
{
DEBUGBUS_PRINTLN(F("WARNING, only single panel can be used of 64 pixel boards due to memory"));
mxconfig.chain_length = 1;
}
}
if (bc.type == TYPE_HUB75MATRIX_HS) {
@@ -835,6 +841,7 @@ BusHub75Matrix::BusHub75Matrix(const BusConfig &bc) : Bus(bc.type, bc.start, bc.
_isVirtual = true;
mxconfig.mx_width = min((uint8_t) 64, panelWidth) * 2;
mxconfig.mx_height = min((uint8_t) 64, panelHeight) / 2;
mxconfig.driver = HUB75_I2S_CFG::FM6124; // use FM6124 for "outdoor" 4-scan panels - workaround until we can make the driver user-configurable
} else {
DEBUGBUS_PRINTLN("Unknown type");
return;
@@ -874,6 +881,12 @@ BusHub75Matrix::BusHub75Matrix(const BusConfig &bc) : Bus(bc.type, bc.start, bc.
// HUB75_I2S_CFG::i2s_pins _pins={R1_PIN, G1_PIN, B1_PIN, R2_PIN, G2_PIN, B2_PIN, A_PIN, B_PIN, C_PIN, D_PIN, E_PIN, LAT_PIN, OE_PIN, CLK_PIN};
mxconfig.gpio = { 1, 5, 6, 7, 13, 9, 16, 48, 47, 21, 38, 8, 4, 18 };
#elif defined(WAVESHARE_S3_PINOUT)
DEBUGBUS_PRINTLN("MatrixPanel_I2S_DMA - Waveshare S3 with PSRAM, Waveshare pinout");
// HUB75_I2S_CFG::i2s_pins _pins={R1_PIN, G1_PIN, B1_PIN, R2_PIN, G2_PIN, B2_PIN, A_PIN, B_PIN, C_PIN, D_PIN, E_PIN, LAT_PIN, OE_PIN, CLK_PIN};
mxconfig.gpio = {4, 5, 6, 7, 15, 16, 18, 8, 3, 42, 9, 40, 2, 41};
#else
DEBUGBUS_PRINTLN("MatrixPanel_I2S_DMA - S3 with PSRAM");
// HUB75_I2S_CFG::i2s_pins _pins={R1_PIN, G1_PIN, B1_PIN, R2_PIN, G2_PIN, B2_PIN, A_PIN, B_PIN, C_PIN, D_PIN, E_PIN, LAT_PIN, OE_PIN, CLK_PIN};
@@ -990,10 +1003,8 @@ BusHub75Matrix::BusHub75Matrix(const BusConfig &bc) : Bus(bc.type, bc.start, bc.
}
setBitArray(_ledsDirty, _len, false); // reset dirty bits
if (mxconfig.double_buff == false) {
// create LEDs buffer (initialized to BLACK), prefer DRAM if enough heap is available (faster in case global _pixels buffer is in PSRAM as not both will fit the cache)
_ledBuffer = static_cast<CRGB*>(allocate_buffer(_len * sizeof(CRGB), BFRALLOC_PREFER_DRAM | BFRALLOC_CLEAR));
}
// create LEDs buffer (initialized to BLACK), prefer DRAM if enough heap is available (faster in case global _pixels buffer is in PSRAM as not both will fit the cache)
_ledBuffer = static_cast<CRGB*>(allocate_buffer(_len * sizeof(CRGB), BFRALLOC_PREFER_DRAM | BFRALLOC_CLEAR));
}
PANEL_CHAIN_TYPE chainType = CHAIN_NONE; // default for quarter-scan panels that do not use chaining
+5 -5
View File
@@ -252,18 +252,18 @@
var buttonBlock = document.createElement('div');
buttonBlock.className = 'bb';
buttonBlock.innerHTML = `
<div class="bh">Button ${i}</div>
<div class="bh">Button (switch) ${i}</div>
<div class="bs">
<div class="ba">
<label>Push/Switch</label>
<label>Short (on → off)</label>
<select name="MP${b}" class="s" required>${presetOpts}</select>
</div>
<div class="ba">
<label>Short (on→off)</label>
<label>Long (off → on)</label>
<select name="ML${b}" class="s" required>${presetOpts}</select>
</div>
<div class="ba">
<label>Long (off→on)</label>
<label>Double press (n/a)</label>
<select name="MD${b}" class="s" required>${presetOpts}</select>
</div>
</div>
@@ -372,7 +372,7 @@
Alexa On/Off Preset: <input name="A0" class="m" type="number" min="0" max="250" required> <input name="A1" class="m" type="number" min="0" max="250" required><br>
</div>
<div class="sec">
<h3>Button Action Presets</h3>
<h3>Button (switch) Action Presets</h3>
<div id="macros"></div>
<a href="https://kno.wled.ge/features/macros/#analog-button" target="_blank">Analog Button setup</a>
</div>
+6 -7
View File
@@ -116,8 +116,8 @@ void handleImprovPacket() {
return;
}
} else if (packetByte > 9) { //RPC data
rpcData[packetByte - 10] = next;
if (packetByte > 137) return; //prevent buffer overflow
rpcData[packetByte - 10] = next;
}
}
}
@@ -238,8 +238,8 @@ void handleImprovWifiScan() {
bool isOpen = WiFi.encryptionType(i) == WIFI_AUTH_OPEN;
#endif
char ssidStr[33];
strcpy(ssidStr, WiFi.SSID(i).c_str());
char ssidStr[33] = {'\0'};
strlcpy(ssidStr, WiFi.SSID(i).c_str(), sizeof(ssidStr));
const char *str[3] = {ssidStr, rssiStr, isOpen ? "NO":"YES"};
sendImprovRPCResult(ImprovRPCType::Request_Scan, 3, str);
}
@@ -258,14 +258,13 @@ static void parseWiFiCommand(char* rpcData) {
unsigned ssidLen = rpcData[1];
if (ssidLen > len -1 || ssidLen > 32) return;
memset(multiWiFi[0].clientSSID, 0, 32);
memset(multiWiFi[0].clientSSID, 0, sizeof(multiWiFi[0].clientSSID));
memcpy(multiWiFi[0].clientSSID, rpcData+2, ssidLen);
memset(multiWiFi[0].clientPass, 0, 64);
memset(multiWiFi[0].clientPass, 0, sizeof(multiWiFi[0].clientPass));
if (len > ssidLen +1) {
unsigned passLen = rpcData[2+ssidLen];
memset(multiWiFi[0].clientPass, 0, 64);
memcpy(multiWiFi[0].clientPass, rpcData+3+ssidLen, passLen);
memcpy(multiWiFi[0].clientPass, rpcData+3+ssidLen, min(size_t(passLen), sizeof(multiWiFi[0].clientPass)));
}
sendImprovStateResponse(0x03); //provisioning
+16
View File
@@ -448,10 +448,26 @@ void WiFiEvent(WiFiEvent_t event)
}
break;
#ifdef ARDUINO_ARCH_ESP32
case ARDUINO_EVENT_WIFI_READY:
DEBUG_PRINTLN(F("WiFi-E: driver ready."));
break;
case ARDUINO_EVENT_WIFI_SCAN_DONE:
// also triggered when connected to selected SSID
DEBUG_PRINTLN(F("WiFi-E: SSID scan completed."));
break;
case ARDUINO_EVENT_WIFI_STA_START:
DEBUG_PRINTLN(F("WiFi-E: STA Started"));
break;
case ARDUINO_EVENT_WIFI_STA_STOP:
DEBUG_PRINTLN(F("WiFi-E: STA Stopped"));
break;
case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE:
DEBUG_PRINTLN(F("WiFi-E: STA authentication mode change."));
break;
case ARDUINO_EVENT_WIFI_STA_LOST_IP:
DEBUG_PRINTLN(F("WiFi-E: IP address lost."));
break;
case ARDUINO_EVENT_WIFI_AP_START:
DEBUG_PRINTLN(F("WiFi-E: AP Started"));
break;
+6 -3
View File
@@ -6,7 +6,7 @@
#define UDP_SEG_SIZE 36
#define SEG_OFFSET (41)
#define WLEDPACKETSIZE (41+(WS2812FX::getMaxSegments()*UDP_SEG_SIZE)+0)
static constexpr size_t WLEDPACKETSIZE = 41+(WS2812FX::getMaxSegments()*UDP_SEG_SIZE); // make sure this is known at compile-time
#define UDP_IN_MAXSIZE 1472
#define PRESUMED_NETWORK_DELAY 3 //how many ms could it take on avg to reach the receiver? This will be added to transmitted times
@@ -268,6 +268,7 @@ static void parseNotifyPacket(const uint8_t *udpIn) {
size_t inactiveSegs = 0;
for (size_t i = 0; i < numSrcSegs && i < WS2812FX::getMaxSegments(); i++) {
unsigned ofs = 41 + i*udpIn[40]; //start of segment offset byte
if (ofs + 36 > UDP_IN_MAXSIZE) break; // avoid reading outside of array
unsigned id = udpIn[0 +ofs];
DEBUG_PRINTF_P(PSTR("UDP segment received: %u\n"), id);
if (id > strip.getSegmentsNum()) break;
@@ -499,7 +500,7 @@ void handleNotifications()
packetSize = rgbUdp.parsePacket();
if (packetSize) {
if (!receiveDirect) return;
if (packetSize > UDP_IN_MAXSIZE || packetSize < 3) return;
if (packetSize > UDP_IN_MAXSIZE || packetSize < 3) return; // packetSize must not exceed buffersize (UDP_IN_MAXSIZE)
realtimeIP = rgbUdp.remoteIP();
DEBUG_PRINTLN(rgbUdp.remoteIP());
uint8_t lbuf[packetSize];
@@ -587,7 +588,9 @@ void handleNotifications()
unsigned id = (tpmPayloadFrameSize/3)*(packetNum-1); //start LED
unsigned totalLen = strip.getLengthTotal();
for (size_t i = 6; i < tpmPayloadFrameSize + 4U && id < totalLen; i += 3, id++) {
// Clamp to prevent buffer overread: loop accesses up to udpIn[tpmPayloadFrameSize + 5]
size_t currentPayloadFrameSize = (packetSize >= 5) ? min(tpmPayloadFrameSize, uint16_t(packetSize - 5)) : 0;
for (size_t i = 6; i < currentPayloadFrameSize + 4U && id < totalLen; i += 3, id++) {
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0);
}
if (tpmPacketCount == numPackets) { //reset packet count and show if all packets were received
+3
View File
@@ -1296,6 +1296,9 @@ String computeSHA1(const String& input) {
#ifdef ESP32
#include "esp_adc_cal.h"
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,4,7) // backwards compatibility patch
#define ADC_ATTEN_DB_12 ADC_ATTEN_DB_11
#endif
String generateDeviceFingerprint() {
uint32_t fp[2] = {0, 0}; // create 64 bit fingerprint
esp_chip_info_t chip_info;
+6
View File
@@ -657,6 +657,7 @@ void WLED::initAP(bool resetAP)
WiFi.softAPConfig(IPAddress(4, 3, 2, 1), IPAddress(4, 3, 2, 1), IPAddress(255, 255, 255, 0));
WiFi.softAP(apSSID, apPass, apChannel, apHide);
#ifdef ARDUINO_ARCH_ESP32
DEBUG_PRINT(F("access point maxTxPower set to ")); DEBUG_PRINTLN(txPower);
WiFi.setTxPower(wifi_power_t(txPower));
#endif
@@ -697,6 +698,7 @@ void WLED::initConnection()
}
#endif
DEBUG_PRINTLN(F("WiFi disconnect."));
WiFi.disconnect(true); // close old connections
delay(5); // wait for hardware to be ready
#ifdef ESP8266
@@ -709,6 +711,7 @@ void WLED::initConnection()
#ifdef ARDUINO_ARCH_ESP32
// Reset mode to NULL to force a full STA mode transition, so that WiFi.mode(WIFI_STA) below actually applies the hostname (and TX power, etc.).
// This is required on reconnects when mode is already WIFI_STA.
DEBUG_PRINTLN(F("WiFi mode_null: driver teardown / re-init."));
WiFi.mode(WIFI_MODE_NULL);
apActive = false; // the AP is physically torn down by WIFI_MODE_NULL
delay(5); // give the WiFi stack time to complete the mode transition
@@ -792,9 +795,12 @@ void WLED::initConnection()
#endif // WLED_ENABLE_WPA_ENTERPRISE
#ifdef ARDUINO_ARCH_ESP32
DEBUG_PRINT(F("WiFi maxTxPower set to ")); DEBUG_PRINT(txPower);
DEBUG_PRINT(F("; WiFi sleep ")); DEBUG_PRINTLN(noWifiSleep ? F("disabled."):F("enabled."));
WiFi.setTxPower(wifi_power_t(txPower));
WiFi.setSleep(!noWifiSleep);
#else // ESP8266 accepts a hostname set after WiFi interface initialization
DEBUG_PRINT(F("WiFi sleep ")); DEBUG_PRINTLN(noWifiSleep ? F("disabled."):F("enabled."));
wifi_set_sleep_type((noWifiSleep) ? NONE_SLEEP_T : MODEM_SLEEP_T);
WiFi.hostname(hostname);
#endif
+1 -1
View File
@@ -274,7 +274,7 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument<PSRAM_Allocator>;
#define STRINGIFY(X) #X
#define TOSTRING(X) STRINGIFY(X)
#define WLED_CODENAME "Niji"
#define WLED_CODENAME "Kagayaki"
// AP and OTA default passwords (for maximum security change them!)
WLED_GLOBAL char apPass[65] _INIT(WLED_AP_PASS);