mirror of
https://github.com/wled/WLED.git
synced 2026-06-17 02:11:51 +00:00
Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4374f013f5 | |||
| f90990b869 | |||
| 7d999a6d61 | |||
| dd0a83f9d1 | |||
| dd01940cf5 | |||
| 0d93ca8bac | |||
| a083217176 | |||
| 86a218e2b4 | |||
| 28b441917b | |||
| 129c68c6c9 | |||
| 923a1ec729 | |||
| 2ceeb67007 | |||
| 13aa7ed787 | |||
| 68640f5dd8 | |||
| 8a653ebb29 | |||
| dc5ee3f2fa | |||
| 5dc627ef5a | |||
| 520d8704c5 | |||
| 55219980c6 | |||
| 6dcae505a0 | |||
| 52d897ed7a | |||
| ef354d0119 | |||
| 89aae707d0 | |||
| 25668bb068 | |||
| 2c1f3c581d | |||
| 1c8cd15cd1 | |||
| 32de108f4c | |||
| e551ca833b | |||
| 2fa5266dbf | |||
| 46dafa2c6b | |||
| 967ef0720c | |||
| 82489664a0 | |||
| 9fdd48776d | |||
| 74e0b166ba | |||
| 61bd8bf5b4 | |||
| f79cf568b2 | |||
| c53511b5aa | |||
| 0325e30bef | |||
| 456ddee962 | |||
| 41e4bbf2bd | |||
| 7f292b9ecd | |||
| 6afc990397 | |||
| 974c15c1d3 | |||
| 96b0c7b4ae | |||
| 6394bec21b | |||
| 79fde56b0e | |||
| e6077acb78 | |||
| 83dd1b078d | |||
| c1e5ac5bd1 | |||
| 1e42ebf852 | |||
| e4a7effdb0 | |||
| ae36f7e203 | |||
| 4cdc23a48b | |||
| 48ab88e11e | |||
| fd890b3d58 | |||
| f994c5e995 | |||
| c789e3d187 | |||
| fadc75da55 | |||
| 945896ddb7 | |||
| 188beec614 | |||
| 19947ec07e | |||
| 8228c7eea4 | |||
| 1aba7057a5 | |||
| 227d535404 | |||
| da8bfda2f0 | |||
| e4f2964bd6 | |||
| a3f730275a | |||
| b5556aa977 | |||
| 82ddefff87 | |||
| 56a97a0caa | |||
| 679bb39922 | |||
| b293db6af5 | |||
| 774fbc5fab |
+10
-149
@@ -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:
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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), 3–10× 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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
Generated
+8
-8
@@ -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
@@ -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,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(.);")
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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,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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -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,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,'µs');"));
|
||||
oappend(F("addInfo('INA226:Decimals',1,'(0-5)');"));
|
||||
oappend(F("addInfo('INA226:ShuntResistor',1,'mΩ');"));
|
||||
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))
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
@@ -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,5 +1,5 @@
|
||||
{
|
||||
"name": "pov_display",
|
||||
"name:": "pov_display",
|
||||
"build": { "libArchive": false},
|
||||
"platforms": ["espressif32"]
|
||||
}
|
||||
|
||||
+12
-11
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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">⚠ If you are using multiple VLANs (i.e. IoT or guest network) either set PIN or disable this option.<br>
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -272,4 +272,4 @@ class E131Priority {
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ESPASYNCE131_H_
|
||||
#endif // ESPASYNCE131_H_
|
||||
+3
-6
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user