Compare commits

..

73 Commits

Author SHA1 Message Date
Will Tatam 4374f013f5 16.0.0 2026-05-03 21:13:08 +01:00
Will Tatam f90990b869 Upload then add notes 2026-05-03 21:12:50 +01:00
Will Tatam 7d999a6d61 16.0.0-rc1 2026-05-03 21:12:50 +01:00
Damian Schneider dd0a83f9d1 align settings overview page width with rest of config pages 2026-05-03 21:45:30 +02:00
BobLoeffler68 dd01940cf5 Fixes issue in Dissolve when a palette has same color pixel as the current background color when Complete is enabled (#5453)
* Fixes issue in Dissolve when a palette has same color pixel as the current background color when Complete mode is enabled

---------

Co-authored-by: Damian Schneider <daedae@gmx.ch>
2026-05-03 21:28:24 +02:00
Will Tatam 0d93ca8bac Merge pull request #5553 from intermittech/add-quinled-v4-ethernet-profiles
Add Ethernet profiles for QuinLED Dig-Uno/Quad v4 and Dig-Octa 32-8L v4
2026-05-03 18:55:02 +01:00
Will Tatam a083217176 move platformio_release.ini.template to .github/ for cleaner project root 2026-05-03 18:42:43 +01:00
Will Tatam 86a218e2b4 Skip GPIO validation for HUB75 types in pin checks and clarify slot usage in getNumberOfPins logic 2026-05-03 18:42:43 +01:00
Will Tatam 28b441917b Update platformio configurations for 16MB ESP32-S3 MoonHub (HUB75) board 2026-05-03 18:42:43 +01:00
Will Tatam 129c68c6c9 Add support for HD-WF2 ESP32-S3 (HUB75 without PSRAM) 2026-05-03 18:42:43 +01:00
Will Tatam 923a1ec729 fix(ui): derive hasPSRAM from psrSz capability, not free bytes
Free PSRAM (info.psram) can be transiently 0 on PSRAM-capable boards,
which would wrongly reject multi-panel HUB75 setups in the LED settings
validation. Prefer info.psrSz (total PSRAM size) and fall back to
info.psram only if psrSz is unavailable.

Suggested in PR #5558 review.
2026-05-03 18:42:43 +01:00
Will Tatam 2ceeb67007 Relax panel limit for devices with PSRAM 2026-05-03 18:42:43 +01:00
Will Tatam 13aa7ed787 ci: build HUB75 envs in release workflow
Move the four HUB75 envs from platformio_override.sample.ini into
platformio.ini (refactored with a shared [hub75] section and extends=
to deduplicate), and gate them behind a release-only config template
so nightly/PR CI keep building only the standard env matrix.

The release workflow now copies platformio_release.ini.template into
place, which extends default_envs with the HUB75 envs.
2026-05-03 18:42:43 +01:00
Will Tatam 68640f5dd8 Fix AudioReactive palette names to avoid duplication with usermod prefix
Since the display format is now "UMName: palName", shorten the palette
names to just "Ratio", "Hue", and "Spectrum" — rendering as
"AudioReactive: Ratio" etc. rather than "AudioReactive: Audio Responsive Ratio".
2026-05-03 18:41:50 +01:00
Will Tatam 8a653ebb29 Use usermod name prefix for usermod-supplied palette names
Palette display names now follow the format "UMName: palName" (e.g.
"AudioReactive: Audio Responsive Hue") when a usermod supplies a
specific palName, keeping the usermod name as a consistent prefix.
Fallback when palName is nullptr remains "UMName index".

Update AudioReactive palette names to the full descriptive names from
the original WLED-MM port: "Audio Responsive Ratio", "Audio Responsive
Hue", and "Audio Responsive Spectrum".
2026-05-03 18:41:50 +01:00
Will Tatam dc5ee3f2fa Add name to usermod palette 2026-05-03 18:41:50 +01:00
Damian Schneider 5dc627ef5a remove unneeded check 2026-05-03 18:41:50 +01:00
Damian Schneider 520d8704c5 fix typo 2026-05-03 18:41:50 +01:00
Damian Schneider 55219980c6 rename removeUsermodPalettes 2026-05-03 18:41:50 +01:00
Damian Schneider 6dcae505a0 apply default pal before bounds check 2026-05-03 18:41:50 +01:00
Damian Schneider 52d897ed7a rename and bugfixes 2026-05-03 18:41:50 +01:00
Damian Schneider ef354d0119 add usermod palettes to fix AR palette indexing 2026-05-03 18:41:50 +01:00
Damian Schneider 89aae707d0 fix refresh of custom palettes 2026-05-03 18:41:50 +01:00
Will Miles 25668bb068 Validate gzipped firmware on ESP8266
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-03 18:41:17 +01:00
copilot-swe-agent[bot] 2c1f3c581d Refactor AR init debug logging for network-only sentinel
Agent-Logs-Url: https://github.com/wled/WLED/sessions/85b2a158-f6ba-45cf-88af-0d51b9202cab

Co-authored-by: netmindz <442066+netmindz@users.noreply.github.com>
2026-05-03 15:01:39 +01:00
copilot-swe-agent[bot] 1c8cd15cd1 Fix network-only dmType handling and debug diagnostics
Agent-Logs-Url: https://github.com/wled/WLED/sessions/85b2a158-f6ba-45cf-88af-0d51b9202cab

Co-authored-by: netmindz <442066+netmindz@users.noreply.github.com>
2026-05-03 15:01:39 +01:00
Will Tatam 32de108f4c Clean up redundant preprocessor directive in audio_reactive.cpp 2026-05-03 15:01:39 +01:00
Will Tatam e551ca833b Add audioreactive custom_usermod to multiple ESP8266 environments in platformio.ini now that we support network only option for 8266 2026-05-03 15:01:39 +01:00
Will Tatam 2fa5266dbf Enhance audio reactive logic with improved input validation and new "network receive only" option. 2026-05-03 15:01:39 +01:00
Will Tatam 46dafa2c6b ci: opt into Node.js 24 for GitHub Actions runners
Set FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true across all affected workflows
to avoid deprecation warnings ahead of the June 2026 forced migration.
2026-05-03 15:01:39 +01:00
Will Tatam 967ef0720c Relax artifact name regex to support forks with different project names 2026-05-03 15:01:39 +01:00
Will Tatam 82489664a0 Name CI artifacts by WLED_RELEASE_NAME instead of PlatformIO env
PR build artifact zips are now named firmware-<RELEASE_NAME> (e.g.
firmware-ESP32.zip) rather than firmware-<pio_env> (e.g.
firmware-esp32dev.zip), making it easier for users to identify the
correct build when testing PRs.
2026-05-03 15:01:39 +01:00
Damian Schneider 9fdd48776d relax restrictions on DDP protocol, minimal check for artNet (#5554) 2026-05-01 22:55:14 +02:00
Damian Schneider 74e0b166ba auto-migration for sunrise/sunset legacy config (#5555)
* add auto-migration for sunris/sunset legacy config
2026-05-01 14:44:07 +02:00
Damian Schneider 61bd8bf5b4 update VERSION 2026-05-01 13:34:40 +02:00
Damian Schneider f79cf568b2 move pixelforge json list and cpt city palettes to wled repo (#5541)
also removed commented code
2026-04-26 03:58:05 +02:00
Damian Schneider c53511b5aa add placeholder to keep custom palette ID's consistent (#5537) 2026-04-25 10:54:07 +02:00
Damian Schneider 0325e30bef cleanup of PR #5503: remove unnecessary changes (#5536) 2026-04-25 10:54:07 +02:00
Will Tatam 456ddee962 Merge pull request #5503 from RobertoD91/main
fix for serial port on esp32 (#5501)
2026-04-25 17:17:15 +01:00
Will Tatam 41e4bbf2bd Merge pull request #5473 from DedeHai/pin_dropdowns
Use drop-downs for pins
2026-04-25 17:17:15 +01:00
Damian Schneider 7f292b9ecd fix indentation 2026-04-22 09:57:21 +02:00
Brandon502 6afc990397 GoL missing return. (#5523) 2026-04-22 06:45:22 +02:00
Damian Schneider 974c15c1d3 Fix blending style options list filter for iOS (#5513)
* fix blending style options list filter for iOS
* fix default fallback
2026-04-20 07:52:18 +02:00
Damian Schneider 96b0c7b4ae better packet queuing & pacing for custom palette live preview (#5515)
* better packet queuing / pacing for custom palette live preview
* fix leak
* remove window prefix from variables
2026-04-20 07:37:33 +02:00
Frank Möhle 6394bec21b Highlight target branch for pull requests
Emphasize the importance of targeting the main branch for PRs.
2026-04-20 15:25:36 +02:00
Frank 79fde56b0e align AI instructions with `main` branch
some files were a bit behind - make sure the same instructions apply everywhere.
2026-04-20 14:27:32 +02:00
Frank Möhle e6077acb78 Update rules for PR descriptions and change logs
Added a guideline to prevent agents from overwriting PR descriptions (see https://github.com/orgs/community/discussions/187027).
2026-04-20 14:25:23 +02:00
Frank Möhle 83dd1b078d Merge pull request #5516 from kilrah/fix_dmx_ident
Add identifier string for DMX realtime mode
2026-04-19 18:44:34 +02:00
Damian Schneider c1e5ac5bd1 bugfixes in PS effects
- fix PS Sparkler for large setups: need 32bit random position, 16bit is not enough
- fix PS Fireworks 1D: need to `break` if no particles are available or it can lead to stalls on large setups
- do not use collisions by default PS Fuzzy Noise: its very slow on larger setups
2026-04-17 15:29:53 +02:00
Damian Schneider 1e42ebf852 mask out flag bits in DDP type for compatibility 2026-04-12 19:50:07 +02:00
Damian Schneider e4a7effdb0 add search icon back(#5500) 2026-04-12 19:08:05 +02:00
Damian Schneider ae36f7e203 use memory aligned allocations, fix bug in FFT magnitude (integer path) (#5499)
* use memory aligned allocations, fix bug in FFT magnitude (integer path)
* assign pointer to globals not local copy
2026-04-12 19:06:16 +02:00
Will Miles 4cdc23a48b Merge pull request #5497 from willmmiles/16_x-dynarray-fixes
0.16 - Backport dynarray fixes from V5 WIP
2026-04-12 11:34:41 -04:00
Will Miles 48ab88e11e Fix up usermod validation again
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 10:03:11 -04:00
Will Miles fd890b3d58 dynarray: Support ESP-IDF 2026-04-12 10:03:05 -04:00
Will Miles f994c5e995 validate_modules: Improve performance
Use readelf instead of nm for great speed.
2026-04-12 10:02:59 -04:00
Will Miles c789e3d187 Fix usermod count for LTO 2026-04-12 10:02:53 -04:00
Will Miles fadc75da55 dynarray: Directly apply linker fix
Rather than append a linker file, we edit the upstream supplied ones to
add our section to the binaries.  Works better on all platforms.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-12 10:02:47 -04:00
Will Tatam 945896ddb7 Merge pull request #5462 from willmmiles/16_x-unlimited-fxdata-size
Serialize fxdata without ArduinoJSON
2026-04-11 10:42:22 +01:00
Will Tatam 188beec614 Merge pull request #5465 from netmindz/usage-extended
New extended data for usage report
2026-04-11 08:21:06 +01:00
Will Tatam 19947ec07e Merge pull request #5489 from wled/copilot/fix-wled-discovery-issues
Fix Alexa/Hue discovery by correcting SSDP response to match UPnP spec
2026-04-10 18:30:07 +01:00
Damian Schneider 8228c7eea4 bugfix in pixelforge (no blur by default), fix glitch in animated gifs for C3 2026-04-07 23:52:28 +02:00
Will Miles 1aba7057a5 Merge pull request #5477 from willmmiles/16_x-ipv6-filter-oops
Fix leak in blockRouterAdvertisements
2026-04-04 21:21:22 -04:00
Will Miles 227d535404 Fix leak in blockRouterAdvertisements 2026-04-04 21:12:17 -04:00
Damian Schneider da8bfda2f0 bugfix: do not restrict segment inputs to allow trailing strips 2026-04-03 14:22:08 +02:00
Damian Schneider e4f2964bd6 hotfix: critical bug in candle FX, integer issue in flow FX 2026-04-02 18:44:52 +02:00
Will Miles a3f730275a Factor out streaming JSON primitives
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 23:30:32 -04:00
Frank b5556aa977 fix for HUB75 builds
replaces the last remaining FastLED.h with fstled_slim.h
2026-03-30 11:45:30 +02:00
Will Miles 82ddefff87 Remove declaration of removed function
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-03-29 23:48:43 -04:00
Will Miles 56a97a0caa Fix off-by-one in fxdata serializer
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-03-29 23:15:27 -04:00
Will Miles 679bb39922 Serialize fxdata without ArduinoJSON
Eliminates size limit.  Fixes #5458
2026-03-29 22:49:26 -04:00
Frank Möhle b293db6af5 Clarify instructions to always use the correct source code branch
Updated instructions for providing references in analysis results.
2026-03-30 00:38:34 +02:00
Will Tatam 774fbc5fab 16.0.0-beta 2026-03-29 22:35:23 +01:00
56 changed files with 475 additions and 1388 deletions
+10 -149
View File
@@ -6,8 +6,6 @@
# 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
@@ -17,7 +15,7 @@ language: en-US
reviews:
# generic review setting, see https://docs.coderabbit.ai/reference/configuration#reference
auto_apply_labels: false
auto_apply_labels: true
# abort_on_close: false
high_level_summary: true
review_status: true
@@ -26,11 +24,6 @@ reviews:
# sequence_diagrams: false
auto_review:
enabled: true
base_branches:
- main
- 16_x
- 0_15_x
- V5
ignore_title_keywords:
- WIP
@@ -38,7 +31,7 @@ reviews:
- path: "**/*.{cpp,h,hpp,ino}"
instructions: >
Follow the C++ coding conventions documented in docs/cpp.instructions.md
and the general project guidelines in AGENTS.md and .github/copilot-instructions.md.
and the general project guidelines in .github/copilot-instructions.md.
Key rules: 2-space indentation (no tabs), camelCase functions/variables,
PascalCase classes, UPPER_CASE macros. No C++ exceptions — use return codes and debug macros.
@@ -46,37 +39,8 @@ 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.
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.
# 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).
- path: "wled00/data/**"
instructions: >
@@ -85,21 +49,8 @@ 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.
# ── 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.
# 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).
- path: "wled00/html_*.h"
instructions: >
@@ -119,33 +70,8 @@ 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).
# ── 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
# 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).
- path: ".github/workflows/*.{yml,yaml}"
instructions: >
@@ -157,6 +83,8 @@ 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: |
@@ -174,73 +102,6 @@ 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:
-14
View File
@@ -1,14 +0,0 @@
{
"features": {
"ghcr.io/ar90n/devcontainer-features/platformio:1": {
"version": "1.0.2",
"resolved": "ghcr.io/ar90n/devcontainer-features/platformio@sha256:4a28f8c147d81ff996afebe6f43e453355b6373dd4022960351090b532ac9dd3",
"integrity": "sha256:4a28f8c147d81ff996afebe6f43e453355b6373dd4022960351090b532ac9dd3"
},
"ghcr.io/devcontainers/features/node:2": {
"version": "2.0.0",
"resolved": "ghcr.io/devcontainers/features/node@sha256:fedd4c11f7adfb64283b578dddc7da906728daa25fa293351c9d913231acf12f",
"integrity": "sha256:fedd4c11f7adfb64283b578dddc7da906728daa25fa293351c9d913231acf12f"
}
}
}
+9 -9
View File
@@ -1,10 +1,11 @@
{
"name": "WLED-dev",
"image": "mcr.microsoft.com/devcontainers/python:3",
"features": {
"ghcr.io/ar90n/devcontainer-features/platformio:1": {},
"ghcr.io/devcontainers/features/node:2": {
"version": "v20.18.3"
"name": "Python 3",
"build": {
"dockerfile": "Dockerfile",
"context": "..",
"args": {
// Update 'VARIANT' to pick a Python version: 3, 3.6, 3.7, 3.8, 3.9
"VARIANT": "3"
}
},
@@ -42,8 +43,7 @@
},
"extensions": [
"ms-python.python",
"platformio.platformio-ide",
"ms-vscode.cpptools-extension-pack"
"platformio.platformio-ide"
]
}
},
@@ -52,7 +52,7 @@
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "",
"postCreateCommand": "bash -i -c 'nvm install && npm ci'",
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "vscode"
+4 -7
View File
@@ -5,9 +5,7 @@ body:
- type: markdown
attributes:
value: |
Please quickly search existing issues first before submitting a bug.
Please don't submit long error analyses created by an AI agent, these are often totally wrong.
Just describe the problem in your own words.
Please quickly search existing issues first before submitting a bug.
- type: textarea
id: what-happened
attributes:
@@ -61,13 +59,12 @@ body:
options:
- ESP8266
- ESP32
- ESP32 with ethernet
- ESP32-S2
- ESP32-S3
- ESP32-S2
- ESP32-C3
- ESP32-C5 (experimental)
- Other
- ESP32-C6 (experimental)
- ESP32-P4 (experimental)
- ESP32-C5 (experimental)
validations:
required: true
- type: textarea
+12
View File
@@ -120,8 +120,20 @@ 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.
+2 -3
View File
@@ -4,7 +4,7 @@
; Copied to platformio_release.ini by the release CI workflow
; (.github/workflows/release.yml -> build.yml with `release: true`)
; in order to extend the matrix of `default_envs` built and published
; for tagged releases and remove the debugging usersmods env
; for tagged releases.
;
; This file overrides `[platformio].default_envs` from platformio.ini via
; `extra_configs`. It MUST list every env that should be released - including
@@ -16,7 +16,6 @@
[platformio]
default_envs = nodemcuv2
esp8266_2m
esp8266_2m_min
esp01_1m_full
nodemcuv2_160
esp8266_2m_160
@@ -35,8 +34,8 @@ default_envs = nodemcuv2
esp32s3dev_16MB_opi
esp32s3dev_8MB_opi
esp32s3dev_8MB_qspi
esp32s3dev_8MB_none
esp32s3_4M_qspi
usermods
; HUB75 release-only envs
esp32dev_hub75
esp32dev_hub75_forum_pinout
+2 -2
View File
@@ -29,12 +29,12 @@ jobs:
uses: janheinrichmerker/action-github-changelog-generator@v2.4
with:
token: ${{ secrets.GITHUB_TOKEN }}
sinceTag: v16.0.0
sinceTag: v0.15.0
output: CHANGELOG_NIGHTLY.md
# 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@5834076edc55cc05975561c9722043f072ac5c26
uses: andelf/nightly-release@main
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
+23 -106
View File
@@ -1,13 +1,10 @@
# AGENTS.md — WLED AI Coding Agent & AI Code Review Reference
# AGENTS.md — WLED Coding Agent Reference
WLED is C++ firmware for ESP32/ESP8266 microcontrollers controlling addressable LEDs,
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/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.
`docs/cpp.instructions.md`, `docs/web.instructions.md`, `docs/cicd.instructions.md`.
## Build Commands
@@ -17,8 +14,8 @@ Always reference these instructions - including coding guidelines in `docs/` - f
| `npm run build` | Build web UI into `wled00/html_*.h` / `wled00/js_*.h` | 30s |
| `npm test` | Run test suite (Node.js built-in `node --test`) | 2 min |
| `npm run dev` | Watch mode — auto-rebuilds web UI on changes | continuous |
| `pio run -e esp32dev` | Build firmware (ESP32, most common target) | 5 min |
| `pio run -e nodemcuv2` | Build firmware (ESP8266) | 5 min |
| `pio run -e esp32dev` | Build firmware (ESP32, most common target) | 30 min |
| `pio run -e nodemcuv2` | Build firmware (ESP8266) | 30 min |
**Always run `npm ci && npm run build` before `pio run`.** The web UI build generates
required C headers for firmware compilation.
@@ -28,7 +25,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:
```bash
```sh
npm test # runs all tests via `node --test`
node --test tools/cdata-test.js # run just that file directly
```
@@ -42,7 +39,7 @@ target environments. Always build after code changes: `pio run -e esp32dev`.
### Recovery / Troubleshooting
```bash
```sh
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
@@ -51,7 +48,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)
@@ -64,19 +61,6 @@ docs/ # Coding convention docs
.github/workflows/ # CI/CD (GitHub Actions)
```
### Branch / Release Structure
```text
main # Main development trunk (daily/nightly) 17.0.0-dev. Target branch for PRs.
├── 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 # maintenance for release 16.0.x
0_15_x # maintenance (bugfixes only) for previous release 0.15.x
(tag) v0.14.4 # old version 0.14.4 (no maintenance)
(tag) v0.13.3 # old version 0.13.3 (no maintenance)
(tag) v0. ... . ... # historical versions 0.12.x and before
```
## C++ Code Style (wled00/, usermods/)
### Formatting
@@ -86,10 +70,6 @@ main # Main development trunk (daily/nightly) 17.0.0-dev. Target
- Space after keywords (`if (...)`, `for (...)`), no space before function parens (`doStuff(a)`)
- No enforced line-length limit
### Comments
- `//` for inline (always space after), `/* */` for block comments
- Important: AI-generated source code blocks **must be mark with `// AI: below section was generated by an AI` / `// AI: end`**
### Naming Conventions
| Kind | Convention | Examples |
|---|---|---|
@@ -125,24 +105,16 @@ main # Main development trunk (daily/nightly) 17.0.0-dev. Target
- No VLAs — use fixed arrays or heap allocation
- Call `reserve()` on strings/vectors to pre-allocate and avoid fragmentation
#### ESP32 PSRAM guidelines
- **Check availability**: Test chip availability with `psramFound() && ESP.getPsramSize() > 0` before assuming PSRAM is present. Never rely on `BOARD_HAS_PSRAM`only.
- **DMA compatibility**: on ESP32 (classic), PSRAM buffers are **not DMA-capable**. On ESP32-S3 with octal PSRAM (`CONFIG_SPIRAM_MODE_OCT`), PSRAM buffers *can* be used with DMA when `CONFIG_SOC_PSRAM_DMA_CAPABLE` is defined.
- **Fragmentation**: PSRAM allocations fragment less than DRAM because the region is larger. But avoid mixing small and large allocations in PSRAM — small allocations waste the MMU page granularity.
- **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.
### Preprocessor / Feature Flags
- Feature toggling: `WLED_DISABLE_*` and `WLED_ENABLE_*` flags (exact names matter!)
- `WLED_DISABLE_*`: `2D`, `ADALIGHT`, `ALEXA`, `MQTT`, `OTA`, `INFRARED`, `WEBSOCKETS`, etc.
- `WLED_ENABLE_*`: `DMX`, `GIF`, `HUB75MATRIX`, `JSONLIVE`, `WEBSOCKETS`, etc.
- Platform: `ARDUINO_ARCH_ESP32`, `ESP8266`, `CONFIG_IDF_TARGET_ESP32S3`
### Comments
- `//` for inline (always space after), `/* */` for block comments
- AI-generated blocks: mark with `// AI: below section was generated by an AI` / `// AI: end`
### Math Functions
- Use `sin8_t()`, `cos8_t()` — NOT `sin8()`, `cos8()` (removed, won't compile)
- Use `sin_approx()` / `cos_approx()` instead of `sinf()` / `cosf()`
@@ -158,13 +130,6 @@ Background Info:
- `delay(1)` in custom FreeRTOS tasks (NOT `yield()`) — feeds IDLE watchdog
- Do not use `delay()` in effects (FX.cpp) or hot pixel path
#### 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 using `std::atomic` or RAII scoped guards as alternatives to mutexes, semaphores or queues.
## Web UI Code Style (wled00/data/)
- **Tab indentation** for HTML, JS, and CSS
@@ -183,48 +148,25 @@ class MyUsermod : public Usermod {
bool enabled = false;
static const char _name[];
public:
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)
void setup() override { /* ... */ }
void loop() override { /* ... */ }
void addToConfig(JsonObject& root) override { /* ... */ }
bool readFromConfig(JsonObject& root) override { /* ... */ }
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.
- Add usermod IDs to `wled00/const.h`
- 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`
- 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:
1. **Inter-usermod communication** — another usermod or an FX effect calls `UsermodManager::lookup(mod_id)` or `UsermodManager::getUMData(..., mod_id)` to find or request data from this specific usermod.
2. **Pin ownership via `pinManager`** — the usermod allocates GPIO pins through `pinManager`. Pin ownership is tracked by `PinOwner` enum values that map directly to `USERMOD_ID_*` constants (see `wled00/pin_manager.h`). This prevents pin-conflict bugs.
3. **Identification in JSON info**`UsermodManager::addToJsonInfo` emits each mod's ID into the `"um"` array; a unique ID makes the mod identifiable in that output.
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`.
### 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`)
@@ -233,35 +175,10 @@ No automated linting is configured. Match existing code style in files you edit.
## General Rules
- Repository language is English.
- 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`.
- Repository language is English
- 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.
- No force-push on open PRs!
- Important: **Changes to `platformio.ini` require maintainer approval**!
- PRs should respect `.gitignore` and not upload files like `platformio_override.ini`. PR authors may add buildenv examples for custom boards into `platformio_override.ini.sample`.
- 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.
- No force-push on open PRs
- Changes to `platformio.ini` require maintainer approval
- Remove dead/unused code — justify or delete it
- Verify feature-flag spelling exactly (misspellings are silently ignored by preprocessor)
-2
View File
@@ -515,8 +515,6 @@ 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:
+1 -1
View File
@@ -413,7 +413,7 @@ WLED provides convenience wrappers with automatic fallback. **Always prefer thes
### PSRAM guidelines
- **Check availability**: test availability with `psramFound() && ESP.getPsramSize() > 0` before assuming PSRAM is present. Never rely on `BOARD_HAS_PSRAM`only.
- **Check availability**: always test `psramFound()` before assuming PSRAM is present.
- **DMA compatibility**: on ESP32 (classic), PSRAM buffers are **not DMA-capable** — use `d_malloc_only()` to allocate DMA buffers in DRAM only. On ESP32-S3 with octal PSRAM (`CONFIG_SPIRAM_MODE_OCT`), PSRAM buffers *can* be used with DMA when `CONFIG_SOC_PSRAM_DMA_CAPABLE` is defined.
- **JSON documents**: use the `PSRAMDynamicJsonDocument` allocator (defined in `wled.h`) to put large JSON documents in PSRAM:
```cpp
-58
View File
@@ -1,58 +0,0 @@
---
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
@@ -1,227 +0,0 @@
---
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.
-6
View File
@@ -23,12 +23,6 @@ applyTo: "wled00/data/**"
**Reuse shared helpers from `common.js` whenever possible** instead of duplicating utilities in page-local scripts.
## Accessibility & Interaction
The WLED web UI targets commonly used browser/platform combinations: desktop browsers on Mac and PC (primarily pointer-driven, touch rare),
and touch-only devices (phones, tablets). If possible, keep the UI accessible to users with disabilities.
Full keyboard operability is not a strict requirement - adding keyboard shortcuts should be a case-by-case decision.
## Build Integration
Files in this directory are processed by `tools/cdata.js` into generated headers
+8 -8
View File
@@ -1,12 +1,12 @@
{
"name": "wled",
"version": "17.0.0-dev",
"version": "16.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "wled",
"version": "17.0.0-dev",
"version": "16.0.0",
"license": "ISC",
"dependencies": {
"clean-css": "^5.3.3",
@@ -132,9 +132,9 @@
}
},
"node_modules/brace-expansion": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz",
"integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==",
"license": "MIT",
"dependencies": {
"balanced-match": "^4.0.2"
@@ -611,9 +611,9 @@
}
},
"node_modules/picomatch": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"license": "MIT",
"engines": {
"node": ">=8.6"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "wled",
"version": "17.0.0-dev",
"version": "16.0.0",
"description": "Tools for WLED project",
"main": "tools/cdata.js",
"directories": {
+2 -8
View File
@@ -2,7 +2,6 @@
# This is implemented as a pio post-script to ensure that we can
# place our linker script at the correct point in the command arguments.
Import("env")
import shutil
from pathlib import Path
# Linker script fragment injected into the rodata output section of whichever
@@ -19,13 +18,7 @@ DYNARRAY_INJECTION = (
def inject_before_marker(path, marker):
"""Patch a linker script file in-place, inserting DYNARRAY_INJECTION before marker."""
original = path.read_text()
marker_pos = original.find(marker)
if marker_pos < 0:
raise RuntimeError(
f"DYNARRAY injection marker not found in linker script: path={path}, marker={marker!r}"
)
patched = original[:marker_pos] + DYNARRAY_INJECTION + original[marker_pos:]
path.write_text(patched)
path.write_text(original.replace(marker, DYNARRAY_INJECTION + marker, 1))
if env.get("PIOPLATFORM") == "espressif32":
@@ -45,6 +38,7 @@ if env.get("PIOPLATFORM") == "espressif32":
# leaves the ASSERTs satisfied.
build_dir = Path(env.subst("$BUILD_DIR"))
patched_path = build_dir / "dynarray_sections.ld"
import shutil
shutil.copy(sections_ld_path, patched_path)
inject_before_marker(patched_path, "_rodata_end = ABSOLUTE(.);")
+5 -12
View File
@@ -37,14 +37,14 @@ def check_elf_modules(elf_path: Path, env, module_lib_builders) -> set[str]:
Returns the set of build_dir basenames for confirmed modules.
"""
readelf_path = _get_readelf_path(env)
secho(f"INFO: Checking for usermod compilation units...")
try:
result = subprocess.run(
[readelf_path, "--debug-dump=info", "--dwarf-depth=1", str(elf_path)],
capture_output=True, text=True, errors="ignore", timeout=120,
)
output = result.stdout
if result.returncode != 0 or result.stderr.strip():
secho(f"WARNING: readelf exited {result.returncode}: {result.stderr.strip()}", fg="yellow", err=True)
except (subprocess.TimeoutExpired, FileNotFoundError, OSError) as e:
secho(f"WARNING: readelf failed ({e}); skipping per-module validation", fg="yellow", err=True)
return {Path(b.build_dir).name for b in module_lib_builders} # conservative pass
@@ -143,27 +143,20 @@ def validate_map_file(source, target, env):
Exit(1)
# Identify the WLED module builders, set by load_usermods.py
module_lib_builders = env.get('WLED_MODULES')
if module_lib_builders is None:
secho("ERROR: WLED_MODULES not set — ensure load_usermods.py is run as a pre: script", fg="red", err=True)
Exit(1)
module_lib_builders = env['WLED_MODULES']
# Extract the values we care about
modules = {Path(builder.build_dir).name: builder.name for builder in module_lib_builders}
secho(f"INFO: {len(modules)} libraries included as WLED optional/user modules")
secho(f"INFO: {len(modules)} libraries linked as WLED optional/user modules")
# Now parse the map file
map_file_contents = read_lines(map_file_path)
usermod_object_count = count_usermod_objects(map_file_contents)
secho(f"INFO: {usermod_object_count} usermod object entries found")
secho(f"INFO: {usermod_object_count} usermod object entries")
elf_path = build_dir / env.subst("${PROGNAME}.elf")
confirmed_modules = check_elf_modules(elf_path, env, module_lib_builders)
if confirmed_modules:
secho(f"INFO: Code from usermod libraries found in binary: {', '.join(confirmed_modules)}")
# else - if there's no usermods found, don't generate a message. If we're legitimately missing all entries, the error report on the
# next line will trip; and if the usermod set is expected to be empty, then there's no need for yet another null message.
missing_modules = [modname for mdir, modname in modules.items() if mdir not in confirmed_modules]
if missing_modules:
+124 -136
View File
@@ -30,7 +30,6 @@ default_envs = nodemcuv2
esp32s3dev_16MB_opi
esp32s3dev_8MB_opi
esp32s3dev_8MB_qspi
esp32s3dev_8MB_none
esp32s3_4M_qspi
usermods
@@ -42,6 +41,40 @@ extra_configs =
platformio_release.ini
[common]
# ------------------------------------------------------------------------------
# PLATFORM:
# !! DO NOT confuse platformio's ESP8266 development platform with Arduino core for ESP8266
#
# arduino core 2.6.3 = platformIO 2.3.2
# arduino core 2.7.0 = platformIO 2.5.0
# ------------------------------------------------------------------------------
arduino_core_2_6_3 = espressif8266@2.3.3
arduino_core_2_7_4 = espressif8266@2.6.2
arduino_core_3_0_0 = espressif8266@3.0.0
arduino_core_3_0_2 = espressif8266@3.2.0
arduino_core_3_1_0 = espressif8266@4.1.0
arduino_core_3_1_2 = espressif8266@4.2.1
# Development platforms
arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop
arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/stage
# Platform to use for ESP8266
platform_wled_default = ${common.arduino_core_3_1_2}
# We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization
#platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
platform_packages = platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502
platformio/tool-esptool #@ ~1.413.0
platformio/tool-esptoolpy #@ ~1.30000.0
## previous platform for 8266, in case of problems with the new one
## you'll need makuna/NeoPixelBus@ 2.6.9 for arduino_core_3_0_2, which does not support Ucs890x
;; platform_wled_default = ${common.arduino_core_3_0_2}
;; platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
;; platformio/toolchain-xtensa @ ~2.40802.200502
;; platformio/tool-esptool @ ~1.413.0
;; platformio/tool-esptoolpy @ ~1.30000.0
# ------------------------------------------------------------------------------
# FLAGS: DEBUG
# esp8266 : see https://docs.platformio.org/en/latest/platforms/espressif8266.html#debug-level
@@ -160,41 +193,6 @@ lib_deps =
extra_scripts = ${scripts_defaults.extra_scripts}
[esp8266]
# ------------------------------------------------------------------------------
# PLATFORM:
# !! DO NOT confuse platformio's ESP8266 development platform with Arduino core for ESP8266
#
# arduino core 2.6.3 = platformIO 2.3.2
# arduino core 2.7.0 = platformIO 2.5.0
# ------------------------------------------------------------------------------
arduino_core_2_6_3 = espressif8266@2.3.3
arduino_core_2_7_4 = espressif8266@2.6.2
arduino_core_3_0_0 = espressif8266@3.0.0
arduino_core_3_0_2 = espressif8266@3.2.0
arduino_core_3_1_0 = espressif8266@4.1.0
arduino_core_3_1_2 = espressif8266@4.2.1
# Development platforms
arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop
arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/stage
# Platform to use for ESP8266
platform_wled_default = ${esp8266.arduino_core_3_1_2}
# We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization
#platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
platform_packages = platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502
platformio/tool-esptool #@ ~1.413.0
platformio/tool-esptoolpy #@ ~1.30000.0
## previous platform for 8266, in case of problems with the new one
## you'll need makuna/NeoPixelBus@ 2.6.9 for arduino_core_3_0_2, which does not support Ucs890x
;; platform_wled_default = ${esp8266.arduino_core_3_0_2}
;; platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
;; platformio/toolchain-xtensa @ ~2.40802.200502
;; platformio/tool-esptool @ ~1.413.0
;; platformio/tool-esptoolpy @ ~1.30000.0
platform = ${esp8266.platform_wled_default}
build_unflags = ${common.build_unflags}
build_flags =
-DESP8266
@@ -227,8 +225,6 @@ lib_deps =
https://github.com/tignioj/ArduinoUZlib.git#20aff95cd80c141f80bdbf66895409a0046d2c2f
${env.lib_deps}
monitor_filters = esp8266_exception_decoder
;; compatibilty flags - same as 0.14.0 which seems to work better on some 8266 boards. Not using PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48
build_flags_compat =
-DESP8266
@@ -276,7 +272,6 @@ platform_packages =
build_unflags = ${common.build_unflags}
build_flags = ${esp32_idf_V4.build_flags}
lib_deps = ${esp32_idf_V4.lib_deps}
monitor_filters = esp32_exception_decoder
tiny_partitions = tools/WLED_ESP32_2MB_noOTA.csv
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
@@ -298,18 +293,8 @@ 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
@@ -339,7 +324,6 @@ build_flags = -g
lib_deps =
${esp32_idf_V4.lib_deps}
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
monitor_filters = esp32_exception_decoder
[esp32c3]
;; generic definitions for all ESP32-C3 boards
@@ -359,7 +343,6 @@ lib_deps =
${esp32_idf_V4.lib_deps}
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
board_build.flash_mode = qio
monitor_filters = esp32_exception_decoder
[esp32s3]
;; generic definitions for all ESP32-S3 boards
@@ -379,8 +362,6 @@ build_flags = -g
lib_deps =
${esp32_idf_V4.lib_deps}
board_build.partitions = ${esp32.large_partitions} ;; default partioning for 8MB flash - can be overridden in build envs
upload_speed = 921600
monitor_filters = esp32_exception_decoder
# ------------------------------------------------------------------------------
@@ -388,11 +369,15 @@ monitor_filters = esp32_exception_decoder
# ------------------------------------------------------------------------------
[env:nodemcuv2]
extends = esp8266
board = nodemcuv2
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP8266\" #-DWLED_DISABLE_2D
-D WLED_DISABLE_PARTICLESYSTEM2D
lib_deps = ${esp8266.lib_deps}
monitor_filters = esp8266_exception_decoder
custom_usermods = audioreactive
[env:nodemcuv2_compat]
@@ -413,12 +398,15 @@ build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=
custom_usermods = audioreactive
[env:esp8266_2m]
extends = esp8266
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP02\"
-D WLED_DISABLE_PARTICLESYSTEM2D
-D WLED_DISABLE_PARTICLESYSTEM1D
lib_deps = ${esp8266.lib_deps}
custom_usermods = audioreactive
[env:esp8266_2m_compat]
@@ -439,37 +427,18 @@ build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=
-D WLED_DISABLE_PARTICLESYSTEM2D
custom_usermods = audioreactive
[env:esp8266_2m_min]
;; Minimal-feature build for ESP02 (2MB flash).
;; Use this to recover from a failed OTA: flash via serial, then OTA-upload the regular esp8266_2m binary.
;; OTA is intentionally kept enabled. All other optional features are stripped to minimise binary size.
extends = esp8266
board = esp_wroom_02
board_build.ldscript = ${common.ldscript_2m512k}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP02_min\"
-D WLED_DISABLE_ALEXA
-D WLED_DISABLE_HUESYNC
-D WLED_DISABLE_INFRARED
-D WLED_DISABLE_MQTT
-D WLED_DISABLE_ADALIGHT
-D WLED_DISABLE_LOXONE
-D WLED_DISABLE_WEBSOCKETS
-D WLED_DISABLE_ESPNOW
-D WLED_DISABLE_2D
-D WLED_DISABLE_PARTICLESYSTEM1D
-D WLED_DISABLE_PARTICLESYSTEM2D
-D WLED_DISABLE_PIXELFORGE
-D WLED_DISABLE_IMPROV_WIFISCAN
[env:esp01_1m_full]
extends = esp8266
board = esp01_1m
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP01\" -D WLED_DISABLE_OTA
; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM
-D WLED_DISABLE_PARTICLESYSTEM1D
-D WLED_DISABLE_PARTICLESYSTEM2D
-D WLED_DISABLE_PIXELFORGE
lib_deps = ${esp8266.lib_deps}
[env:esp01_1m_full_compat]
extends = env:esp01_1m_full
@@ -492,11 +461,15 @@ build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=
custom_usermods = audioreactive
[env:esp32dev]
extends = esp32
board = esp32dev
platform = ${esp32_idf_V4.platform}
platform_packages = ${esp32_idf_V4.platform_packages}
build_unflags = ${common.build_unflags}
custom_usermods = audioreactive
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32\" #-D WLED_DISABLE_BROWNOUT_DET
-DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
lib_deps = ${esp32_idf_V4.lib_deps}
monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.default_partitions}
board_build.flash_mode = dio
@@ -508,33 +481,50 @@ build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags}
-D WLED_RELEASE_NAME=\"ESP32_DEBUG\"
[env:esp32dev_8M]
extends = env:esp32dev
board = esp32dev
platform = ${esp32_idf_V4.platform}
platform_packages = ${esp32_idf_V4.platform_packages}
custom_usermods = audioreactive
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_8M\" #-D WLED_DISABLE_BROWNOUT_DET
-DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
lib_deps = ${esp32_idf_V4.lib_deps}
monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.large_partitions}
board_upload.flash_size = 8MB
board_upload.maximum_size = 8388608
; board_build.f_flash = 80000000L
board_build.flash_mode = dio
[env:esp32dev_16M]
extends = env:esp32dev
board = esp32dev
platform = ${esp32_idf_V4.platform}
platform_packages = ${esp32_idf_V4.platform_packages}
custom_usermods = audioreactive
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_16M\" #-D WLED_DISABLE_BROWNOUT_DET
-DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
lib_deps = ${esp32_idf_V4.lib_deps}
monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.extreme_partitions}
board_upload.flash_size = 16MB
board_upload.maximum_size = 16777216
board_build.f_flash = 80000000L
board_build.flash_mode = dio
[env:esp32_eth]
extends = esp32
board = esp32-poe
platform = ${esp32_idf_V4.platform}
platform_packages = ${esp32_idf_V4.platform_packages}
upload_speed = 921600
custom_usermods = audioreactive
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32_Ethernet\" -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1
-D SR_DMTYPE=-1 -D AUDIOPIN=-1 -D I2S_SDPIN=-1 -D I2S_WSPIN=-1 -D I2S_CKPIN=-1 -D MCLK_PIN=-1 ;; force AR to not allocate any PINs at startup
-D DATA_PINS=4 ;; default led pin = 16 conflicts with pins used for ethernet
; -D WLED_DISABLE_ESPNOW ;; ESP-NOW requires wifi, may crash with ethernet only => uncomment if your board uses ETH_CLOCK_GPIO0_OUT, ETH_CLOCK_GPIO16_OUT, ETH_CLOCK_GPIO17_OUT
-DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
lib_deps = ${esp32.lib_deps}
board_build.partitions = ${esp32.default_partitions}
board_build.flash_mode = dio
@@ -545,14 +535,20 @@ board_build.f_flash = 80000000L
board_build.flash_mode = qio
board_build.partitions = ${esp32.extended_partitions}
custom_usermods = audioreactive
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_WROVER\"
-DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
-DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue ;; Older ESP32 (rev.<3) need a PSRAM fix (increases static RAM used) https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/external-ram.html
-D DATA_PINS=25
lib_deps = ${esp32_idf_V4.lib_deps}
[env:esp32c3dev]
extends = esp32c3
platform = ${esp32c3.platform}
platform_packages = ${esp32c3.platform_packages}
framework = arduino
board = esp32-c3-devkitm-1
board_build.partitions = ${esp32.default_partitions}
custom_usermods = audioreactive
build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-C3\"
-D WLED_WATCHDOG_TIMEOUT=0
@@ -560,6 +556,8 @@ build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME=
-DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB
;-DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip
upload_speed = 460800
build_unflags = ${common.build_unflags}
lib_deps = ${esp32c3.lib_deps}
board_build.flash_mode = dio ; safe default, required for OTA updates to 0.16 from older version which used dio (must match the bootloader!)
[env:esp32c3dev_qio]
@@ -569,34 +567,45 @@ board_build.flash_mode = qio ; qio is faster and works on almost all boards (som
[env:esp32s3dev_16MB_opi]
;; ESP32-S3 development board, with 16MB FLASH and >= 8MB PSRAM (memory_type: qio_opi)
extends = esp32s3
board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support
board_build.arduino.memory_type = qio_opi ;; use with PSRAM: 8MB or 16MB
platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600
custom_usermods = audioreactive
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_16MB_opi\"
-D WLED_WATCHDOG_TIMEOUT=0
;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
-DBOARD_HAS_PSRAM
lib_deps = ${esp32s3.lib_deps}
board_build.partitions = ${esp32.extreme_partitions}
board_upload.flash_size = 16MB
board_upload.maximum_size = 16777216
board_build.f_flash = 80000000L
board_build.flash_mode = qio
monitor_filters = esp32_exception_decoder
[env:esp32s3dev_8MB_opi]
;; ESP32-S3 development board, with 8MB FLASH and >= 8MB PSRAM (memory_type: qio_opi)
extends = esp32s3
board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support
board_build.arduino.memory_type = qio_opi ;; use with PSRAM: 8MB or 16MB
platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600
custom_usermods = audioreactive
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_8MB_opi\"
-D WLED_WATCHDOG_TIMEOUT=0
;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
-DBOARD_HAS_PSRAM
lib_deps = ${esp32s3.lib_deps}
board_build.partitions = ${esp32.large_partitions}
board_build.f_flash = 80000000L
board_build.flash_mode = qio
monitor_filters = esp32_exception_decoder
[env:esp32s3dev_8MB_qspi]
;; generic ESP32-S3 board with 8MB FLASH and PSRAM (memory_type: qio_qspi). Try this one if esp32s3dev_8MB_opi does not work on your board
@@ -609,27 +618,18 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=
-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
-DBOARD_HAS_PSRAM
;; -DLOLIN_WIFI_FIX ;; uncomment if you have WiFi connectivity problems
[env:esp32s3dev_8MB_none]
;; ESP32-S3 development board, 8MB FLASH, no PSRAM
extends = esp32s3
board = esp32-s3-devkitc-1 ;; generic dev board
custom_usermods = audioreactive
build_unflags = ${esp32s3.build_unflags} -DBOARD_HAS_PSRAM ;; make sure PSRAM support is removed
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_8MB_none\"
-D WLED_WATCHDOG_TIMEOUT=0
;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
board_build.f_flash = 80000000L
board_build.flash_mode = qio
monitor_filters = esp32_exception_decoder
[env:esp32S3_wroom2]
;; For ESP32-S3 WROOM-2, a.k.a. ESP32-S3 DevKitC-1 v1.1
;; with >= 16MB FLASH and >= 8MB PSRAM (memory_type: opi_opi)
extends = esp32s3
platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages}
board = esp32s3camlcd ;; this is the only standard board with "opi_opi"
board_build.arduino.memory_type = opi_opi
upload_speed = 921600
custom_usermods = audioreactive
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_WROOM-2\"
-D WLED_WATCHDOG_TIMEOUT=0
-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
@@ -639,9 +639,12 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=
-D BTNPIN=0 -D RLYPIN=16 -D IRPIN=17 -D AUDIOPIN=-1
;;-D WLED_DEBUG
-D SR_DMTYPE=1 -D I2S_SDPIN=13 -D I2S_CKPIN=14 -D I2S_WSPIN=15 -D MCLK_PIN=4 ;; I2S mic
lib_deps = ${esp32s3.lib_deps}
board_build.partitions = ${esp32.extreme_partitions}
board_upload.flash_size = 16MB
board_upload.maximum_size = 16777216
monitor_filters = esp32_exception_decoder
[env:esp32S3_wroom2_32MB]
;; For ESP32-S3 WROOM-2 with 32MB Flash, and >= 8MB PSRAM (memory_type: opi_opi)
@@ -658,42 +661,36 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=
board_build.partitions = tools/WLED_ESP32_32MB.csv
board_upload.flash_size = 32MB
board_upload.maximum_size = 33554432
monitor_filters = esp32_exception_decoder
[env:esp32s3_4M_qspi]
;; ESP32-S3, with 4MB FLASH and <= 4MB PSRAM (memory_type: qio_qspi)
extends = esp32s3
board = lolin_s3_mini ;; -S3 mini, 4MB flash 2MB PSRAM
board = lolin_s3_mini ;; -S3 mini, 4MB flash 2MB PSRAM
platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600
custom_usermods = audioreactive
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_4M_qspi\"
-DARDUINO_USB_CDC_ON_BOOT=1 ;; -DARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
-DBOARD_HAS_PSRAM
-DLOLIN_WIFI_FIX ; seems to work much better with this
-D WLED_WATCHDOG_TIMEOUT=0
lib_deps = ${esp32s3.lib_deps}
board_build.partitions = ${esp32.default_partitions}
board_build.f_flash = 80000000L
board_build.flash_mode = qio
[env:esp32s3_4M_none]
;; ESP32-S3 with 4MB FLASH, no PSRAM
extends = esp32s3
board = esp32-s3-devkitc-1
custom_usermods = audioreactive
build_unflags = ${esp32s3.build_unflags} -D BOARD_HAS_PSRAM ;; make sure PSRAM support is removed
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_4M_none\"
-DARDUINO_USB_CDC_ON_BOOT=1 ;; -DARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
-DLOLIN_WIFI_FIX ; seems to work much better with this
-D WLED_WATCHDOG_TIMEOUT=0
board_build.partitions = ${esp32.default_partitions}
board_build.f_flash = 80000000L
board_build.flash_mode = qio
monitor_filters = esp32_exception_decoder
[env:lolin_s2_mini]
extends = esp32s2
platform = ${esp32s2.platform}
platform_packages = ${esp32s2.platform_packages}
board = lolin_s2_mini
board_build.partitions = ${esp32.default_partitions}
board_build.flash_mode = qio
board_build.f_flash = 80000000L
custom_usermods = audioreactive
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S2\"
-DARDUINO_USB_CDC_ON_BOOT=1
-DARDUINO_USB_MSC_ON_BOOT=0
@@ -708,12 +705,19 @@ build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=
-D HW_PIN_DATASPI=11
-D HW_PIN_MISOSPI=9
; -D STATUSLED=15
lib_deps = ${esp32s2.lib_deps}
[env:usermods]
extends = env:esp32dev
board = esp32dev
platform = ${esp32_idf_V4.platform}
platform_packages = ${esp32_idf_V4.platform_packages}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_USERMODS\"
-DTOUCH_CS=9
lib_deps = ${esp32_idf_V4.lib_deps}
monitor_filters = esp32_exception_decoder
board_build.flash_mode = dio
custom_usermods = * ; Expands to all usermods in usermods folder
board_build.partitions = ${esp32.extreme_partitions} ; We're gonna need a bigger boat
@@ -730,7 +734,6 @@ 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
@@ -771,8 +774,8 @@ build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} ${hub75.build_fl
; HD-WF2 - NOTE: this board has NO PSRAM, so BOARD_HAS_PSRAM must not be set
; (BOARD_HAS_PSRAM causes the DMA library to allocate only in SPIRAM, which fails without PSRAM)
extends = env:esp32s3dev_8MB_qspi
board_build.partitions = ${esp32.extended_partitions} ;; 1.65MB firmware, 700KB filesystem - only 4MB flash usable on this board
build_unflags = ${esp32s3.build_unflags} -DBOARD_HAS_PSRAM
board_build.partitions = ${esp32.extended_partitions} ;; 1.65MB firmware, 700KB filesystem
build_unflags = -DBOARD_HAS_PSRAM
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_HD-WF2\"
-D HD_WF2_PINOUT ;; Huidu HD-WF2 specific GPIO wiring
@@ -790,21 +793,6 @@ 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
+56 -59
View File
@@ -13,8 +13,8 @@ default_envs = WLED_generic8266_1M, esp32dev_V4_dio80 # put the name(s) of your
[env:WLED_generic8266_1M]
extends = env:esp01_1m_full # when you want to extend the existing environment (define only updated options)
; board = esp01_1m # uncomment when ou need different board
; platform = ${esp8266.platform_wled_default} # uncomment and change when you want particular platform
; platform_packages = ${esp8266.platform_packages}
; platform = ${common.platform_wled_default} # uncomment and change when you want particular platform
; platform_packages = ${common.platform_packages}
; board_build.ldscript = ${common.ldscript_1m128k}
; upload_speed = 921600 # fast upload speed (remove ';' if your board supports fast upload speed)
# Sample libraries used for various usermods. Uncomment when using particular usermod.
@@ -33,12 +33,7 @@ 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\"
;
@@ -103,17 +98,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
; custom_usermods = ${env:esp32dev.custom_usermods} auto_save
; -D USERMOD_AUTO_SAVE
; -D AUTOSAVE_AFTER_SEC=90
;
; Use AHT10/AHT15/AHT20 usermod
; custom_usermods = ${env:esp32dev.custom_usermods} AHT10_v2
; -D USERMOD_AHT10
;
; Use INA226 usermod
; custom_usermods = ${env:esp32dev.custom_usermods} INA226_v2
; -D USERMOD_INA226
;
; Use 4 Line Display usermod with SPI display
; custom_usermods = ${env:esp32dev.custom_usermods} four_line_display_ALT
; -D USERMOD_FOUR_LINE_DISPLAY
; -DFLD_SPI_DEFAULT
; -D FLD_TYPE=SSD1306_SPI64
; -D FLD_PIN_CLOCKSPI=14
@@ -123,22 +118,22 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
; -D FLD_PIN_RESET=27
;
; Use Rotary encoder usermod (in conjunction with 4LD)
; custom_usermods = ${env:esp32dev.custom_usermods} rotary_encoder_ui_ALT
; -D USERMOD_ROTARY_ENCODER_UI
; -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
; custom_usermods = ${env:esp32dev.custom_usermods} Temperature
; -D USERMOD_DALLASTEMPERATURE
; -D TEMPERATURE_PIN=13
;
; Use Multi Relay usermod and configure it to use 6 relays and appropriate GPIO
; custom_usermods = ${env:esp32dev.custom_usermods} multi_relay
; -D USERMOD_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
; custom_usermods = ${env:esp32dev.custom_usermods} PIR_sensor_switch
; -D USERMOD_PIRSWITCH
; -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)
@@ -151,12 +146,12 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
; -D I2S_CKPIN=19
;
; Use PWM fan usermod
; custom_usermods = ${env:esp32dev.custom_usermods} PWM_fan
; -D USERMOD_PWM_FAN
; -D TACHO_PIN=33
; -D PWM_PIN=32
;
; Use POV Display usermod
; custom_usermods = ${env:esp32dev.custom_usermods} pov_display
; -D USERMOD_POV_DISPLAY
; Use built-in or custom LED as a status indicator (assumes LED is connected to GPIO16)
; -D STATUSLED=16
;
@@ -220,8 +215,8 @@ build_flags =
[env:esp07]
board = esp07
platform = ${esp8266.platform_wled_default}
platform_packages = ${esp8266.platform_packages}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags}
@@ -229,8 +224,8 @@ lib_deps = ${esp8266.lib_deps}
[env:d1_mini]
board = d1_mini
platform = ${esp8266.platform_wled_default}
platform_packages = ${esp8266.platform_packages}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
upload_speed = 921600
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
@@ -240,8 +235,8 @@ monitor_filters = esp8266_exception_decoder
[env:heltec_wifi_kit_8]
board = d1_mini
platform = ${esp8266.platform_wled_default}
platform_packages = ${esp8266.platform_packages}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags}
@@ -249,8 +244,8 @@ lib_deps = ${esp8266.lib_deps}
[env:h803wf]
board = d1_mini
platform = ${esp8266.platform_wled_default}
platform_packages = ${esp8266.platform_packages}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=1 -D WLED_DISABLE_INFRARED
@@ -300,8 +295,8 @@ board_build.arduino.memory_type = qio_qspi ;; use with PSRAM: 2MB or 4MB
[env:esp8285_4CH_MagicHome]
board = esp8285
platform = ${esp8266.platform_wled_default}
platform_packages = ${esp8266.platform_packages}
platform = ${common.platform_wled_default}
platform_packages = ${common.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
@@ -309,8 +304,8 @@ lib_deps = ${esp8266.lib_deps}
[env:esp8285_H801]
board = esp8285
platform = ${esp8266.platform_wled_default}
platform_packages = ${esp8266.platform_packages}
platform = ${common.platform_wled_default}
platform_packages = ${common.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
@@ -318,8 +313,8 @@ lib_deps = ${esp8266.lib_deps}
[env:d1_mini_5CH_Shojo_PCB]
board = d1_mini
platform = ${esp8266.platform_wled_default}
platform_packages = ${esp8266.platform_packages}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_USE_SHOJO_PCB ;; NB: WLED_USE_SHOJO_PCB is not used anywhere in the source code. Not sure why its needed.
@@ -328,8 +323,8 @@ lib_deps = ${esp8266.lib_deps}
[env:d1_mini_debug]
board = d1_mini
build_type = debug
platform = ${esp8266.platform_wled_default}
platform_packages = ${esp8266.platform_packages}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} ${common.debug_flags}
@@ -340,8 +335,8 @@ board = d1_mini
upload_protocol = espota
# exchange for your WLED IP
upload_port = "10.10.1.27"
platform = ${esp8266.platform_wled_default}
platform_packages = ${esp8266.platform_packages}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags}
@@ -349,8 +344,8 @@ lib_deps = ${esp8266.lib_deps}
[env:anavi_miracle_controller]
board = d1_mini
platform = ${esp8266.platform_wled_default}
platform_packages = ${esp8266.platform_packages}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=12 -D IRPIN=-1 -D RLYPIN=2
@@ -380,7 +375,6 @@ 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
@@ -388,8 +382,12 @@ 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]
@@ -415,22 +413,22 @@ build_flags = ${common.build_flags} ${esp32.build_flags} -D DATA_PINS=27 -D BTNP
[env:sp501e]
board = esp_wroom_02
platform = ${esp8266.platform_wled_default}
platform = ${common.platform_wled_default}
board_build.ldscript = ${common.ldscript_2m512k}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=3 -D BTNPIN=1
lib_deps = ${esp8266.lib_deps}
[env:sp511e]
board = esp_wroom_02
platform = ${esp8266.platform_wled_default}
platform = ${common.platform_wled_default}
board_build.ldscript = ${common.ldscript_2m512k}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=3 -D BTNPIN=2 -D IRPIN=5 -D WLED_MAX_BUTTONS=3
lib_deps = ${esp8266.lib_deps}
[env:Athom_RGBCW] ;7w and 5w(GU10) bulbs
board = esp8285
platform = ${esp8266.platform_wled_default}
platform_packages = ${esp8266.platform_packages}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,13,5
@@ -439,8 +437,8 @@ lib_deps = ${esp8266.lib_deps}
[env:Athom_15w_RGBCW] ;15w bulb
board = esp8285
platform = ${esp8266.platform_wled_default}
platform_packages = ${esp8266.platform_packages}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,5,13
@@ -449,8 +447,8 @@ lib_deps = ${esp8266.lib_deps}
[env:Athom_3Pin_Controller] ;small controller with only data
board = esp8285
platform = ${esp8266.platform_wled_default}
platform_packages = ${esp8266.platform_packages}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=0 -D RLYPIN=-1 -D DATA_PINS=1 -D WLED_DISABLE_INFRARED
@@ -458,8 +456,8 @@ lib_deps = ${esp8266.lib_deps}
[env:Athom_4Pin_Controller] ; With clock and data interface
board = esp8285
platform = ${esp8266.platform_wled_default}
platform_packages = ${esp8266.platform_packages}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=0 -D RLYPIN=12 -D DATA_PINS=1 -D WLED_DISABLE_INFRARED
@@ -467,8 +465,8 @@ lib_deps = ${esp8266.lib_deps}
[env:Athom_5Pin_Controller] ;Analog light strip controller
board = esp8285
platform = ${esp8266.platform_wled_default}
platform_packages = ${esp8266.platform_packages}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=0 -D RLYPIN=-1 DATA_PINS=4,12,14,13 -D WLED_DISABLE_INFRARED
@@ -476,12 +474,11 @@ lib_deps = ${esp8266.lib_deps}
[env:MY9291]
board = esp01_1m
platform = ${esp8266.platform_wled_default}
platform_packages = ${esp8266.platform_packages}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags}
custom_usermods = ${env:esp01_1m_full.custom_usermods} MY9291
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_DISABLE_OTA
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_DISABLE_OTA -D USERMOD_MY9291
lib_deps = ${esp8266.lib_deps}
# ------------------------------------------------------------------------------
@@ -491,8 +488,8 @@ lib_deps = ${esp8266.lib_deps}
[env:codm-controller-0_6]
board = esp_wroom_02
platform = ${esp8266.platform_wled_default}
platform_packages = ${esp8266.platform_packages}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags}
@@ -500,8 +497,8 @@ lib_deps = ${esp8266.lib_deps}
[env:codm-controller-0_6-rev2]
board = esp_wroom_02
platform = ${esp8266.platform_wled_default}
platform_packages = ${esp8266.platform_packages}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags}
+1 -1
View File
@@ -38,7 +38,7 @@ pyelftools==0.32
# via platformio
pyserial==3.5
# via platformio
requests==2.33.0
requests==2.32.4
# via platformio
semantic-version==2.10.0
# via platformio
+6 -12
View File
@@ -119,7 +119,7 @@ describe('Script', () => {
async function checkIfFileWasNewlyCreated(file) {
const modifiedTime = fs.statSync(file).mtimeMs;
assert(Date.now() - modifiedTime < 850, file + ' was not modified');
assert(Date.now() - modifiedTime < 500, file + ' was not modified');
}
async function testFileModification(sourceFilePath, resultFile) {
@@ -129,7 +129,7 @@ describe('Script', () => {
// modify file
fs.appendFileSync(sourceFilePath, ' ');
// delay for 1 second to ensure the modified time is different
await new Promise(resolve => setTimeout(resolve, 1400));
await new Promise(resolve => setTimeout(resolve, 1000));
// run script cdata.js again and wait for it to finish
await execPromise('node tools/cdata.js');
@@ -175,19 +175,13 @@ describe('Script', () => {
});
it('a settings file changes', async () => {
await testFileModification(path.join(dataPath, 'settings_leds.htm'), 'html_settings.h');
await testFileModification(path.join(dataPath, 'settings_leds.htm'), 'html_ui.h');
});
it('common.js changes', async () => {
await testFileModification(path.join(dataPath, 'common.js'), 'html_settings.h');
it('the favicon changes', async () => {
await testFileModification(path.join(dataPath, 'favicon.ico'), 'html_ui.h');
});
// this testcase currently fails - might be due to npm updates (maybe "faking" a favicon.ico change is harder now), or a real regression
// see https://github.com/wled/WLED/issues/5581
// it('the favicon changes', async () => {
// await testFileModification(path.join(dataPath, 'favicon.ico'), 'html_other.h');
// });
it('cdata.js changes', async () => {
await testFileModification('tools/cdata.js', 'html_ui.h');
});
@@ -215,4 +209,4 @@ describe('Script', () => {
assert(secondRunTime < firstRunTime / 2, 'html_*.h files were rebuilt');
});
});
});
});
+2 -10
View File
@@ -141,16 +141,8 @@ discover_devices() {
exit 1
fi
# Map avahi responses to strings separated by 0x1F (unit separator), deduplicated
mapfile -t raw_devices < <(
avahi-browse _wled._tcp --terminate -r -p |
awk -F';' '
/^=/ {
key = $7 "\x1F" $8 "\x1F" $9
if (!seen[key]++) print key
}
'
)
# Map avahi responses to strings seperated by 0x1F (unit separator)
mapfile -t raw_devices < <(avahi-browse _wled._tcp --terminate -r -p | awk -F';' '/^=/ {print $7"\x1F"$8"\x1F"$9}')
local devices_array=()
for device in "${raw_devices[@]}"; do
+11 -12
View File
@@ -28,19 +28,18 @@ class ADS1115Usermod : public Usermod {
}
void loop() {
if (!isEnabled || strip.isUpdating() || millis() - lastTime <= loopInterval)
return;
if (isEnabled && millis() - lastTime > loopInterval) {
lastTime = millis();
lastTime = millis();
// If we don't have new data, skip this iteration.
if (!ads.conversionComplete()) {
return;
}
// If we don't have new data, skip this iteration.
if (!ads.conversionComplete()) {
return;
updateResult();
moveToNextChannel();
startReading();
}
updateResult();
moveToNextChannel();
startReading();
}
void addToJsonInfo(JsonObject& root)
@@ -70,8 +69,6 @@ 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);
@@ -82,6 +79,8 @@ 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.17.4",
"Adafruit ADS1X15": "https://github.com/adafruit/Adafruit_ADS1X15#2.6.2"
"Adafruit BusIO": "https://github.com/adafruit/Adafruit_BusIO#1.13.2",
"Adafruit ADS1X15": "https://github.com/adafruit/Adafruit_ADS1X15#2.4.0"
}
}
+5 -24
View File
@@ -1,29 +1,10 @@
# ADS1115 Usermod
# ADS1115 16-Bit ADC with four inputs
Reads values from an ADS1115 16-bit ADC and exposes them in the `Info` tab.
This usermod will read from an ADS1115 ADC. The voltages are displayed in the Info section of the web UI.
## 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.
Configuration is performed via the Usermod menu. There are no parameters to set in code!
## Compatibility
- Requires an ADS1115 module connected via I2C.
- Works on targets with I2C support.
- Default ADC gain is `1x` (input range `+/-4.096V`).
## Installation
## Installation
- Add `ADS1115` to `custom_usermods` in your `platformio.ini` (or `platformio_override.ini`).
## Author
- Dima Zhemkov [@dima-zhemkov](https://github.com/dima-zhemkov)
Add 'ADS1115' to `custom_usermods` in your platformio environment.
@@ -25,8 +25,6 @@ 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;
@@ -93,8 +91,6 @@ 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
@@ -160,12 +156,6 @@ 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;
@@ -174,15 +164,15 @@ class Animated_Staircase : public Usermod {
bottomSensorRead = bottomSensorWrite ||
(!useUSSensorBottom ?
(bottomPIRorTriggerPin<0 ? false : readPIRPin(bottomPIRorTriggerPin, bottomAPinInvert)) :
(bottomPIRorTriggerPin<0 ? false : digitalRead(bottomPIRorTriggerPin)) :
ultrasoundRead(bottomPIRorTriggerPin, bottomEchoPin, bottomMaxDist*59) // cm to us
);
topSensorRead = topSensorWrite ||
(!useUSSensorTop ?
(topPIRorTriggerPin<0 ? false : readPIRPin(topPIRorTriggerPin, topAPinInvert)) :
(topPIRorTriggerPin<0 ? false : digitalRead(topPIRorTriggerPin)) :
ultrasoundRead(topPIRorTriggerPin, topEchoPin, topMaxDist*59) // cm to us
);
if (bottomSensorRead != bottomSensorState) {
bottomSensorState = bottomSensorRead; // change previous state
sensorChanged = true;
@@ -449,20 +439,18 @@ 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(_topPIRorTrigger_pin_invert)] = topAPinInvert;
staircase[FPSTR(_bottomPIRorTrigger_pin_invert)] = bottomAPinInvert;
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;
DEBUG_PRINTLN(F("Staircase config saved."));
}
@@ -474,13 +462,11 @@ 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));
@@ -499,12 +485,10 @@ 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)
@@ -570,15 +554,14 @@ 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);
+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() && (millis() - lastTime < 200))) return; // adjust "200" (in millisecond) to your needs - prevents starvation with very long strips
if (!enabled || strip.isUpdating()) return;
// 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 is defined in wled.h)
userVar0 = usermod["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value
}
// 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,5 +1,4 @@
{
"name": "Fix_unreachable_netservices_v2",
"build": { "libArchive": false },
"platforms": ["espressif8266"]
}
@@ -77,7 +77,7 @@ public:
}
if (m_updateConfig)
{
serializeConfigToFS();
serializeConfig();
m_updateConfig = false;
}
}
+35 -114
View File
@@ -1,38 +1,14 @@
#include "wled.h"
#include <INA226_WE.h>
#ifndef INA226_ADDRESS
#define INA226_ADDRESS 0x40 // Default I2C address for INA226
#endif
#ifndef INA226_CHECK_INTERVAL_MS
#define INA226_CHECK_INTERVAL_MS 60000 // Default check interval in milliseconds
#endif
#define DEFAULT_CHECKINTERVAL INA226_CHECK_INTERVAL_MS
#define DEFAULT_CHECKINTERVAL 60000
#define DEFAULT_INASAMPLES 128
#define DEFAULT_INASAMPLESENUM AVERAGE_128
#define DEFAULT_INACONVERSIONTIME 1100
#define DEFAULT_INACONVERSIONTIMEENUM CONV_TIME_1100
// Compile-time defaults for shunt resistor (micro-ohms), current range (mA), and current offset (mA)
// These can be overridden via -D flags in platformio.ini / platformio_override.ini
#ifndef INA226_SHUNT_MICRO_OHMS
#define INA226_SHUNT_MICRO_OHMS 1000000 // 1 Ohm = 1,000,000 μΩ
#endif
#ifndef INA226_DEFAULT_CURRENT_RANGE
#define INA226_DEFAULT_CURRENT_RANGE 1000 // 1000 mA = 1 A
#endif
#ifndef INA226_CURRENT_OFFSET_MA
#define INA226_CURRENT_OFFSET_MA 0 // No offset by default
#endif
#ifndef INA226_ENABLED_DEFAULT
#define INA226_ENABLED_DEFAULT false
#endif
// A packed version of all INA settings enums and their human friendly counterparts packed into a 32 bit structure
// Some values are shifted and need to be preprocessed before usage
struct InaSettingLookup
@@ -105,11 +81,10 @@ private:
uint16_t _settingInaSamples : 11; // Number of samples for averaging, max 1024
uint8_t _i2cAddress;
uint32_t _checkIntervalMs; // milliseconds, user settings is in seconds
float _decimalFactor; // a power of 10 factor. 1 would be no change, 10 is one decimal, 100 is two etc. User sees a power of 10 (0, 1, 2, ..)
uint32_t _shuntResistorUOhm; // Shunt resistor value in micro-ohms (μΩ)
uint16_t _currentRangeMa; // Expected maximum current in milliamps
int16_t _currentOffsetMa; // Current offset in milliamps, subtracted from readings
uint16_t _checkInterval; // milliseconds, user settings is in seconds
float _decimalFactor; // a power of 10 factor. 1 would be no change, 10 is one decimal, 100 is two etc. User sees a power of 10 (0, 1, 2, ..)
uint16_t _shuntResistor; // Shunt resistor value in milliohms
uint16_t _currentRange; // Expected maximum current in milliamps
uint8_t _lastStatus = 0;
float _lastCurrent = 0;
@@ -118,7 +93,7 @@ private:
float _lastShuntVoltage = 0;
bool _lastOverflow = false;
#ifndef WLED_DISABLE_MQTT
#ifndef WLED_MQTT_DISABLE
float _lastCurrentSent = 0;
float _lastVoltageSent = 0;
float _lastPowerSent = 0;
@@ -143,11 +118,9 @@ private:
_ina226 = new INA226_WE(_i2cAddress);
if (!_ina226->init())
{
DEBUG_PRINTLN(F("INA226: init failed!"));
DEBUG_PRINTLN(F("INA226 initialization failed!"));
return;
}
DEBUG_PRINTF_P(PSTR("INA226: addr=0x%02X shunt=%luμΩ range=%umA offset=%dmA\n"),
_i2cAddress, _shuntResistorUOhm, _currentRangeMa, _currentOffsetMa);
_ina226->setCorrectionFactor(1.0);
uint16_t tmpShort = _settingInaSamples;
@@ -156,7 +129,7 @@ private:
tmpShort = _settingInaConversionTimeUs << 2;
_ina226->setConversionTime(getConversionTimeEnum(tmpShort));
if (_checkIntervalMs >= 20000)
if (_checkInterval >= 20000)
{
_isTriggeredOperationMode = true;
_ina226->setMeasureMode(TRIGGERED);
@@ -167,11 +140,7 @@ private:
_ina226->setMeasureMode(CONTINUOUS);
}
_ina226->setResistorRange(static_cast<float>(_shuntResistorUOhm) / 1000000.0f, static_cast<float>(_currentRangeMa) / 1000.0f);
DEBUG_PRINTF_P(PSTR("INA226: mode=%s interval=%lums samples=%u convTime=%uμs\n"),
_isTriggeredOperationMode ? "triggered" : "continuous",
_checkIntervalMs, _settingInaSamples, _settingInaConversionTimeUs << 2);
_ina226->setResistorRange(static_cast<float>(_shuntResistor) / 1000.0, static_cast<float>(_currentRange) / 1000.0);
}
void fetchAndPushValues()
@@ -181,19 +150,17 @@ private:
if (_lastStatus != 0)
return;
float current = truncateDecimals((_ina226->getCurrent_mA() - _currentOffsetMa) / 1000.0f);
float current = truncateDecimals(_ina226->getCurrent_mA() / 1000.0);
float voltage = truncateDecimals(_ina226->getBusVoltage_V());
float power = truncateDecimals(_ina226->getBusPower() / 1000.0f);
float shuntVoltage = truncateDecimals(_ina226->getShuntVoltage_mV());
float power = truncateDecimals(_ina226->getBusPower() / 1000.0);
float shuntVoltage = truncateDecimals(_ina226->getShuntVoltage_V());
bool overflow = _ina226->overflow;
#ifndef WLED_DISABLE_MQTT
mqttPublishIfChanged(F("current"), _lastCurrentSent, current, 0.01f);
mqttPublishIfChanged(F("voltage"), _lastVoltageSent, voltage, 0.01f);
mqttPublishIfChanged(F("power"), _lastPowerSent, power, 0.1f);
// Publish in V for backward compatibility
float shuntVoltageV = shuntVoltage / 1000.0f;
mqttPublishIfChanged(F("shunt_voltage"), _lastShuntVoltageSent, shuntVoltageV, 0.01f);
mqttPublishIfChanged(F("shunt_voltage"), _lastShuntVoltageSent, shuntVoltage, 0.01f);
mqttPublishIfChanged(F("overflow"), _lastOverflowSent, overflow);
#endif
@@ -202,9 +169,6 @@ private:
_lastPower = power;
_lastShuntVoltage = shuntVoltage;
_lastOverflow = overflow;
DEBUG_PRINTF_P(PSTR("INA226: %.3fA %.2fV %.2fW shunt=%.2fmV%s\n"),
current, voltage, power, shuntVoltage, overflow ? " OVF" : "");
}
void handleTriggeredMode(unsigned long currentTime)
@@ -224,7 +188,7 @@ private:
}
else
{
if (currentTime - _lastLoopCheck >= _checkIntervalMs)
if (currentTime - _lastLoopCheck >= _checkInterval)
{
// Start a measurement and use isBusy() later to determine when it is done
_ina226->startSingleMeasurementNoWait();
@@ -237,7 +201,7 @@ private:
void handleContinuousMode(unsigned long currentTime)
{
if (currentTime - _lastLoopCheck >= _checkIntervalMs)
if (currentTime - _lastLoopCheck >= _checkInterval)
{
_lastLoopCheck = currentTime;
fetchAndPushValues();
@@ -251,23 +215,19 @@ private:
return;
char topic[128];
auto buildTopic = [&](const char *suffix) {
snprintf_P(topic, sizeof(topic), PSTR("%s/%s"), mqttDeviceTopic, suffix);
};
buildTopic("current");
snprintf_P(topic, 127, "%s/current", mqttDeviceTopic);
mqttCreateHassSensor(F("Current"), topic, F("current"), F("A"));
buildTopic("voltage");
snprintf_P(topic, 127, "%s/voltage", mqttDeviceTopic);
mqttCreateHassSensor(F("Voltage"), topic, F("voltage"), F("V"));
buildTopic("power");
snprintf_P(topic, 127, "%s/power", mqttDeviceTopic);
mqttCreateHassSensor(F("Power"), topic, F("power"), F("W"));
buildTopic("shunt_voltage");
snprintf_P(topic, 127, "%s/shunt_voltage", mqttDeviceTopic);
mqttCreateHassSensor(F("Shunt Voltage"), topic, F("voltage"), F("V"));
buildTopic("overflow");
snprintf_P(topic, 127, "%s/overflow", mqttDeviceTopic);
mqttCreateHassBinarySensor(F("Overflow"), topic);
}
@@ -355,23 +315,14 @@ public:
UsermodINA226()
{
// Default values
_settingEnabled = INA226_ENABLED_DEFAULT;
_settingInaSamples = DEFAULT_INASAMPLES;
_settingInaConversionTimeUs = DEFAULT_INACONVERSIONTIME >> 2; // stored shifted to fit 12-bit field
_settingInaConversionTimeUs = DEFAULT_INACONVERSIONTIME;
_i2cAddress = INA226_ADDRESS;
_checkIntervalMs = DEFAULT_CHECKINTERVAL;
_checkInterval = DEFAULT_CHECKINTERVAL;
_decimalFactor = 100;
_shuntResistorUOhm = INA226_SHUNT_MICRO_OHMS;
_currentRangeMa = INA226_DEFAULT_CURRENT_RANGE;
_currentOffsetMa = INA226_CURRENT_OFFSET_MA;
_mqttPublish = false;
_mqttPublishAlways = false;
_mqttHomeAssistant = false;
_initDone = false;
_isTriggeredOperationMode = false;
_measurementTriggered = false;
_shuntResistor = 1000;
_currentRange = 1000;
}
void setup()
@@ -448,7 +399,7 @@ public:
JsonArray jsonCurrent = user.createNestedArray(F("Current"));
JsonArray jsonVoltage = user.createNestedArray(F("Voltage"));
JsonArray jsonPower = user.createNestedArray(F("Power"));
JsonArray jsonShuntVoltage = user.createNestedArray(F("Shunt Voltage Drop"));
JsonArray jsonShuntVoltage = user.createNestedArray(F("Shunt Voltage"));
JsonArray jsonOverflow = user.createNestedArray(F("Overflow"));
if (_lastLoopCheck == 0)
@@ -481,7 +432,7 @@ public:
jsonPower.add(F("W"));
jsonShuntVoltage.add(_lastShuntVoltage);
jsonShuntVoltage.add(F("mV"));
jsonShuntVoltage.add(F("V"));
jsonOverflow.add(_lastOverflow ? F("true") : F("false"));
}
@@ -491,13 +442,12 @@ public:
JsonObject top = root.createNestedObject(FPSTR(_name));
top[F("Enabled")] = _settingEnabled;
top[F("I2CAddress")] = static_cast<uint8_t>(_i2cAddress);
top[F("CheckInterval")] = _checkIntervalMs / 1000;
top[F("CheckInterval")] = _checkInterval / 1000;
top[F("INASamples")] = _settingInaSamples;
top[F("INAConversionTime")] = _settingInaConversionTimeUs << 2;
top[F("Decimals")] = log10f(_decimalFactor);
top[F("ShuntResistor")] = static_cast<float>(_shuntResistorUOhm) / 1000.0f;
top[F("CurrentRange")] = _currentRangeMa;
top[F("CurrentOffset")] = _currentOffsetMa;
top[F("ShuntResistor")] = _shuntResistor;
top[F("CurrentRange")] = _currentRange;
#ifndef WLED_DISABLE_MQTT
top[F("MqttPublish")] = _mqttPublish;
top[F("MqttPublishAlways")] = _mqttPublishAlways;
@@ -507,17 +457,6 @@ public:
DEBUG_PRINTLN(F("INA226 config saved."));
}
void appendConfigData() override
{
oappend(F("addInfo('INA226:CheckInterval',1,'seconds');"));
oappend(F("addInfo('INA226:INASamples',1,'samples (1-1024)');"));
oappend(F("addInfo('INA226:INAConversionTime',1,'&micro;s');"));
oappend(F("addInfo('INA226:Decimals',1,'(0-5)');"));
oappend(F("addInfo('INA226:ShuntResistor',1,'m&Omega;');"));
oappend(F("addInfo('INA226:CurrentRange',1,'mA');"));
oappend(F("addInfo('INA226:CurrentOffset',1,'mA');"));
}
bool readFromConfig(JsonObject &root) override
{
JsonObject top = root[FPSTR(_name)];
@@ -533,12 +472,12 @@ public:
configComplete = false;
configComplete &= getJsonValue(top[F("I2CAddress")], _i2cAddress);
if (getJsonValue(top[F("CheckInterval")], _checkIntervalMs))
if (getJsonValue(top[F("CheckInterval")], _checkInterval))
{
if (1 <= _checkIntervalMs && _checkIntervalMs <= 600)
_checkIntervalMs *= 1000;
if (1 <= _checkInterval && _checkInterval <= 600)
_checkInterval *= 1000;
else
_checkIntervalMs = DEFAULT_CHECKINTERVAL;
_checkInterval = DEFAULT_CHECKINTERVAL;
}
else
configComplete = false;
@@ -572,26 +511,8 @@ public:
else
configComplete = false;
float shuntMilliOhms;
if (getJsonValue(top[F("ShuntResistor")], shuntMilliOhms))
{
if (shuntMilliOhms > 0)
_shuntResistorUOhm = static_cast<uint32_t>(shuntMilliOhms * 1000.0f + 0.5f);
else
_shuntResistorUOhm = INA226_SHUNT_MICRO_OHMS;
}
else
configComplete = false;
if (getJsonValue(top[F("CurrentRange")], _currentRangeMa))
{
if (_currentRangeMa == 0 || _currentRangeMa > 20000)
_currentRangeMa = INA226_DEFAULT_CURRENT_RANGE;
}
else
configComplete = false;
if (!getJsonValue(top[F("CurrentOffset")], _currentOffsetMa))
_currentOffsetMa = INA226_CURRENT_OFFSET_MA; // Use compile-time default if missing from config
configComplete &= getJsonValue(top[F("ShuntResistor")], _shuntResistor);
configComplete &= getJsonValue(top[F("CurrentRange")], _currentRange);
#ifndef WLED_DISABLE_MQTT
if (getJsonValue(top[F("MqttPublish")], tmpBool))
+1 -31
View File
@@ -18,7 +18,6 @@ The following settings can be configured in the Usermod Menu:
- **Decimals**: Number of decimals in the output.
- **ShuntResistor**: Shunt resistor value in milliohms. An R100 shunt resistor should be written as "100", while R010 should be "10".
- **CurrentRange**: Expected maximum current in milliamps (e.g., 5 A = 5000 mA).
- **CurrentOffset**: Current offset in milliamps, subtracted from raw readings. Useful for compensating a consistent bias in the sensor. Default is 0.
- **MqttPublish**: Enable or disable MQTT publishing.
- **MqttPublishAlways**: Publish always, regardless if there is a change.
- **MqttHomeAssistantDiscovery**: Enable Home Assistant discovery.
@@ -64,33 +63,4 @@ extends = env:esp32dev
custom_usermods = ${env:esp32dev.custom_usermods} INA226
build_flags = ${env:esp32dev.build_flags}
; -D USERMOD_INA226_DEBUG ; -- add a debug status to the info modal
```
### Compile-time Defaults
Several parameters can be overridden at compile time via `-D` build flags. This is useful for setting board-specific defaults so the device works correctly on first boot without manual configuration.
| Build Flag | Default | Unit | Description |
|---|---|---|---|
| `INA226_ADDRESS` | `0x40` | — | I2C address of the INA226 |
| `INA226_SHUNT_MICRO_OHMS` | `1000000` | μΩ | Shunt resistor value (1 000 000 μΩ = 1 Ω) |
| `INA226_DEFAULT_CURRENT_RANGE` | `1000` | mA | Expected maximum current (1000 mA = 1 A) |
| `INA226_CURRENT_OFFSET_MA` | `0` | mA | Current offset subtracted from readings |
| `INA226_CHECK_INTERVAL_MS` | `60000` | ms | Default interval between readings on first boot |
| `INA226_ENABLED_DEFAULT` | `false` | — | Enable the usermod on first boot |
Example for a board with a 2.888 mΩ effective shunt, 10 A range, -118 mA offset, 1 second polling, and enabled by default:
```ini
[env:my_board]
extends = env:esp32dev
custom_usermods = ${env:esp32dev.custom_usermods} INA226
build_flags = ${env:esp32dev.build_flags}
-D INA226_ENABLED_DEFAULT=true
-D INA226_SHUNT_MICRO_OHMS=2888
-D INA226_DEFAULT_CURRENT_RANGE=10000
-D INA226_CURRENT_OFFSET_MA=-118
-D INA226_CHECK_INTERVAL_MS=1000
```
All compile-time defaults can still be changed at runtime through the Usermod settings page.
```
@@ -1,4 +1,4 @@
{
"name": "ST7789_display",
"name:": "ST7789_display",
"build": { "libArchive": false }
}
+30 -31
View File
@@ -296,17 +296,17 @@ void FFTcode(void * parameter)
ArduinoFFT<float> FFT = ArduinoFFT<float>(valFFT, vImag, samplesFFT, SAMPLE_RATE, true);
#elif !defined(UM_AUDIOREACTIVE_USE_INTEGER_FFT)
// allocate and initialize FFT buffers on first call
// note: free() is never used on these pointers. If it ever is implemented, this implementation can cause memory leaks (need to free raw pointers)
if (valFFT == nullptr) {
float* raw_buffer = (float*)heap_caps_malloc((2 * samplesFFT * sizeof(float)) + 16, MALLOC_CAP_8BIT);
if ((raw_buffer == nullptr)) return; // something went wrong
valFFT = (float*)(((uintptr_t)raw_buffer + 15) & ~15); // SIMD requires aligned memory to 16-byte boundary. note in IDF5 there is MALLOC_CAP_SIMD available
valFFT = (float*)heap_caps_aligned_calloc(16, 2 * samplesFFT, sizeof(float), MALLOC_CAP_8BIT); // SIMD requires aligned memory to 16-byte boundary. note in IDF5 there is MALLOC_CAP_SIMD available
if ((valFFT == nullptr)) return; // something went wrong
}
// create window
if (windowFFT == nullptr) {
float* raw_buffer = (float*)heap_caps_malloc((samplesFFT * sizeof(float)) + 16, MALLOC_CAP_8BIT);
if ((raw_buffer == nullptr)) return; // something went wrong
windowFFT = (float*)(((uintptr_t)raw_buffer + 15) & ~15); // SIMD requires aligned memory to 16-byte boundary
windowFFT = (float*)heap_caps_aligned_calloc(16, samplesFFT, sizeof(float), MALLOC_CAP_8BIT); // SIMD requires aligned memory to 16-byte boundary. note in IDF5 there is MALLOC_CAP_SIMD available
if ((windowFFT == nullptr)) {
heap_caps_free(valFFT); valFFT = nullptr;
return; // something went wrong
}
}
if (dsps_fft2r_init_fc32(NULL, samplesFFT) != ESP_OK) return; // initialize FFT tables
// create window function for FFT
@@ -316,16 +316,20 @@ void FFTcode(void * parameter)
dsps_wind_flat_top_f32(windowFFT, samplesFFT);
#endif
#else
// allocate and initialize integer FFT buffers on first call
if (valFFT == nullptr) valFFT = (int16_t*) calloc(sizeof(int16_t), samplesFFT * 2);
if ((valFFT == nullptr)) return; // something went wrong
// use integer FFT - allocate and initialize integer FFT buffers on first call, 4 bytes aligned (just in case, even if not strictly needed for int16_t)
if (valFFT == nullptr) valFFT = (int16_t*) heap_caps_aligned_calloc(4, samplesFFT * 2, sizeof(int16_t), MALLOC_CAP_8BIT);
// create window
if (windowFFT == nullptr) windowFFT = (int16_t*) calloc(sizeof(int16_t), samplesFFT);
if ((windowFFT == nullptr)) return; // something went wrong
if (dsps_fft2r_init_sc16(NULL, samplesFFT) != ESP_OK) return; // initialize FFT tables
if (windowFFT == nullptr) windowFFT = (int16_t*) heap_caps_aligned_calloc(4, samplesFFT, sizeof(int16_t), MALLOC_CAP_8BIT);
// create window function for FFT
float *windowFloat = (float*) calloc(sizeof(float), samplesFFT); // temporary buffer for window function
if ((windowFloat == nullptr)) return; // something went wrong
float *windowFloat = (float*) heap_caps_aligned_calloc(4, samplesFFT, sizeof(float), MALLOC_CAP_8BIT); // temporary buffer for window function
if (windowFloat == nullptr || windowFFT == nullptr || valFFT == nullptr) { // something went wrong
if (windowFloat) heap_caps_free(windowFloat);
if (windowFFT) heap_caps_free(windowFFT); windowFFT = nullptr;
if (valFFT) heap_caps_free(valFFT); valFFT = nullptr;
return;
}
if (dsps_fft2r_init_sc16(NULL, samplesFFT) != ESP_OK) return; // initialize FFT tables
#ifdef FFT_PREFER_EXACT_PEAKS
dsps_wind_blackman_harris_f32(windowFloat, samplesFFT);
#else
@@ -335,7 +339,7 @@ void FFTcode(void * parameter)
for (int i = 0; i < samplesFFT; i++) {
windowFFT[i] = (int16_t)(windowFloat[i] * 32767.0f);
}
free(windowFloat); // free temporary buffer
heap_caps_free(windowFloat); // free temporary buffer
#endif
// see https://www.freertos.org/vtaskdelayuntil.html
@@ -468,7 +472,6 @@ void FFTcode(void * parameter)
}
FFT_Magnitude = FFT_Magnitude_int * 512; // scale to match raw float value
FFT_MajorPeak = FFT_MajorPeak_int;
FFT_Magnitude = FFT_Magnitude_int;
#endif
#endif
FFT_MajorPeak = constrain(FFT_MajorPeak, 1.0f, 11025.0f); // restrict value to range expected by effects
@@ -1300,12 +1303,7 @@ class AudioReactive : public Usermod {
size_t packetSize = fftUdp.parsePacket();
#ifdef ARDUINO_ARCH_ESP32
if ((packetSize > 0) && ((packetSize < 5) || (packetSize > UDPSOUND_MAX_PACKET)))
#if ESP_IDF_VERSION_MAJOR < 5
fftUdp.flush(); // discard invalid packets (too small or too big) - only works on esp32
#else
fftUdp.clear(); // function was renamed in newer frameworks
#endif
if ((packetSize > 0) && ((packetSize < 5) || (packetSize > UDPSOUND_MAX_PACKET))) fftUdp.flush(); // discard invalid packets (too small or too big) - only works on esp32
#endif
if ((packetSize > 5) && (packetSize <= UDPSOUND_MAX_PACKET)) {
//DEBUGSR_PRINTLN("Received UDP Sync Packet");
@@ -1538,9 +1536,14 @@ class AudioReactive : public Usermod {
// We cannot wait indefinitely before processing audio data
if (strip.isUpdating() && (millis() - lastUMRun < 2)) return; // be nice, but not too nice
// suspend local sound processing when "real time mode" is active (E131, UDP, ADALIGHT, ARTNET, DDP, DMX)
// exception: sound input is still needed when useMainSegmentOnly - other segments are still running with local input.
if (realtimeMode && !realtimeOverride && !useMainSegmentOnly) {
// suspend local sound processing when "real time mode" is active (E131, UDP, ADALIGHT, ARTNET)
if ( (realtimeOverride == REALTIME_OVERRIDE_NONE) // please add other overrides here if needed
&&( (realtimeMode == REALTIME_MODE_GENERIC)
||(realtimeMode == REALTIME_MODE_E131)
||(realtimeMode == REALTIME_MODE_UDP)
||(realtimeMode == REALTIME_MODE_ADALIGHT)
||(realtimeMode == REALTIME_MODE_ARTNET) ) ) // please add other modes here if needed
{
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_DEBUG)
if ((disableSoundProcessing == false) && (audioSyncEnabled == 0)) { // we just switched to "disabled"
DEBUG_PRINTLN(F("[AR userLoop] realtime mode active - audio processing suspended."));
@@ -1617,11 +1620,7 @@ class AudioReactive : public Usermod {
have_new_sample = receiveAudioData();
if (have_new_sample) last_UDPTime = millis();
#ifdef ARDUINO_ARCH_ESP32
#if ESP_IDF_VERSION_MAJOR < 5
else fftUdp.flush(); // Flush udp input buffers if we haven't read it - avoids hickups in receive mode. Does not work on 8266.
#else
else fftUdp.clear(); // function was renamed in newer frameworks
#endif
#endif
lastTime = millis();
}
@@ -1726,7 +1725,7 @@ class AudioReactive : public Usermod {
);
}
micDataReal = 0.0f; // just to be sure
if (enabled) disableSoundProcessing = false; // allows FFT_Task to run at least once, even when loop() might disable again
if (enabled) disableSoundProcessing = false;
updateIsRunning = init;
}
+1 -1
View File
@@ -1,5 +1,5 @@
{
"name": "pov_display",
"name:": "pov_display",
"build": { "libArchive": false},
"platforms": ["espressif32"]
}
+12 -11
View File
@@ -2345,7 +2345,7 @@ void mode_colortwinkle() {
unsigned index = i >> 3;
unsigned bitNum = i & 0x07;
bitWrite(SEGENV.data[index], bitNum, true);
SEGMENT.setPixelColor(i, ColorFromPalette(SEGPALETTE, hw_random8(), gamma8inv(64), NOBLEND)); // note on gamma8inv: inverting results in non-linear brightness fade as originally designed
SEGMENT.setPixelColor(i, ColorFromPalette(SEGPALETTE, hw_random8(), 64, NOBLEND));
break; //only spawn 1 new pixel per frame per 50 LEDs
}
}
@@ -2616,7 +2616,7 @@ static CRGBW twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat)
unsigned hue = slowcycle8 - salt;
CRGBW c;
if (bright > 0) {
c = ColorFromPalette(SEGPALETTE, hue, gamma8inv(bright), NOBLEND); // note on gamma8inv: inverting results in non-linear brightness fade as originally designed
c = ColorFromPalette(SEGPALETTE, hue, bright, NOBLEND);
if (!SEGMENT.check1) {
// This code takes a pixel, and if its in the 'fading down'
// part of the cycle, it adjusts the color a little bit like the
@@ -2651,18 +2651,19 @@ static void twinklefox_base(bool cat)
if (SEGMENT.speed > 100) SEGENV.aux0 = 3 + ((255 - SEGMENT.speed) >> 3);
else SEGENV.aux0 = 22 + ((100 - SEGMENT.speed) >> 1);
// Set up the background color, "bg". Note: using gamma invert for brightness as the FX was written without any gamma correction, it will dim down too much now
// Set up the background color, "bg".
CRGBW bg = SEGCOLOR(1);
unsigned bglight = bg.getRGBaverage();
unsigned bglight = bg.getAverageLight();
if (bglight > 64) {
bg = color_fade(bg, gamma8inv(16), true); // very bright, so scale to 1/16th
bg = color_fade(bg, 16, true); // very bright, so scale to 1/16th
} else if (bglight > 16) {
bg = color_fade(bg, gamma8inv(64), true); // not that bright, so scale to 1/4
bg = color_fade(bg, 64, true); // not that bright, so scale to 1/4th
} else {
bg = color_fade(bg, gamma8inv(86), true); // dim, scale to 1/3rd
bg = color_fade(bg, 86, true); // dim, scale to 1/3rd.
}
bg = gamma32inv(bg); // need to invert gamma as the FX was written without any gamma correction and it will dim down too much otherwise
bglight = bg.getRGBaverage(); // update after scaling
unsigned backgroundBrightness = bg.getAverageLight();
for (unsigned i = 0; i < SEGLEN; i++) {
@@ -2679,8 +2680,8 @@ static void twinklefox_base(bool cat)
// on the "brightness = f( time )" idea.
CRGBW c = twinklefox_one_twinkle(myclock30, myunique8, cat);
unsigned cbright = c.getRGBaverage();
int deltabright = cbright - bglight;
unsigned cbright = c.getAverageLight();
int deltabright = cbright - backgroundBrightness;
if (deltabright >= 32 || (bg==0)) {
// If the new pixel is significantly brighter than the background color,
// use the new color.
@@ -6836,7 +6837,7 @@ void mode_gravcenter_base(unsigned mode) {
uint8_t gravity = 8 - SEGMENT.speed/32;
int offset = 1;
if(mode == 2) offset = 0; // Gravimeter
if (tempsamp >= gravcen->topLED + offset) gravcen->topLED = tempsamp-offset;
if (tempsamp >= gravcen->topLED) gravcen->topLED = tempsamp-offset;
else if (gravcen->gravityCounter % gravity == 0) gravcen->topLED--;
if(mode == 1) { //Gravcentric
+7 -64
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, true, 0); // color_wheel is a continuous (moving) wheel, so wrap end->start (restores pre-0.16 behaviour)
if (palette) return color_from_palette(pos, false, false, 0); // only wrap if "always wrap" is set
uint8_t w = W(getCurrentColor(0));
CRGBW rgb;
rgb = CHSV32(static_cast<uint16_t>(pos << 8), 255, 255);
@@ -1382,7 +1382,6 @@ static uint8_t _dummy (uint8_t a, uint8_t b) { return a; } // dummy (same as
void WS2812FX::blendSegment(const Segment &topSegment) const {
typedef uint8_t(*FuncType)(uint8_t, uint8_t);
// function pointer array: fill with _dummy if using special case: avoid OOB access and always provide a valid path
// note: making the function array static const uses more ram and comes at no significant speed gain
FuncType funcs[] = {
_dummy, _dummy, _dummy, _subtract,
_difference, _average, _dummy, _divide,
@@ -1414,71 +1413,14 @@ void WS2812FX::blendSegment(const Segment &topSegment) const {
const size_t matrixSize = Segment::maxWidth * Segment::maxHeight;
const size_t startIndx = XY(topSegment.start, topSegment.startY);
const size_t stopIndx = startIndx + length;
const unsigned progress = topSegment.progress();
const unsigned progInv = 0xFFFFU - progress;
uint8_t opacity = topSegment.currentBri(); // returns transitioned opacity for style FADE
uint8_t cct = topSegment.currentCCT();
if (gammaCorrectCol) opacity = gamma8inv(opacity); // use inverse gamma on brightness for correct color scaling after gamma correction (see #5343 for details)
const Segment *segO = topSegment.getOldSegment();
const bool hasGrouping = topSegment.groupLength() != 1;
Segment::setClippingRect(0, 0); // disable clipping by default
// fast path: handle the default case - no transitions, no grouping/spacing, no mirroring, no CCT
if (!segO && blendingStyle == TRANSITION_FADE && !hasGrouping && !topSegment.mirror && !topSegment.mirror_y) {
if (isMatrix && stopIndx <= matrixSize && !_pixelCCT) {
#ifndef WLED_DISABLE_2D
// Calculate pointer steps to avoid 'if' and 'XY()' inside loops
int x_inc = 1;
int y_inc = Segment::maxWidth;
int start_offset = XY(topSegment.start, topSegment.startY);
// adjust starting position and steps based on Reverse/Transpose
// note: transpose is handled in separate loop so it is still fast and no branching is needed in default path
if (!topSegment.transpose) {
if (topSegment.reverse) { start_offset += (width - 1); x_inc = -1; }
if (topSegment.reverse_y) { start_offset += (height - 1) * Segment::maxWidth; y_inc = -Segment::maxWidth; }
for (int y = 0; y < height; y++) {
uint32_t* pRow = &_pixels[start_offset + y * y_inc];
const int y_width = y * width;
for (int x = 0; x < width; x++) {
uint32_t* p = pRow + x * x_inc;
uint32_t c_a = topSegment.getPixelColorRaw(x + y_width);
*p = color_blend(*p, segblend(c_a, *p), opacity);
}
}
} else { // transposed
for (int y = 0; y < height; y++) {
const int px = topSegment.reverse ? (height - y - 1) : y; // source pixel: swap y into x, reverse if needed
for (int x = 0; x < width; x++) {
const int py = topSegment.reverse_y ? (width - x - 1) : x; // source pixel: swap x into y, reverse if needed
const uint32_t c_a = topSegment.getPixelColorRaw(px + py * height); // height = virtual width
const size_t idx = XY(topSegment.start + x, topSegment.startY + y); // write logical (non swapped) pixel coordinate
_pixels[idx] = color_blend(_pixels[idx], segblend(c_a, _pixels[idx]), opacity);
}
}
}
return;
#endif
} else if (!isMatrix) {
// 1D fast path, include CCT as it is more common on 1D setups
uint32_t* strip = _pixels;
int start = topSegment.start;
int off = topSegment.offset;
for (int i = 0; i < length; i++) {
uint32_t c_a = topSegment.getPixelColorRaw(i);
int p = topSegment.reverse ? (length - i - 1) : i;
int idx = start + p + off;
if (idx >= topSegment.stop) idx -= length;
strip[idx] = color_blend(strip[idx], segblend(c_a, strip[idx]), opacity);
if (_pixelCCT) _pixelCCT[idx] = cct;
}
return;
}
}
// slow path: handle transitions, grouping/spacing, segments with clipping and CCT pixels
Segment::setClippingRect(0, 0); // disable clipping by default
const unsigned progress = topSegment.progress();
const unsigned progInv = 0xFFFFU - progress;
const unsigned dw = (blendingStyle==TRANSITION_OUTSIDE_IN ? progInv : progress) * width / 0xFFFFU + 1;
const unsigned dh = (blendingStyle==TRANSITION_OUTSIDE_IN ? progInv : progress) * height / 0xFFFFU + 1;
const unsigned orgBS = blendingStyle;
@@ -1539,6 +1481,7 @@ void WS2812FX::blendSegment(const Segment &topSegment) const {
#ifndef WLED_DISABLE_2D
const int nCols = topSegment.virtualWidth();
const int nRows = topSegment.virtualHeight();
const Segment *segO = topSegment.getOldSegment();
const int oCols = segO ? segO->virtualWidth() : nCols;
const int oRows = segO ? segO->virtualHeight() : nRows;
@@ -1634,8 +1577,8 @@ void WS2812FX::blendSegment(const Segment &topSegment) const {
}
#endif
} else {
// 1D Slow Path
const int nLen = topSegment.virtualLength();
const Segment *segO = topSegment.getOldSegment();
const int oLen = segO ? segO->virtualLength() : nLen;
const auto setMirroredPixel = [&](int i, uint32_t c, uint8_t o) {
@@ -1715,7 +1658,7 @@ void WS2812FX::show() {
if (realtimeMode == REALTIME_MODE_INACTIVE || useMainSegmentOnly || realtimeOverride > REALTIME_OVERRIDE_NONE) {
// clear frame buffer
memset(_pixels, 0, sizeof(uint32_t) * totalLen);
for (size_t i = 0; i < totalLen; i++) _pixels[i] = BLACK; // memset(_pixels, 0, sizeof(uint32_t) * getLengthTotal());
// blend all segments into (cleared) buffer
for (Segment &seg : _segments) if (seg.isActive() && (seg.on || seg.isInTransition())) {
blendSegment(seg); // blend segment's buffer into frame buffer
+20 -38
View File
@@ -802,11 +802,10 @@ BusHub75Matrix::BusHub75Matrix(const BusConfig &bc) : Bus(bc.type, bc.start, bc.
_hasRgb = true;
_hasWhite = false;
virtualDisp = nullptr; // todo: this should be solved properly, can cause memory leak (if omitted here, nothing seems to work)
_isVirtual = false;
// aliases for easier reading
unsigned panelWidth = bc.pins[0];
unsigned panelHeight = bc.pins[1];
unsigned chainLength = bc.pins[2];
uint8_t panelWidth = bc.pins[0];
uint8_t panelHeight = bc.pins[1];
uint8_t chainLength = bc.pins[2];
_rows = bc.pins[3];
_cols = bc.pins[4];
@@ -814,7 +813,6 @@ 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
@@ -823,26 +821,20 @@ BusHub75Matrix::BusHub75Matrix(const BusConfig &bc) : Bus(bc.type, bc.start, bc.
mxconfig.clkphase = bc.reversed;
// allow chain length up to 4, limit to prevent bad data from preventing boot due to low memory
mxconfig.chain_length = max(1U, min(chainLength, 4U));
mxconfig.chain_length = max((uint8_t) 1, min(chainLength, (uint8_t) 4));
if (mxconfig.mx_height >= 64 && (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;
}
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) {
mxconfig.mx_width = min(128U, panelWidth); // UI limit is 128
mxconfig.mx_height = min(64U, panelHeight);
mxconfig.mx_width = min((uint8_t) 64, panelWidth); // TODO: UI limit is 128, this limits to 64
mxconfig.mx_height = min((uint8_t) 64, panelHeight);
} else if (bc.type == TYPE_HUB75MATRIX_QS) {
_isVirtual = true;
mxconfig.mx_width = min(128U, panelWidth) * 2;
mxconfig.mx_height = min(64U, panelHeight) / 2;
mxconfig.driver = HUB75_I2S_CFG::FM6124; // use FM6124 for "outdoor" 4-scan panels - workaround until we can make the driver user-configurable
mxconfig.mx_width = min((uint8_t) 64, panelWidth) * 2;
mxconfig.mx_height = min((uint8_t) 64, panelHeight) / 2;
} else {
DEBUGBUS_PRINTLN("Unknown type");
return;
@@ -882,18 +874,6 @@ 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};
#elif defined(SEENGREAT_V2_PINOUT)
DEBUGBUS_PRINTLN("MatrixPanel_I2S_DMA - S3 devKit-C with PSRAM, SEENGREAT_V2 pinout");
mxconfig.gpio = { 18, 8, 17, // R1_PIN, G1_PIN, B1_PIN,
16, 1, 15, // R2_PIN, G2_PIN, B2_PIN,
7, 48, 6, 47, 2, // A_PIN, B_PIN, C_PIN, D_PIN, E_PIN,
21, 4, 5 }; //LAT_PIN, OE_PIN,CLK_PIN
#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};
@@ -1010,23 +990,25 @@ BusHub75Matrix::BusHub75Matrix(const BusConfig &bc) : Bus(bc.type, bc.start, bc.
}
setBitArray(_ledsDirty, _len, false); // reset dirty bits
// 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));
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));
}
}
PANEL_CHAIN_TYPE chainType = CHAIN_NONE; // default for quarter-scan panels that do not use chaining
// chained panels with cols and rows define need the virtual display driver, so do quarter-scan panels
if (chainLength > 1 && (_rows > 1 || _cols > 1) || bc.type == TYPE_HUB75MATRIX_QS) {
_isVirtual = true;
if (chainLength > 1 && (_rows > 1 || _cols > 1)) chainType = CHAIN_TOP_RIGHT_DOWN; // we need to use a _DOWN chainType, otherwise the display is upside-down
DEBUGBUS_PRINTF_P(PSTR("Using virtual matrix: %ux%u panels of %ux%u pixels\n"), _cols, _rows, mxconfig.mx_width/2, mxconfig.mx_height*2);
chainType = CHAIN_BOTTOM_LEFT_UP; // TODO: is there any need to support other chaining types?
DEBUGBUS_PRINTF_P(PSTR("Using virtual matrix: %ux%u panels of %ux%u pixels\n"), _cols, _rows, mxconfig.mx_width, mxconfig.mx_height);
}
else {
_isVirtual = false;
}
if (_isVirtual) {
virtualDisp = new VirtualMatrixPanel((*display), _rows, _cols, mxconfig.mx_width/2, mxconfig.mx_height*2, chainType);
virtualDisp = new VirtualMatrixPanel((*display), _rows, _cols, mxconfig.mx_width, mxconfig.mx_height, chainType);
virtualDisp->setRotation(0);
if (bc.type == TYPE_HUB75MATRIX_QS) {
switch(panelHeight) {
@@ -1097,7 +1079,7 @@ void IRAM_ATTR BusHub75Matrix::setPixelColor(unsigned pix, uint32_t c) {
uint32_t BusHub75Matrix::getPixelColor(unsigned pix) const {
if (!_valid) return IS_BLACK; // note: no need to check pix >= _len as that is checked in containsPixel()
if (_ledBuffer)
return uint32_t(_ledBuffer[pix]) & 0x00FFFFFF; // FastLED 32bit is RGBA, we need RGBW
return uint32_t(_ledBuffer[pix]);
else
return getBitFromArray(_ledsDirty, pix) ? IS_DARKGREY: IS_BLACK; // just a hack - we only know if the pixel is black or not
}
@@ -1162,8 +1144,8 @@ std::vector<LEDType> BusHub75Matrix::getLEDTypes() {
size_t BusHub75Matrix::getPins(uint8_t* pinArray) const {
if (pinArray) {
pinArray[0] = _isVirtual ? mxconfig.mx_width /2 : mxconfig.mx_width;
pinArray[1] = _isVirtual ? mxconfig.mx_height *2 : mxconfig.mx_height;
pinArray[0] = mxconfig.mx_width;
pinArray[1] = mxconfig.mx_height;
pinArray[2] = mxconfig.chain_length;
pinArray[3] = _rows;
pinArray[4] = _cols;
-5
View File
@@ -198,11 +198,6 @@ struct CRGBW {
uint8_t getAverageLight() const {
return (r + g + b + w) >> 2;
}
// get the average of the R, G, B values
uint8_t getRGBaverage() const {
return ((r + g + b) * 21846) >> 16; // x*21846>>16 is equal to "divide by 3"
}
};
inline CHSV32::CHSV32(const CRGBW& rgb) {
+3 -8
View File
@@ -165,14 +165,9 @@ static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit");
#define WLED_MAX_PANELS 18 // must not be more than 32
// Usermod IDs
// A unique ID is only required when a usermod needs one or more of:
// 1. Inter-usermod communication: UsermodManager::lookup(mod_id) or getUMData(..., mod_id)
// 2. Pin ownership via pinManager: PinOwner enum entries map to these IDs (see pin_manager.h)
// 3. Identification in JSON info: addToJsonInfo emits each mod's ID into the "um" array
// If none of the above apply, omit getId() (or return USERMOD_ID_UNSPECIFIED) and do NOT add an entry here.
#define USERMOD_ID_RESERVED 0 //Unused. Reserved; may indicate no usermod present
#define USERMOD_ID_UNSPECIFIED 1 //Default for usermods that do not require a unique ID
//Usermod IDs
#define USERMOD_ID_RESERVED 0 //Unused. Might indicate no usermod present
#define USERMOD_ID_UNSPECIFIED 1 //Default value for a general user mod that does not specify a custom ID
#define USERMOD_ID_EXAMPLE 2 //Usermod "usermod_v2_example.h"
#define USERMOD_ID_TEMPERATURE 3 //Usermod "usermod_temperature.h"
#define USERMOD_ID_FIXNETSERVICES 4 //Usermod "usermod_Fix_unreachable_netservices.h"
+1 -1
View File
@@ -62,7 +62,7 @@
</div>
<div class="sec" id="OTA">
<h3>Software Update</h3>
<button type="button" onclick="U()">Update WLED</button><br>
<button type="button" onclick="U()">Manual OTA Update</button><br>
<div id="aOTA">Enable ArduinoOTA: <input type="checkbox" name="AO"></div>
Only allow update from same network/WiFi: <input type="checkbox" name="SU"><br>
<i class="warn">&#9888; If you are using multiple VLANs (i.e. IoT or guest network) either set PIN or disable this option.<br>
+5 -5
View File
@@ -252,18 +252,18 @@
var buttonBlock = document.createElement('div');
buttonBlock.className = 'bb';
buttonBlock.innerHTML = `
<div class="bh">Button (switch) ${i}</div>
<div class="bh">Button ${i}</div>
<div class="bs">
<div class="ba">
<label>Short (on → off)</label>
<label>Push/Switch</label>
<select name="MP${b}" class="s" required>${presetOpts}</select>
</div>
<div class="ba">
<label>Long (off → on)</label>
<label>Short (on→off)</label>
<select name="ML${b}" class="s" required>${presetOpts}</select>
</div>
<div class="ba">
<label>Double press (n/a)</label>
<label>Long (off→on)</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 (switch) Action Presets</h3>
<h3>Button Action Presets</h3>
<div id="macros"></div>
<a href="https://kno.wled.ge/features/macros/#analog-button" target="_blank">Analog Button setup</a>
</div>
+9 -71
View File
@@ -7,7 +7,6 @@
<script>
function B() { window.history.back(); }
var cnfr = false;
var deviceInfo = null;
function cR() {
if (!cnfr) {
var bt = gId('rev');
@@ -23,30 +22,15 @@
fetch(getURL('/json/info'))
.then(response => response.json())
.then(data => {
deviceInfo = data;
document.querySelector('.installed-version').textContent = `${data.brand} ${data.ver} (${data.vid})`;
const repoUrl = data.repo && data.repo !== "unknown" ? "https://github.com/" + data.repo + "/releases/latest" : null;
document.querySelector('.release-name').textContent = data.release;
// assemble update URL and update release badge
if (repoUrl) {
if (data.repo && data.repo !== "unknown") {
const repoUrl = "https://github.com/" + data.repo + "/releases/latest";
const badgeUrl = "https://img.shields.io/github/release/" + data.repo + ".svg?style=flat-square";
document.querySelector('.release-repo').href = repoUrl;
document.querySelector('.release-badge').src = badgeUrl;
toggle('release-download'); // only show release download item after receiving a valid data.repo
// fetch latest release from GitHub to build direct bin download link
fetch(`https://api.github.com/repos/${data.repo}/releases/latest`)
.then(r => r.json())
.then(gh => {
const ver = gh.tag_name.replace(/^v/, '');
const suffix = `_${ver}_${data.release}.bin`;
const asset = gh.assets.find(a => a.name.endsWith(suffix));
if (asset) {
const binUrl = asset.browser_download_url.replace(/^https?:\/\/[^/]+/, 'https://download.wled.me');
const el = document.querySelector('.release-name');
el.innerHTML = `<a href="${binUrl}">${data.release}</a>`;
}
})
.catch(() => {}); // silently ignore if GitHub API unavailable
} else {
gId('Norelease-download').classList.add("hide"); // repo invalid -> hide everything
}
@@ -76,48 +60,6 @@
gId('bootupd').classList.add("hide");
toggle('upd');
}
async function autoUpdate() {
const btn = gId('autoUpdBtn');
const status = gId('autoUpdStatus');
btn.disabled = true;
status.textContent = '';
try {
const info = deviceInfo;
if (!info || !info.repo || info.repo === 'unknown') {
status.textContent = 'No release repository available for this build.';
btn.disabled = false;
return;
}
status.textContent = 'Checking GitHub for latest release...';
const ghResp = await fetch(`https://api.github.com/repos/${info.repo}/releases/latest`);
if (!ghResp.ok) throw new Error(`GitHub API error: ${ghResp.status}`);
const ghRelease = await ghResp.json();
const releaseVer = ghRelease.tag_name.replace(/^v/, '');
const assetSuffix = `_${releaseVer}_${info.release}.bin`;
const asset = ghRelease.assets.find(a => a.name.endsWith(assetSuffix));
if (!asset) {
const available = ghRelease.assets.map(a => a.name).join(', ');
status.textContent = `Firmware not found (*${assetSuffix}). Available: ${available}`;
btn.disabled = false;
return;
}
status.textContent = `Downloading ${asset.name} (${Math.round(asset.size/1024)} KB)...`;
const fwUrl = asset.browser_download_url.replace(/^https?:\/\/[^/]+/, 'https://download.wled.me');
const fwResp = await fetch(fwUrl);
if (!fwResp.ok) throw new Error(`Download failed: ${fwResp.status}`);
const blob = await fwResp.blob();
const file = new File([blob], asset.name, {type: 'application/octet-stream'});
const dt = new DataTransfer();
dt.items.add(file);
document.querySelector('input[name="update"]').files = dt.files;
status.textContent = `Downloaded ${asset.name}. Uploading to device...`;
hideforms();
gId('upd').submit();
} catch(e) {
status.textContent = 'Auto-update failed: ' + e.message;
btn.disabled = false;
}
}
</script>
<style>
@import url("style.css");
@@ -135,25 +77,21 @@
<p>
Installed version: <span class="sip installed-version">Loading...</span><br>
Release: <span class="sip release-name">Loading...</span><br>
<span id="Norelease-download">Latest release: Checking...<br></span>
<span id="Norelease-download">Latest binary: Checking...<br></span>
<span id="release-download" class="hide">
Latest version: <a class="release-repo" href="https://github.com/wled/WLED/releases/latest" target="_blank" rel="noopener noreferrer"
Download the latest binary: <a class="release-repo" href="https://github.com/wled/WLED/releases/latest" target="_blank" rel="noopener noreferrer"
style="vertical-align: text-bottom; display: inline-flex;">
<img class="release-badge" alt="View latest"></a><br>
<button type="button" id="autoUpdBtn" onclick="autoUpdate()">Auto update</button>
<span id="autoUpdStatus" style="display:block;margin-top:4px;font-size:13px;"></span>
<img class="release-badge" alt="badge"></a><br> <!-- start with an empty placeholder, src will be filled after fetching /json/info -->
</span>
</p>
<hr class="sml">
<h3>Manual upload</h3>
<input type="hidden" name="skipValidation" value="" id="sV">
<input type='file' name='update' required><br> <!--should have accept='.bin', but it prevents file upload from android app-->
<input type='checkbox' onchange="sV.value=checked?1:''" id="skipValidation">
<label for='skipValidation'>Ignore firmware validation</label><br>
<button type="submit">Upload</button><br>
<span>
<button type="submit">Update WLED!</button><br>
<span id="rev">
<hr class="sml">
<button id="rev" type="button" onclick="cR()">Revert update</button><br>
<button type="button" onclick="cR()">Revert update</button><br>
</span>
</div>
</form>
@@ -169,4 +107,4 @@
<div id="Noupd" class="hide sec"><b>Updating...</b><br>Please do not close or refresh the page :)</div>
<p><button id="backbtn" type="button" onclick="B()">Back</button></p>
</body>
</html>
</html>
+1 -11
View File
@@ -106,17 +106,12 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol, size_
handleArtnetPollReply(clientIP);
return;
}
if (packetLen < 18) return; // need art_length (offset 16, 2 bytes) for DMX data
uni = p->art_universe;
dmxChannels = htons(p->art_length);
const int artNetMaxData = (packetLen >= 18) ? (int)(packetLen - 18) : 0; // art_data at offset 18; clamp so e131_data[dmxChannels] stays in bounds
if (dmxChannels > artNetMaxData) dmxChannels = artNetMaxData;
if (dmxChannels > MAX_CHANNELS_PER_UNIVERSE) dmxChannels = MAX_CHANNELS_PER_UNIVERSE;
e131_data = p->art_data;
seq = p->art_sequence_number;
mde = REALTIME_MODE_ARTNET;
} else if (protocol == P_E131) {
if (packetLen < 126) return; // need up to property_values[0] (offset 125) and property_value_count (offset 123)
// Ignore PREVIEW data (E1.31: 6.2.6)
if ((p->options & 0x80) != 0) return;
dmxChannels = htons(p->property_value_count) - 1; // on malformed packets, this can become negative, checked below
@@ -125,9 +120,6 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol, size_
uni = htons(p->universe);
e131_data = p->property_values;
seq = p->sequence_number;
const int e131MaxData = (packetLen > 126) ? (int)(packetLen - 126) : 0; // property_values at offset 125; clamp so e131_data[dmxChannels] stays in bounds
if (dmxChannels > e131MaxData) dmxChannels = e131MaxData;
if (dmxChannels > MAX_CHANNELS_PER_UNIVERSE) dmxChannels = MAX_CHANNELS_PER_UNIVERSE;
if (e131Priority != 0) {
if (p->priority < e131Priority ) return;
// track highest priority & skip all lower priorities
@@ -143,10 +135,8 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol, size_
#ifdef WLED_ENABLE_DMX
// does not act on out-of-order packets yet
if (e131ProxyUniverse > 0 && uni == e131ProxyUniverse) {
// Art-Net: art_data is 0-indexed (channel 1 at index 0)
// E1.31: property_values[0] is start code, (channel 1 at index 1)
for (uint16_t i = 1; i <= dmxChannels; i++)
dmx.write(i, mde == REALTIME_MODE_ARTNET ? e131_data[i-1] : e131_data[i]);
dmx.write(i, e131_data[i]);
dmx.update();
}
#endif
+4 -4
View File
@@ -524,10 +524,10 @@ inline size_t getFreeHeapSize() { return ESP.getFreeHeap(); } // returns free he
inline size_t getContiguousFreeHeap() { return ESP.getMaxFreeBlockSize(); } // returns largest contiguous free block
#endif
#define BFRALLOC_NOBYTEACCESS (1 << 0) // ESP32 has 32bit accessible DRAM (usually ~50kB free) that must not be byte-accessed
#define BFRALLOC_PREFER_DRAM (1 << 1) // prefer DRAM over PSRAM (can still use PSRAM for larger allocations if DRAM is starting to run low)
#define BFRALLOC_ENFORCE_DRAM (1 << 2) // use DRAM only, no PSRAM allowed
#define BFRALLOC_PREFER_PSRAM (1 << 3) // prefer PSRAM over DRAM (can still use DRAM if there is loads of free DRAM to optimize speed)
#define BFRALLOC_ENFORCE_PSRAM (1 << 4) // use PSRAM if available, falls back to DRAM if PSRAM fails
#define BFRALLOC_PREFER_DRAM (1 << 1) // prefer DRAM over PSRAM
#define BFRALLOC_ENFORCE_DRAM (1 << 2) // use DRAM only, no PSRAM
#define BFRALLOC_PREFER_PSRAM (1 << 3) // prefer PSRAM over DRAM
#define BFRALLOC_ENFORCE_PSRAM (1 << 4) // use PSRAM if available, otherwise uses DRAM
#define BFRALLOC_CLEAR (1 << 5) // clear allocated buffer after allocation
void *allocate_buffer(size_t size, uint32_t type);
-1
View File
@@ -188,7 +188,6 @@ static bool appendObjectToFile(const char* key, const JsonDocument* content, uin
if (f.size() < 3) {
char init[10];
strcpy_P(init, PSTR("{\"0\":{}}"));
f.seek(0, SeekSet); // rewind to ensure we overwrite from the start, instead of appending
f.print(init);
}
+7 -6
View File
@@ -116,8 +116,8 @@ void handleImprovPacket() {
return;
}
} else if (packetByte > 9) { //RPC data
if (packetByte > 137) return; //prevent buffer overflow
rpcData[packetByte - 10] = next;
if (packetByte > 137) return; //prevent buffer overflow
}
}
}
@@ -238,8 +238,8 @@ void handleImprovWifiScan() {
bool isOpen = WiFi.encryptionType(i) == WIFI_AUTH_OPEN;
#endif
char ssidStr[33] = {'\0'};
strlcpy(ssidStr, WiFi.SSID(i).c_str(), sizeof(ssidStr));
char ssidStr[33];
strcpy(ssidStr, WiFi.SSID(i).c_str());
const char *str[3] = {ssidStr, rssiStr, isOpen ? "NO":"YES"};
sendImprovRPCResult(ImprovRPCType::Request_Scan, 3, str);
}
@@ -258,13 +258,14 @@ static void parseWiFiCommand(char* rpcData) {
unsigned ssidLen = rpcData[1];
if (ssidLen > len -1 || ssidLen > 32) return;
memset(multiWiFi[0].clientSSID, 0, sizeof(multiWiFi[0].clientSSID));
memset(multiWiFi[0].clientSSID, 0, 32);
memcpy(multiWiFi[0].clientSSID, rpcData+2, ssidLen);
memset(multiWiFi[0].clientPass, 0, sizeof(multiWiFi[0].clientPass));
memset(multiWiFi[0].clientPass, 0, 64);
if (len > ssidLen +1) {
unsigned passLen = rpcData[2+ssidLen];
memcpy(multiWiFi[0].clientPass, rpcData+3+ssidLen, min(size_t(passLen), sizeof(multiWiFi[0].clientPass)));
memset(multiWiFi[0].clientPass, 0, 64);
memcpy(multiWiFi[0].clientPass, rpcData+3+ssidLen, passLen);
}
sendImprovStateResponse(0x03); //provisioning
+1 -3
View File
@@ -520,9 +520,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
if (root["win"].isNull() && getVal(root["ps"], presetCycCurr, 1, 250) && presetCycCurr > 0 && presetCycCurr < 251 && presetCycCurr != currentPreset) {
DEBUG_PRINTF_P(PSTR("Preset select: %d\n"), presetCycCurr);
// b) preset ID only or preset that does not change state (use embedded cycling limits if they exist in getVal())
// async load from file system (only preset ID was specified)
// avoid propogating CALL_MODE_INIT, which may cause accidental recursion
applyPreset(presetCycCurr, callMode == CALL_MODE_INIT ? CALL_MODE_DIRECT_CHANGE : callMode);
applyPreset(presetCycCurr, callMode); // async load from file system (only preset ID was specified)
return stateResponse;
} else presetCycCurr = currentPreset; // restore presetCycCurr
}
+1 -23
View File
@@ -87,7 +87,7 @@ const ethernet_settings ethernetBoards[] = {
// ESP32-ETHERNET-KIT-VE
{
1, // eth_address,
0, // eth_address,
5, // eth_power,
23, // eth_mdc,
18, // eth_mdio,
@@ -177,12 +177,6 @@ const ethernet_settings ethernetBoards[] = {
},
};
// sanity checks for ethernet config table and WLED_ETH_DEFAULT
static_assert((sizeof(ethernetBoards)/sizeof(ethernetBoards[0])) == WLED_NUM_ETH_TYPES, "WLED_NUM_ETH_TYPES does not match size of ethernetBoards[] table.");
#ifdef WLED_ETH_DEFAULT
static_assert(((WLED_ETH_DEFAULT) >= WLED_ETH_NONE) && ((WLED_ETH_DEFAULT) < WLED_NUM_ETH_TYPES), "WLED_ETH_DEFAULT is out of range.");
#endif
bool initEthernet()
{
static bool successfullyConfiguredEthernet = false;
@@ -448,26 +442,10 @@ 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;
+2 -2
View File
@@ -194,8 +194,8 @@ void handlePresets()
changePreset = true;
} else {
if (!fdo["seg"].isNull() || !fdo["on"].isNull() || !fdo["bri"].isNull() || !fdo["nl"].isNull() || !fdo["ps"].isNull() || !fdo[F("playlist")].isNull()) changePreset = true;
if (!(tmpMode == CALL_MODE_INIT || (tmpMode == CALL_MODE_BUTTON_PRESET && fdo["ps"].is<const char *>() && strchr(fdo["ps"].as<const char *>(),'~') != strrchr(fdo["ps"].as<const char *>(),'~'))))
fdo.remove("ps"); // remove load request for presets to prevent recursive crash (if not called by boot preset or button which contains preset cycling string "1~5~")
if (!(tmpMode == CALL_MODE_BUTTON_PRESET && fdo["ps"].is<const char *>() && strchr(fdo["ps"].as<const char *>(),'~') != strrchr(fdo["ps"].as<const char *>(),'~')))
fdo.remove("ps"); // remove load request for presets to prevent recursive crash (if not called by button and contains preset cycling string "1~5~")
deserializeState(fdo, CALL_MODE_NO_NOTIFY, tmpPreset); // may change presetToApply by calling applyPreset()
}
if (!errorFlag && tmpPreset < 255 && changePreset) currentPreset = tmpPreset;
+19 -23
View File
@@ -99,37 +99,33 @@ bool ESPAsyncE131::initMulticast(uint16_t port, uint16_t universe, uint8_t n) {
void ESPAsyncE131::parsePacket(AsyncUDPPacket _packet) {
bool error = false;
uint8_t protocol = P_ARTNET;
uint8_t protocol = P_E131;
const size_t pktLen = _packet.length();
e131_packet_t *sbuff = reinterpret_cast<e131_packet_t *>(_packet.data());
// E1.31 packet identifier (ACN_ID = "ASC-E1.17"), need at least 16 bytes to safely read acn_id (offset 4, length 12).
if (pktLen >= 16) {
if (!memcmp(sbuff->acn_id, ESPAsyncE131::ACN_ID, sizeof(sbuff->acn_id)))
protocol = P_E131;
}
if (protocol == P_ARTNET) {
if (memcmp(sbuff->art_id, ESPAsyncE131::ART_ID, sizeof(sbuff->art_id)))
error = true; //not ART_ID = "Art-Net"
if (sbuff->art_opcode != ARTNET_OPCODE_OPDMX && sbuff->art_opcode != ARTNET_OPCODE_OPPOLL)
error = true; //not a DMX or poll packet
} else { //E1.31 error handling
if (pktLen < 126) { // need up to property_values[0] at offset 125
error = true;
} else {
if (htonl(sbuff->root_vector) != ESPAsyncE131::VECTOR_ROOT)
error = true;
if (htonl(sbuff->frame_vector) != ESPAsyncE131::VECTOR_FRAME)
error = true;
if (sbuff->dmp_vector != ESPAsyncE131::VECTOR_DMP)
error = true;
if (sbuff->property_values[0] != 0)
error = true;
}
if (memcmp(sbuff->acn_id, ESPAsyncE131::ACN_ID, sizeof(sbuff->acn_id)))
protocol = P_ARTNET;
}
if (protocol == P_ARTNET) {
if (memcmp(sbuff->art_id, ESPAsyncE131::ART_ID, sizeof(sbuff->art_id)))
error = true; //not "Art-Net"
if (sbuff->art_opcode != ARTNET_OPCODE_OPDMX && sbuff->art_opcode != ARTNET_OPCODE_OPPOLL)
error = true; //not a DMX or poll packet
} else { //E1.31 error handling
if (htonl(sbuff->root_vector) != ESPAsyncE131::VECTOR_ROOT)
error = true;
if (htonl(sbuff->frame_vector) != ESPAsyncE131::VECTOR_FRAME)
error = true;
if (sbuff->dmp_vector != ESPAsyncE131::VECTOR_DMP)
error = true;
if (sbuff->property_values[0] != 0)
error = true;
}
if (error && _packet.localPort() == DDP_DEFAULT_PORT) { //DDP packet
error = false;
protocol = P_DDP;
+1 -1
View File
@@ -272,4 +272,4 @@ class E131Priority {
}
};
#endif // ESPASYNCE131_H_
#endif // ESPASYNCE131_H_
+3 -6
View File
@@ -6,7 +6,7 @@
#define UDP_SEG_SIZE 36
#define SEG_OFFSET (41)
static constexpr size_t WLEDPACKETSIZE = 41+(WS2812FX::getMaxSegments()*UDP_SEG_SIZE); // make sure this is known at compile-time
#define WLEDPACKETSIZE (41+(WS2812FX::getMaxSegments()*UDP_SEG_SIZE)+0)
#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,7 +268,6 @@ 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;
@@ -500,7 +499,7 @@ void handleNotifications()
packetSize = rgbUdp.parsePacket();
if (packetSize) {
if (!receiveDirect) return;
if (packetSize > UDP_IN_MAXSIZE || packetSize < 3) return; // packetSize must not exceed buffersize (UDP_IN_MAXSIZE)
if (packetSize > UDP_IN_MAXSIZE || packetSize < 3) return;
realtimeIP = rgbUdp.remoteIP();
DEBUG_PRINTLN(rgbUdp.remoteIP());
uint8_t lbuf[packetSize];
@@ -588,9 +587,7 @@ void handleNotifications()
unsigned id = (tpmPayloadFrameSize/3)*(packetNum-1); //start LED
unsigned totalLen = strip.getLengthTotal();
// 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++) {
for (size_t i = 6; i < tpmPayloadFrameSize + 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
+1 -4
View File
@@ -920,7 +920,7 @@ void *allocate_buffer(size_t size, uint32_t type) {
buffer = p_malloc(size); // prefer PSRAM
}
else if (type & BFRALLOC_ENFORCE_PSRAM)
buffer = p_malloc(size); // use PSRAM if available, fall back to DRAM if not (safeguard for boards without PSRAM #5629)
buffer = heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); // use PSRAM only, otherwise return nullptr
buffer = validateFreeHeap(buffer);
#endif
if (buffer && (type & BFRALLOC_CLEAR))
@@ -1296,9 +1296,6 @@ 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;
-12
View File
@@ -503,12 +503,6 @@ void WLED::setup()
if (needsCfgSave) serializeConfigToFS(); // usermods required new parameters; need to wait for strip to be initialised #4752
if (bootPreset > 0) {
handlePresets(); // handle boot preset
handlePlaylist(); // handle playlist if preset queued one
handlePresets(); // handle presets again to give a chance for anything queued by the boot preset or playlist
}
if (strcmp(multiWiFi[0].clientSSID, DEFAULT_CLIENT_SSID) == 0 && !configBackupExists())
showWelcomePage = true;
@@ -657,7 +651,6 @@ 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
@@ -698,7 +691,6 @@ 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
@@ -711,7 +703,6 @@ 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
@@ -795,12 +786,9 @@ 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
+2 -2
View File
@@ -7,7 +7,7 @@
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2605011
#define VERSION 2605010
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG
@@ -274,7 +274,7 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument<PSRAM_Allocator>;
#define STRINGIFY(X) #X
#define TOSTRING(X) STRINGIFY(X)
#define WLED_CODENAME "Kagayaki"
#define WLED_CODENAME "Niji"
// AP and OTA default passwords (for maximum security change them!)
WLED_GLOBAL char apPass[65] _INIT(WLED_AP_PASS);