mirror of
https://github.com/wled/WLED.git
synced 2025-07-18 16:26:32 +00:00
Merge branch 'main' into usermod-libs-matrix
This commit is contained in:
commit
adb9b773b0
2
.github/ISSUE_TEMPLATE/bug.yml
vendored
2
.github/ISSUE_TEMPLATE/bug.yml
vendored
@ -80,7 +80,7 @@ body:
|
||||
id: terms
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/Aircoookie/WLED/blob/master/CODE_OF_CONDUCT.md)
|
||||
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/wled-dev/WLED/blob/main/CODE_OF_CONDUCT.md)
|
||||
options:
|
||||
- label: I agree to follow this project's Code of Conduct
|
||||
required: true
|
||||
|
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -26,7 +26,7 @@ jobs:
|
||||
|
||||
|
||||
build:
|
||||
name: Build Enviornments
|
||||
name: Build Environments
|
||||
runs-on: ubuntu-latest
|
||||
needs: get_default_envs
|
||||
strategy:
|
||||
|
6
.github/workflows/nightly.yml
vendored
6
.github/workflows/nightly.yml
vendored
@ -39,3 +39,9 @@ jobs:
|
||||
files: |
|
||||
*.bin
|
||||
*.bin.gz
|
||||
- name: Repository Dispatch
|
||||
uses: peter-evans/repository-dispatch@v3
|
||||
with:
|
||||
repository: wled/WLED-WebInstaller
|
||||
event-type: release-nightly
|
||||
token: ${{ secrets.PAT_PUBLIC }}
|
||||
|
16
.github/workflows/pr-merge.yaml
vendored
Normal file
16
.github/workflows/pr-merge.yaml
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
name: Notify Discord on PR Merge
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
|
||||
jobs:
|
||||
notify:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send Discord notification
|
||||
shell: bash
|
||||
env:
|
||||
DISCORD_WEBHOOK_BETA_TESTERS: ${{ secrets.DISCORD_WEBHOOK_BETA_TESTERS }}
|
||||
if: github.event.pull_request.merged == true
|
||||
run: |
|
||||
curl -H "Content-Type: application/json" -d '{"content": "Pull Request #{{ github.event.pull_request.number }} merged by {{ github.actor }}"}' $DISCORD_WEBHOOK_BETA_TESTERS
|
7
.github/workflows/release.yml
vendored
7
.github/workflows/release.yml
vendored
@ -18,9 +18,16 @@ jobs:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
merge-multiple: true
|
||||
- name: "✏️ Generate release changelog"
|
||||
id: changelog
|
||||
uses: janheinrichmerker/action-github-changelog-generator@v2.3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
sinceTag: v0.15.0
|
||||
- name: Create draft release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
body: ${{ steps.changelog.outputs.changelog }}
|
||||
draft: True
|
||||
files: |
|
||||
*.bin
|
||||
|
13
.github/workflows/test.yaml
vendored
Normal file
13
.github/workflows/test.yaml
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
dispatch:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Repository Dispatch
|
||||
uses: peter-evans/repository-dispatch@v3
|
||||
with:
|
||||
repository: wled/WLED-WebInstaller
|
||||
event-type: release-nightly
|
||||
token: ${{ secrets.PAT_PUBLIC }}
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,6 +15,7 @@ wled-update.sh
|
||||
|
||||
/build_output/
|
||||
/node_modules/
|
||||
/logs/
|
||||
|
||||
/wled00/extLibs
|
||||
/wled00/LittleFS
|
||||
|
@ -27,7 +27,7 @@ Github will pick up the changes so your PR stays up-to-date.
|
||||
> For example, we regularly lost review comments when the PR author force-pushes code changes. So, pretty please, do not force-push.
|
||||
|
||||
|
||||
You can find a collection of very useful tips and tricks here: https://github.com/Aircoookie/WLED/wiki/How-to-properly-submit-a-PR
|
||||
You can find a collection of very useful tips and tricks here: https://github.com/wled-dev/WLED/wiki/How-to-properly-submit-a-PR
|
||||
|
||||
|
||||
### Code style
|
||||
|
1893
package-lock.json
generated
1893
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@ -14,19 +14,19 @@
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Aircoookie/WLED.git"
|
||||
"url": "git+https://github.com/wled-dev/WLED.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/Aircoookie/WLED/issues"
|
||||
"url": "https://github.com/wled-dev/WLED/issues"
|
||||
},
|
||||
"homepage": "https://github.com/Aircoookie/WLED#readme",
|
||||
"homepage": "https://github.com/wled-dev/WLED#readme",
|
||||
"dependencies": {
|
||||
"clean-css": "^5.3.3",
|
||||
"html-minifier-terser": "^7.2.0",
|
||||
"inliner": "^1.13.1",
|
||||
"nodemon": "^3.1.7"
|
||||
"web-resource-inliner": "^7.0.0",
|
||||
"nodemon": "^3.1.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
|
@ -1,3 +1,21 @@
|
||||
Import('env')
|
||||
Import("env")
|
||||
import shutil
|
||||
|
||||
env.Execute("npm run build")
|
||||
node_ex = shutil.which("node")
|
||||
# Check if Node.js is installed and present in PATH if it failed, abort the build
|
||||
if node_ex is None:
|
||||
print('\x1b[0;31;43m' + 'Node.js is not installed or missing from PATH html css js will not be processed check https://kno.wled.ge/advanced/compiling-wled/' + '\x1b[0m')
|
||||
exitCode = env.Execute("null")
|
||||
exit(exitCode)
|
||||
else:
|
||||
# Install the necessary node packages for the pre-build asset bundling script
|
||||
print('\x1b[6;33;42m' + 'Installing node packages' + '\x1b[0m')
|
||||
env.Execute("npm ci")
|
||||
|
||||
# Call the bundling script
|
||||
exitCode = env.Execute("npm run build")
|
||||
|
||||
# If it failed, abort the build
|
||||
if (exitCode):
|
||||
print('\x1b[0;31;43m' + 'npm run build fails check https://kno.wled.ge/advanced/compiling-wled/' + '\x1b[0m')
|
||||
exit(exitCode)
|
||||
|
@ -139,7 +139,7 @@ lib_compat_mode = strict
|
||||
lib_deps =
|
||||
fastled/FastLED @ 3.6.0
|
||||
IRremoteESP8266 @ 2.8.2
|
||||
makuna/NeoPixelBus @ 2.8.0
|
||||
makuna/NeoPixelBus @ 2.8.3
|
||||
#https://github.com/makuna/NeoPixelBus.git#CoreShaderBeta
|
||||
https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.4.0
|
||||
# for I2C interface
|
||||
@ -360,6 +360,7 @@ 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
|
||||
|
||||
@ -369,12 +370,14 @@ extends = env:nodemcuv2
|
||||
platform = ${esp8266.platform_compat}
|
||||
platform_packages = ${esp8266.platform_packages_compat}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP8266_compat\" #-DWLED_DISABLE_2D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
;; lib_deps = ${esp8266.lib_deps_compat} ;; experimental - use older NeoPixelBus 2.7.9
|
||||
|
||||
[env:nodemcuv2_160]
|
||||
extends = env:nodemcuv2
|
||||
board_build.f_cpu = 160000000L
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP8266_160\" #-DWLED_DISABLE_2D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
custom_usermods = audioreactive
|
||||
|
||||
[env:esp8266_2m]
|
||||
@ -384,6 +387,8 @@ 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}
|
||||
|
||||
[env:esp8266_2m_compat]
|
||||
@ -392,11 +397,15 @@ extends = env:esp8266_2m
|
||||
platform = ${esp8266.platform_compat}
|
||||
platform_packages = ${esp8266.platform_packages_compat}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP02_compat\" #-DWLED_DISABLE_2D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM1D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
|
||||
[env:esp8266_2m_160]
|
||||
extends = env:esp8266_2m
|
||||
board_build.f_cpu = 160000000L
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP02_160\"
|
||||
-D WLED_DISABLE_PARTICLESYSTEM1D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
custom_usermods = audioreactive
|
||||
|
||||
[env:esp01_1m_full]
|
||||
@ -407,6 +416,8 @@ 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
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp01_1m_full_compat]
|
||||
@ -415,12 +426,16 @@ extends = env:esp01_1m_full
|
||||
platform = ${esp8266.platform_compat}
|
||||
platform_packages = ${esp8266.platform_packages_compat}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP01_compat\" -D WLED_DISABLE_OTA #-DWLED_DISABLE_2D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM1D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
|
||||
[env:esp01_1m_full_160]
|
||||
extends = env:esp01_1m_full
|
||||
board_build.f_cpu = 160000000L
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP01_160\" -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
|
||||
custom_usermods = audioreactive
|
||||
|
||||
[env:esp32dev]
|
||||
|
@ -34,7 +34,7 @@ lib_deps = ${esp8266.lib_deps}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
|
||||
;
|
||||
; *** To use the below defines/overrides, copy and paste each onto it's own line just below build_flags in the section above.
|
||||
; *** To use the below defines/overrides, copy and paste each onto its own line just below build_flags in the section above.
|
||||
;
|
||||
; Set a release name that may be used to distinguish required binary for flashing
|
||||
; -D WLED_RELEASE_NAME=\"ESP32_MULTI_USREMODS\"
|
||||
@ -280,7 +280,7 @@ lib_deps = ${esp32s2.lib_deps}
|
||||
[env:esp32s3dev_8MB_PSRAM_qspi]
|
||||
;; ESP32-TinyS3 development board, with 8MB FLASH and PSRAM (memory_type: qio_qspi)
|
||||
extends = env:esp32s3dev_8MB_PSRAM_opi
|
||||
;board = um_tinys3 ; -> needs workaround from https://github.com/Aircoookie/WLED/pull/2905#issuecomment-1328049860
|
||||
;board = um_tinys3 ; -> needs workaround from https://github.com/wled-dev/WLED/pull/2905#issuecomment-1328049860
|
||||
board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support
|
||||
board_build.arduino.memory_type = qio_qspi ;; use with PSRAM: 2MB or 4MB
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
<p align="center">
|
||||
<img src="/images/wled_logo_akemi.png">
|
||||
<a href="https://github.com/Aircoookie/WLED/releases"><img src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square"></a>
|
||||
<a href="https://raw.githubusercontent.com/Aircoookie/WLED/master/LICENSE"><img src="https://img.shields.io/github/license/Aircoookie/wled?color=blue&style=flat-square"></a>
|
||||
<a href="https://github.com/wled-dev/WLED/releases"><img src="https://img.shields.io/github/release/wled-dev/WLED.svg?style=flat-square"></a>
|
||||
<a href="https://raw.githubusercontent.com/wled-dev/WLED/main/LICENSE"><img src="https://img.shields.io/github/license/wled-dev/wled?color=blue&style=flat-square"></a>
|
||||
<a href="https://wled.discourse.group"><img src="https://img.shields.io/discourse/topics?colorB=blue&label=forum&server=https%3A%2F%2Fwled.discourse.group%2F&style=flat-square"></a>
|
||||
<a href="https://discord.gg/QAh7wJHrRM"><img src="https://img.shields.io/discord/473448917040758787.svg?colorB=blue&label=discord&style=flat-square"></a>
|
||||
<a href="https://kno.wled.ge"><img src="https://img.shields.io/badge/quick_start-wiki-blue.svg?style=flat-square"></a>
|
||||
<a href="https://github.com/Aircoookie/WLED-App"><img src="https://img.shields.io/badge/app-wled-blue.svg?style=flat-square"></a>
|
||||
<a href="https://gitpod.io/#https://github.com/Aircoookie/WLED"><img src="https://img.shields.io/badge/Gitpod-ready--to--code-blue?style=flat-square&logo=gitpod"></a>
|
||||
<a href="https://gitpod.io/#https://github.com/wled-dev/WLED"><img src="https://img.shields.io/badge/Gitpod-ready--to--code-blue?style=flat-square&logo=gitpod"></a>
|
||||
|
||||
</p>
|
||||
|
||||
|
@ -1 +1 @@
|
||||
platformio
|
||||
platformio>=6.1.17
|
||||
|
@ -2,27 +2,25 @@
|
||||
# This file is autogenerated by pip-compile with Python 3.11
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile
|
||||
# pip-compile requirements.in
|
||||
#
|
||||
ajsonrpc==1.2.0
|
||||
# via platformio
|
||||
anyio==4.6.0
|
||||
anyio==4.8.0
|
||||
# via starlette
|
||||
bottle==0.13.1
|
||||
bottle==0.13.2
|
||||
# via platformio
|
||||
certifi==2024.8.30
|
||||
certifi==2025.1.31
|
||||
# via requests
|
||||
charset-normalizer==3.3.2
|
||||
charset-normalizer==3.4.1
|
||||
# via requests
|
||||
click==8.1.7
|
||||
click==8.1.8
|
||||
# via
|
||||
# platformio
|
||||
# uvicorn
|
||||
colorama==0.4.6
|
||||
# via
|
||||
# click
|
||||
# platformio
|
||||
h11==0.14.0
|
||||
# via platformio
|
||||
h11==0.16.0
|
||||
# via
|
||||
# uvicorn
|
||||
# wsproto
|
||||
@ -30,13 +28,13 @@ idna==3.10
|
||||
# via
|
||||
# anyio
|
||||
# requests
|
||||
marshmallow==3.22.0
|
||||
marshmallow==3.26.1
|
||||
# via platformio
|
||||
packaging==24.1
|
||||
packaging==24.2
|
||||
# via marshmallow
|
||||
platformio==6.1.16
|
||||
platformio==6.1.17
|
||||
# via -r requirements.in
|
||||
pyelftools==0.31
|
||||
pyelftools==0.32
|
||||
# via platformio
|
||||
pyserial==3.5
|
||||
# via platformio
|
||||
@ -46,13 +44,15 @@ semantic-version==2.10.0
|
||||
# via platformio
|
||||
sniffio==1.3.1
|
||||
# via anyio
|
||||
starlette==0.39.1
|
||||
starlette==0.45.3
|
||||
# via platformio
|
||||
tabulate==0.9.0
|
||||
# via platformio
|
||||
urllib3==2.2.3
|
||||
typing-extensions==4.12.2
|
||||
# via anyio
|
||||
urllib3==2.3.0
|
||||
# via requests
|
||||
uvicorn==0.30.6
|
||||
uvicorn==0.34.0
|
||||
# via platformio
|
||||
wsproto==1.2.0
|
||||
# via platformio
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
const fs = require("node:fs");
|
||||
const path = require("path");
|
||||
const inliner = require("inliner");
|
||||
const inline = require("web-resource-inliner");
|
||||
const zlib = require("node:zlib");
|
||||
const CleanCSS = require("clean-css");
|
||||
const minifyHtml = require("html-minifier-terser").minify;
|
||||
@ -89,7 +89,7 @@ function adoptVersionAndRepo(html) {
|
||||
repoUrl = repoUrl.replace(/^git\+/, "");
|
||||
repoUrl = repoUrl.replace(/\.git$/, "");
|
||||
html = html.replaceAll("https://github.com/atuline/WLED", repoUrl);
|
||||
html = html.replaceAll("https://github.com/Aircoookie/WLED", repoUrl);
|
||||
html = html.replaceAll("https://github.com/wled-dev/WLED", repoUrl);
|
||||
}
|
||||
let version = packageJson.version;
|
||||
if (version) {
|
||||
@ -128,7 +128,12 @@ async function minify(str, type = "plain") {
|
||||
|
||||
async function writeHtmlGzipped(sourceFile, resultFile, page) {
|
||||
console.info("Reading " + sourceFile);
|
||||
new inliner(sourceFile, async function (error, html) {
|
||||
inline.html({
|
||||
fileContent: fs.readFileSync(sourceFile, "utf8"),
|
||||
relativeTo: path.dirname(sourceFile),
|
||||
strict: true,
|
||||
},
|
||||
async function (error, html) {
|
||||
if (error) throw error;
|
||||
|
||||
html = adoptVersionAndRepo(html);
|
||||
|
@ -2,48 +2,45 @@
|
||||
* @file usermod_BMW68X.h
|
||||
* @author Gabriel A. Sieben (GeoGab)
|
||||
* @brief Usermod for WLED to implement the BME680/BME688 sensor
|
||||
* @version 1.0.0
|
||||
* @date 19 Feb 2024
|
||||
* @version 1.0.2
|
||||
* @date 28 March 2025
|
||||
*/
|
||||
|
||||
#warning ********************Included USERMOD_BME68X ********************
|
||||
#define UMOD_DEVICE "ESP32" // NOTE - Set your hardware here
|
||||
#define HARDWARE_VERSION "1.0" // NOTE - Set your hardware version here
|
||||
#define UMOD_BME680X_SW_VERSION "1.0.2" // NOTE - Version of the User Mod
|
||||
#define CALIB_FILE_NAME "/BME680X-Calib.hex" // NOTE - Calibration file name
|
||||
#define UMOD_NAME "BME680X" // NOTE - User module name
|
||||
#define UMOD_DEBUG_NAME "UM-BME680X: " // NOTE - Debug print module name addon
|
||||
|
||||
#define UMOD_DEVICE "ESP32" // NOTE - Set your hardware here
|
||||
#define HARDWARE_VERSION "1.0" // NOTE - Set your hardware version here
|
||||
#define UMOD_BME680X_SW_VERSION "1.0.1" // NOTE - Version of the User Mod
|
||||
#define CALIB_FILE_NAME "/BME680X-Calib.hex" // NOTE - Calibration file name
|
||||
#define UMOD_NAME "BME680X" // NOTE - User module name
|
||||
#define UMOD_DEBUG_NAME "UM-BME680X: " // NOTE - Debug print module name addon
|
||||
#define ESC "\033"
|
||||
#define ESC_CSI ESC "["
|
||||
#define ESC_STYLE_RESET ESC_CSI "0m"
|
||||
#define ESC_CURSOR_COLUMN(n) ESC_CSI #n "G"
|
||||
|
||||
/* Debug Print Text Coloring */
|
||||
#define ESC "\033"
|
||||
#define ESC_CSI ESC "["
|
||||
#define ESC_STYLE_RESET ESC_CSI "0m"
|
||||
#define ESC_CURSOR_COLUMN(n) ESC_CSI #n "G"
|
||||
#define ESC_FGCOLOR_BLACK ESC_CSI "30m"
|
||||
#define ESC_FGCOLOR_RED ESC_CSI "31m"
|
||||
#define ESC_FGCOLOR_GREEN ESC_CSI "32m"
|
||||
#define ESC_FGCOLOR_YELLOW ESC_CSI "33m"
|
||||
#define ESC_FGCOLOR_BLUE ESC_CSI "34m"
|
||||
#define ESC_FGCOLOR_MAGENTA ESC_CSI "35m"
|
||||
#define ESC_FGCOLOR_CYAN ESC_CSI "36m"
|
||||
#define ESC_FGCOLOR_WHITE ESC_CSI "37m"
|
||||
#define ESC_FGCOLOR_DEFAULT ESC_CSI "39m"
|
||||
|
||||
#define ESC_FGCOLOR_BLACK ESC_CSI "30m"
|
||||
#define ESC_FGCOLOR_RED ESC_CSI "31m"
|
||||
#define ESC_FGCOLOR_GREEN ESC_CSI "32m"
|
||||
#define ESC_FGCOLOR_YELLOW ESC_CSI "33m"
|
||||
#define ESC_FGCOLOR_BLUE ESC_CSI "34m"
|
||||
#define ESC_FGCOLOR_MAGENTA ESC_CSI "35m"
|
||||
#define ESC_FGCOLOR_CYAN ESC_CSI "36m"
|
||||
#define ESC_FGCOLOR_WHITE ESC_CSI "37m"
|
||||
#define ESC_FGCOLOR_DEFAULT ESC_CSI "39m"
|
||||
/* Debug Print Special Text */
|
||||
#define INFO_COLUMN ESC_CURSOR_COLUMN(60)
|
||||
#define GOGAB_OK INFO_COLUMN "[" ESC_FGCOLOR_GREEN "OK" ESC_STYLE_RESET "]"
|
||||
#define GOGAB_FAIL INFO_COLUMN "[" ESC_FGCOLOR_RED "FAIL" ESC_STYLE_RESET "]"
|
||||
#define GOGAB_WARN INFO_COLUMN "[" ESC_FGCOLOR_YELLOW "WARN" ESC_STYLE_RESET "]"
|
||||
#define GOGAB_DONE INFO_COLUMN "[" ESC_FGCOLOR_CYAN "DONE" ESC_STYLE_RESET "]"
|
||||
|
||||
/* Debug Print Special Text */
|
||||
#define INFO_COLUMN ESC_CURSOR_COLUMN(60)
|
||||
#define OK INFO_COLUMN "[" ESC_FGCOLOR_GREEN "OK" ESC_STYLE_RESET "]"
|
||||
#define FAIL INFO_COLUMN "[" ESC_FGCOLOR_RED "FAIL" ESC_STYLE_RESET "]"
|
||||
#define WARN INFO_COLUMN "[" ESC_FGCOLOR_YELLOW "WARN" ESC_STYLE_RESET "]"
|
||||
#define DONE INFO_COLUMN "[" ESC_FGCOLOR_CYAN "DONE" ESC_STYLE_RESET "]"
|
||||
#include "bsec.h" // Bosch sensor library
|
||||
#include "wled.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "bsec.h" // Bosch sensor library
|
||||
#include "wled.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
/* UsermodBME68X class definition */
|
||||
class UsermodBME68X : public Usermod {
|
||||
/* UsermodBME68X class definition */
|
||||
class UsermodBME68X : public Usermod {
|
||||
|
||||
public:
|
||||
/* Public: Functions */
|
||||
@ -250,85 +247,85 @@ class UsermodBME68X : public Usermod {
|
||||
|
||||
static const char _unitCelsius[];
|
||||
static const char _unitFahrenheit[];
|
||||
}; // UsermodBME68X class definition End
|
||||
}; // UsermodBME68X class definition End
|
||||
|
||||
/*** Setting C O N S T A N T S ***/
|
||||
/* Private: Settings Strings*/
|
||||
const char UsermodBME68X::_enabled[] PROGMEM = "Enabled";
|
||||
const char UsermodBME68X::_hadtopic[] PROGMEM = "homeassistant/sensor/";
|
||||
/*** Setting C O N S T A N T S ***/
|
||||
/* Private: Settings Strings*/
|
||||
const char UsermodBME68X::_enabled[] PROGMEM = "Enabled";
|
||||
const char UsermodBME68X::_hadtopic[] PROGMEM = "homeassistant/sensor/";
|
||||
|
||||
const char UsermodBME68X::_nameI2CAdr[] PROGMEM = "i2C Address";
|
||||
const char UsermodBME68X::_nameInterval[] PROGMEM = "Interval";
|
||||
const char UsermodBME68X::_nameMaxAge[] PROGMEM = "Max Age";
|
||||
const char UsermodBME68X::_namePublishChange[] PROGMEM = "Pub changes only";
|
||||
const char UsermodBME68X::_namePubAc[] PROGMEM = "Pub Accuracy";
|
||||
const char UsermodBME68X::_namePubSenState[] PROGMEM = "Pub Calib State";
|
||||
const char UsermodBME68X::_namePubAfterCalib[] PROGMEM = "Pub After Calib";
|
||||
const char UsermodBME68X::_nameTempScale[] PROGMEM = "Temp Scale";
|
||||
const char UsermodBME68X::_nameTempOffset[] PROGMEM = "Temp Offset";
|
||||
const char UsermodBME68X::_nameHADisc[] PROGMEM = "HA Discovery";
|
||||
const char UsermodBME68X::_nameDelCalib[] PROGMEM = "Del Calibration Hist";
|
||||
const char UsermodBME68X::_namePauseOnActWL[] PROGMEM = "Pause while WLED active";
|
||||
const char UsermodBME68X::_nameI2CAdr[] PROGMEM = "i2C Address";
|
||||
const char UsermodBME68X::_nameInterval[] PROGMEM = "Interval";
|
||||
const char UsermodBME68X::_nameMaxAge[] PROGMEM = "Max Age";
|
||||
const char UsermodBME68X::_namePublishChange[] PROGMEM = "Pub changes only";
|
||||
const char UsermodBME68X::_namePubAc[] PROGMEM = "Pub Accuracy";
|
||||
const char UsermodBME68X::_namePubSenState[] PROGMEM = "Pub Calib State";
|
||||
const char UsermodBME68X::_namePubAfterCalib[] PROGMEM = "Pub After Calib";
|
||||
const char UsermodBME68X::_nameTempScale[] PROGMEM = "Temp Scale";
|
||||
const char UsermodBME68X::_nameTempOffset[] PROGMEM = "Temp Offset";
|
||||
const char UsermodBME68X::_nameHADisc[] PROGMEM = "HA Discovery";
|
||||
const char UsermodBME68X::_nameDelCalib[] PROGMEM = "Del Calibration Hist";
|
||||
const char UsermodBME68X::_namePauseOnActWL[] PROGMEM = "Pause while WLED active";
|
||||
|
||||
/* Private: Sensor names / Sensor short name */
|
||||
const char UsermodBME68X::_nameTemp[] PROGMEM = "Temperature";
|
||||
const char UsermodBME68X::_nameHum[] PROGMEM = "Humidity";
|
||||
const char UsermodBME68X::_namePress[] PROGMEM = "Pressure";
|
||||
const char UsermodBME68X::_nameGasRes[] PROGMEM = "Gas-Resistance";
|
||||
const char UsermodBME68X::_nameAHum[] PROGMEM = "Absolute-Humidity";
|
||||
const char UsermodBME68X::_nameDrewP[] PROGMEM = "Drew-Point";
|
||||
const char UsermodBME68X::_nameIaq[] PROGMEM = "IAQ";
|
||||
const char UsermodBME68X::_nameIaqVerb[] PROGMEM = "IAQ-Verbal";
|
||||
const char UsermodBME68X::_nameStaticIaq[] PROGMEM = "Static-IAQ";
|
||||
const char UsermodBME68X::_nameStaticIaqVerb[] PROGMEM = "Static-IAQ-Verbal";
|
||||
const char UsermodBME68X::_nameCo2[] PROGMEM = "CO2";
|
||||
const char UsermodBME68X::_nameVoc[] PROGMEM = "VOC";
|
||||
const char UsermodBME68X::_nameGasPer[] PROGMEM = "Gas-Percentage";
|
||||
const char UsermodBME68X::_nameIaqAc[] PROGMEM = "IAQ-Accuracy";
|
||||
const char UsermodBME68X::_nameStaticIaqAc[] PROGMEM = "Static-IAQ-Accuracy";
|
||||
const char UsermodBME68X::_nameCo2Ac[] PROGMEM = "CO2-Accuracy";
|
||||
const char UsermodBME68X::_nameVocAc[] PROGMEM = "VOC-Accuracy";
|
||||
const char UsermodBME68X::_nameGasPerAc[] PROGMEM = "Gas-Percentage-Accuracy";
|
||||
const char UsermodBME68X::_nameStabStatus[] PROGMEM = "Stab-Status";
|
||||
const char UsermodBME68X::_nameRunInStatus[] PROGMEM = "Run-In-Status";
|
||||
/* Private: Sensor names / Sensor short name */
|
||||
const char UsermodBME68X::_nameTemp[] PROGMEM = "Temperature";
|
||||
const char UsermodBME68X::_nameHum[] PROGMEM = "Humidity";
|
||||
const char UsermodBME68X::_namePress[] PROGMEM = "Pressure";
|
||||
const char UsermodBME68X::_nameGasRes[] PROGMEM = "Gas-Resistance";
|
||||
const char UsermodBME68X::_nameAHum[] PROGMEM = "Absolute-Humidity";
|
||||
const char UsermodBME68X::_nameDrewP[] PROGMEM = "Drew-Point";
|
||||
const char UsermodBME68X::_nameIaq[] PROGMEM = "IAQ";
|
||||
const char UsermodBME68X::_nameIaqVerb[] PROGMEM = "IAQ-Verbal";
|
||||
const char UsermodBME68X::_nameStaticIaq[] PROGMEM = "Static-IAQ";
|
||||
const char UsermodBME68X::_nameStaticIaqVerb[] PROGMEM = "Static-IAQ-Verbal";
|
||||
const char UsermodBME68X::_nameCo2[] PROGMEM = "CO2";
|
||||
const char UsermodBME68X::_nameVoc[] PROGMEM = "VOC";
|
||||
const char UsermodBME68X::_nameGasPer[] PROGMEM = "Gas-Percentage";
|
||||
const char UsermodBME68X::_nameIaqAc[] PROGMEM = "IAQ-Accuracy";
|
||||
const char UsermodBME68X::_nameStaticIaqAc[] PROGMEM = "Static-IAQ-Accuracy";
|
||||
const char UsermodBME68X::_nameCo2Ac[] PROGMEM = "CO2-Accuracy";
|
||||
const char UsermodBME68X::_nameVocAc[] PROGMEM = "VOC-Accuracy";
|
||||
const char UsermodBME68X::_nameGasPerAc[] PROGMEM = "Gas-Percentage-Accuracy";
|
||||
const char UsermodBME68X::_nameStabStatus[] PROGMEM = "Stab-Status";
|
||||
const char UsermodBME68X::_nameRunInStatus[] PROGMEM = "Run-In-Status";
|
||||
|
||||
/* Private Units */
|
||||
const char UsermodBME68X::_unitTemp[] PROGMEM = " "; // NOTE - Is set with the selectable temperature unit
|
||||
const char UsermodBME68X::_unitHum[] PROGMEM = "%";
|
||||
const char UsermodBME68X::_unitPress[] PROGMEM = "hPa";
|
||||
const char UsermodBME68X::_unitGasres[] PROGMEM = "kΩ";
|
||||
const char UsermodBME68X::_unitAHum[] PROGMEM = "g/m³";
|
||||
const char UsermodBME68X::_unitDrewp[] PROGMEM = " "; // NOTE - Is set with the selectable temperature unit
|
||||
const char UsermodBME68X::_unitIaq[] PROGMEM = " "; // No unit
|
||||
const char UsermodBME68X::_unitStaticIaq[] PROGMEM = " "; // No unit
|
||||
const char UsermodBME68X::_unitCo2[] PROGMEM = "ppm";
|
||||
const char UsermodBME68X::_unitVoc[] PROGMEM = "ppm";
|
||||
const char UsermodBME68X::_unitGasPer[] PROGMEM = "%";
|
||||
const char UsermodBME68X::_unitNone[] PROGMEM = "";
|
||||
/* Private Units */
|
||||
const char UsermodBME68X::_unitTemp[] PROGMEM = " "; // NOTE - Is set with the selectable temperature unit
|
||||
const char UsermodBME68X::_unitHum[] PROGMEM = "%";
|
||||
const char UsermodBME68X::_unitPress[] PROGMEM = "hPa";
|
||||
const char UsermodBME68X::_unitGasres[] PROGMEM = "kΩ";
|
||||
const char UsermodBME68X::_unitAHum[] PROGMEM = "g/m³";
|
||||
const char UsermodBME68X::_unitDrewp[] PROGMEM = " "; // NOTE - Is set with the selectable temperature unit
|
||||
const char UsermodBME68X::_unitIaq[] PROGMEM = " "; // No unit
|
||||
const char UsermodBME68X::_unitStaticIaq[] PROGMEM = " "; // No unit
|
||||
const char UsermodBME68X::_unitCo2[] PROGMEM = "ppm";
|
||||
const char UsermodBME68X::_unitVoc[] PROGMEM = "ppm";
|
||||
const char UsermodBME68X::_unitGasPer[] PROGMEM = "%";
|
||||
const char UsermodBME68X::_unitNone[] PROGMEM = "";
|
||||
|
||||
const char UsermodBME68X::_unitCelsius[] PROGMEM = "°C"; // Symbol for Celsius
|
||||
const char UsermodBME68X::_unitFahrenheit[] PROGMEM = "°F"; // Symbol for Fahrenheit
|
||||
const char UsermodBME68X::_unitCelsius[] PROGMEM = "°C"; // Symbol for Celsius
|
||||
const char UsermodBME68X::_unitFahrenheit[] PROGMEM = "°F"; // Symbol for Fahrenheit
|
||||
|
||||
/* Load Sensor Settings */
|
||||
const uint8_t UsermodBME68X::bsec_config_iaq[] = {
|
||||
/* Load Sensor Settings */
|
||||
const uint8_t UsermodBME68X::bsec_config_iaq[] = {
|
||||
#include "config/generic_33v_3s_28d/bsec_iaq.txt" // Allow 28 days for calibration because the WLED module normally stays in the same place anyway
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************************************************/
|
||||
/********************************************* M A I N C O D E *********************************************/
|
||||
/************************************************************************************************************/
|
||||
/************************************************************************************************************/
|
||||
/********************************************* M A I N C O D E *********************************************/
|
||||
/************************************************************************************************************/
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Called by WLED: Setup of the usermod
|
||||
*/
|
||||
void UsermodBME68X::setup() {
|
||||
void UsermodBME68X::setup() {
|
||||
DEBUG_PRINTLN(F(UMOD_DEBUG_NAME ESC_FGCOLOR_CYAN "Initialize" ESC_STYLE_RESET));
|
||||
|
||||
/* Check, if i2c is activated */
|
||||
if (i2c_scl < 0 || i2c_sda < 0) {
|
||||
settings.enabled = false; // Disable usermod once i2c is not running
|
||||
DEBUG_PRINTLN(F(UMOD_DEBUG_NAME "I2C is not activated. Please activate I2C first." FAIL));
|
||||
DEBUG_PRINTLN(F(UMOD_DEBUG_NAME "I2C is not activated. Please activate I2C first." GOGAB_FAIL));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -352,14 +349,14 @@ void UsermodBME68X::setup() {
|
||||
loadState(); // Load the old calibration data
|
||||
checkIaqSensorStatus(); // Check the sensor status
|
||||
// HomeAssistantDiscovery();
|
||||
DEBUG_PRINTLN(F(INFO_COLUMN DONE));
|
||||
}
|
||||
DEBUG_PRINTLN(F(INFO_COLUMN GOGAB_DONE));
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Called by WLED: Main loop called by WLED
|
||||
*
|
||||
*/
|
||||
void UsermodBME68X::loop() {
|
||||
void UsermodBME68X::loop() {
|
||||
if (!settings.enabled || strip.isUpdating() || !flags.InitSuccessful) return; // Leave if not enabled or string is updating or init failed
|
||||
|
||||
if (settings.pauseOnActiveWled && strip.getBrightness()) return; // Workarround Known Issue: handing led update - Leave once pause on activ wled is active and wled is active
|
||||
@ -452,13 +449,13 @@ void UsermodBME68X::loop() {
|
||||
if (flags.SaveState) saveState(); // Save if the save state flag is set
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Retrieves the sensor data and truncates it to the requested decimal places
|
||||
*
|
||||
*/
|
||||
void UsermodBME68X::getValues() {
|
||||
void UsermodBME68X::getValues() {
|
||||
/* Swap the point to the data structures */
|
||||
swap = PrevValuesPtr;
|
||||
PrevValuesPtr = ValuesPtr;
|
||||
@ -500,27 +497,27 @@ void UsermodBME68X::getValues() {
|
||||
ValuesPtr->gasPercAccuracy = iaqSensor.gasPercentageAccuracy;
|
||||
ValuesPtr->stabStatus = iaqSensor.stabStatus;
|
||||
ValuesPtr->runInStatus = iaqSensor.runInStatus;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Sends the current sensor data via MQTT
|
||||
* @param topic Suptopic of the sensor as const char
|
||||
* @param value Current sensor value as float
|
||||
*/
|
||||
void UsermodBME68X::MQTT_publish(const char* topic, const float& value, const int8_t& dig) {
|
||||
void UsermodBME68X::MQTT_publish(const char* topic, const float& value, const int8_t& dig) {
|
||||
if (dig<0) return;
|
||||
if (WLED_MQTT_CONNECTED) {
|
||||
snprintf_P(charbuffer, 127, PSTR("%s/%s"), mqttDeviceTopic, topic);
|
||||
mqtt->publish(charbuffer, 0, false, String(value, dig).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Called by WLED: Initialize the MQTT parts when the connection to the MQTT server is established.
|
||||
* @param bool Session Present
|
||||
*/
|
||||
void UsermodBME68X::onMqttConnect(bool sessionPresent) {
|
||||
void UsermodBME68X::onMqttConnect(bool sessionPresent) {
|
||||
DEBUG_PRINTLN(UMOD_DEBUG_NAME "OnMQTTConnect event fired");
|
||||
HomeAssistantDiscovery();
|
||||
|
||||
@ -528,13 +525,13 @@ void UsermodBME68X::onMqttConnect(bool sessionPresent) {
|
||||
flags.MqttInitialized=true;
|
||||
DEBUG_PRINTLN(UMOD_DEBUG_NAME "MQTT first connect");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief MQTT initialization to generate the mqtt topic strings. This initialization also creates the HomeAssistat device configuration (HA Discovery), which home assinstant automatically evaluates to create a device.
|
||||
*/
|
||||
void UsermodBME68X::HomeAssistantDiscovery() {
|
||||
void UsermodBME68X::HomeAssistantDiscovery() {
|
||||
if (!settings.HomeAssistantDiscovery || !flags.InitSuccessful || !settings.enabled) return; // Leave once HomeAssistant Discovery is inactive
|
||||
|
||||
DEBUG_PRINTLN(UMOD_DEBUG_NAME ESC_FGCOLOR_CYAN "Creating HomeAssistant Discovery Mqtt-Entrys" ESC_STYLE_RESET);
|
||||
@ -564,10 +561,10 @@ void UsermodBME68X::HomeAssistantDiscovery() {
|
||||
MQTT_PublishHASensor(_nameStabStatus, "", _unitNone, settings.publishSensorState - 1, 1);
|
||||
MQTT_PublishHASensor(_nameRunInStatus, "", _unitNone, settings.publishSensorState - 1, 1);
|
||||
|
||||
DEBUG_PRINTLN(UMOD_DEBUG_NAME DONE);
|
||||
}
|
||||
DEBUG_PRINTLN(UMOD_DEBUG_NAME GOGAB_DONE);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief These MQTT entries are responsible for the Home Assistant Discovery of the sensors. HA is shown here where to look for the sensor data. This entry therefore only needs to be sent once.
|
||||
* Important note: In order to find everything that is sent from this device to Home Assistant via MQTT under the same device name, the "device/identifiers" entry must be the same.
|
||||
* I use the MQTT device name here. If other user mods also use the HA Discovery, it is recommended to set the identifier the same. Otherwise you would have several devices,
|
||||
@ -578,7 +575,7 @@ void UsermodBME68X::HomeAssistantDiscovery() {
|
||||
* @param digs Number of decimal places
|
||||
* @param option Set to true if the sensor is part of diagnostics (dafault 0)
|
||||
*/
|
||||
void UsermodBME68X::MQTT_PublishHASensor(const String& name, const String& deviceClass, const String& unitOfMeasurement, const int8_t& digs, const uint8_t& option) {
|
||||
void UsermodBME68X::MQTT_PublishHASensor(const String& name, const String& deviceClass, const String& unitOfMeasurement, const int8_t& digs, const uint8_t& option) {
|
||||
DEBUG_PRINT(UMOD_DEBUG_NAME "\t" + name);
|
||||
|
||||
snprintf_P(charbuffer, 127, PSTR("%s/%s"), mqttDeviceTopic, name.c_str()); // Current values will be posted here
|
||||
@ -626,13 +623,13 @@ void UsermodBME68X::MQTT_PublishHASensor(const String& name, const String& devic
|
||||
DEBUG_PRINTLN(INFO_COLUMN "published");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Called by WLED: Publish Sensor Information to Info Page
|
||||
* @param JsonObject Pointer
|
||||
*/
|
||||
void UsermodBME68X::addToJsonInfo(JsonObject& root) {
|
||||
void UsermodBME68X::addToJsonInfo(JsonObject& root) {
|
||||
//DEBUG_PRINTLN(F(UMOD_DEBUG_NAME "Add to info event"));
|
||||
JsonObject user = root[F("u")];
|
||||
|
||||
@ -678,9 +675,9 @@ void UsermodBME68X::addToJsonInfo(JsonObject& root) {
|
||||
InfoHelper(user, _nameRunInStatus, ValuesPtr->runInStatus, 0, " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Info Page helper function
|
||||
* @param root JSON object
|
||||
* @param name Name of the sensor as char
|
||||
@ -688,36 +685,36 @@ void UsermodBME68X::addToJsonInfo(JsonObject& root) {
|
||||
* @param decimals Decimal places of the value
|
||||
* @param unit Unit of the sensor
|
||||
*/
|
||||
void UsermodBME68X::InfoHelper(JsonObject& root, const char* name, const float& sensorvalue, const int8_t& decimals, const char* unit) {
|
||||
void UsermodBME68X::InfoHelper(JsonObject& root, const char* name, const float& sensorvalue, const int8_t& decimals, const char* unit) {
|
||||
if (decimals > -1) {
|
||||
JsonArray sub_json = root.createNestedArray(name);
|
||||
sub_json.add(roundf(sensorvalue * powf(10, decimals)) / powf(10, decimals));
|
||||
sub_json.add(unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Info Page helper function (overload)
|
||||
* @param root JSON object
|
||||
* @param name Name of the sensor
|
||||
* @param sensorvalue Value of the sensor as string
|
||||
* @param status Status of the value (active/inactive)
|
||||
*/
|
||||
void UsermodBME68X::InfoHelper(JsonObject& root, const char* name, const String& sensorvalue, const bool& status) {
|
||||
void UsermodBME68X::InfoHelper(JsonObject& root, const char* name, const String& sensorvalue, const bool& status) {
|
||||
if (status) {
|
||||
JsonArray sub_json = root.createNestedArray(name);
|
||||
sub_json.add(sensorvalue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Called by WLED: Adds the usermodul neends on the config page for user modules
|
||||
* @param JsonObject Pointer
|
||||
*
|
||||
* @see Usermod::addToConfig()
|
||||
* @see UsermodManager::addToConfig()
|
||||
*/
|
||||
void UsermodBME68X::addToConfig(JsonObject& root) {
|
||||
void UsermodBME68X::addToConfig(JsonObject& root) {
|
||||
DEBUG_PRINT(F(UMOD_DEBUG_NAME "Creating configuration pages content: "));
|
||||
|
||||
JsonObject top = root.createNestedObject(FPSTR(UMOD_NAME));
|
||||
@ -750,15 +747,15 @@ void UsermodBME68X::addToConfig(JsonObject& root) {
|
||||
sensors_json[FPSTR(_nameVoc)] = settings.decimals.Voc;
|
||||
sensors_json[FPSTR(_nameGasPer)] = settings.decimals.gasPerc;
|
||||
|
||||
DEBUG_PRINTLN(F(OK));
|
||||
}
|
||||
DEBUG_PRINTLN(F(GOGAB_OK));
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Called by WLED: Add dropdown and additional infos / structure
|
||||
* @see Usermod::appendConfigData()
|
||||
* @see UsermodManager::appendConfigData()
|
||||
*/
|
||||
void UsermodBME68X::appendConfigData() {
|
||||
void UsermodBME68X::appendConfigData() {
|
||||
// snprintf_P(charbuffer, 127, PSTR("addInfo('%s:%s',1,'read interval [seconds]');"), UMOD_NAME, _nameInterval); oappend(charbuffer);
|
||||
// snprintf_P(charbuffer, 127, PSTR("addInfo('%s:%s',1,'only if value changes');"), UMOD_NAME, _namePublishChange); oappend(charbuffer);
|
||||
// snprintf_P(charbuffer, 127, PSTR("addInfo('%s:%s',1,'maximum age of a message in seconds');"), UMOD_NAME, _nameMaxAge); oappend(charbuffer);
|
||||
@ -782,9 +779,9 @@ void UsermodBME68X::appendConfigData() {
|
||||
oappend(F("');"));
|
||||
oappend(F("addOption(dd,'0x76',0x76);"));
|
||||
oappend(F("addOption(dd,'0x77',0x77);"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Called by WLED: Read Usermod Config Settings default settings values could be set here (or below using the 3-argument getJsonValue())
|
||||
* instead of in the class definition or constructor setting them inside readFromConfig() is slightly more robust, handling the rare but
|
||||
* plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
|
||||
@ -797,7 +794,7 @@ void UsermodBME68X::appendConfigData() {
|
||||
* @see Usermod::readFromConfig()
|
||||
* @see UsermodManager::readFromConfig()
|
||||
*/
|
||||
bool UsermodBME68X::readFromConfig(JsonObject& root) {
|
||||
bool UsermodBME68X::readFromConfig(JsonObject& root) {
|
||||
DEBUG_PRINT(F(UMOD_DEBUG_NAME "Reading configuration: "));
|
||||
|
||||
JsonObject top = root[FPSTR(UMOD_NAME)];
|
||||
@ -831,7 +828,7 @@ bool UsermodBME68X::readFromConfig(JsonObject& root) {
|
||||
configComplete &= getJsonValue(top["Sensors"][FPSTR(_nameVoc)], settings.decimals.Voc, 0 );
|
||||
configComplete &= getJsonValue(top["Sensors"][FPSTR(_nameGasPer)], settings.decimals.gasPerc, 0 );
|
||||
|
||||
DEBUG_PRINTLN(F(OK));
|
||||
DEBUG_PRINTLN(F(GOGAB_OK));
|
||||
|
||||
/* Set the selected temperature unit */
|
||||
if (settings.tempScale) {
|
||||
@ -845,10 +842,10 @@ bool UsermodBME68X::readFromConfig(JsonObject& root) {
|
||||
DEBUG_PRINT(F(UMOD_DEBUG_NAME "Deleting Calibration File"));
|
||||
flags.DeleteCaibration = false;
|
||||
if (WLED_FS.remove(CALIB_FILE_NAME)) {
|
||||
DEBUG_PRINTLN(F(OK));
|
||||
DEBUG_PRINTLN(F(GOGAB_OK));
|
||||
}
|
||||
else {
|
||||
DEBUG_PRINTLN(F(FAIL));
|
||||
DEBUG_PRINTLN(F(GOGAB_FAIL));
|
||||
}
|
||||
}
|
||||
|
||||
@ -856,169 +853,169 @@ bool UsermodBME68X::readFromConfig(JsonObject& root) {
|
||||
iaqSensor.setTemperatureOffset(settings.tempOffset); // Set Temp Offset
|
||||
|
||||
return configComplete;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Called by WLED: Retunrs the user modul id number
|
||||
*
|
||||
* @return uint16_t User module number
|
||||
*/
|
||||
uint16_t UsermodBME68X::getId() {
|
||||
uint16_t UsermodBME68X::getId() {
|
||||
return USERMOD_ID_BME68X;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the current temperature in the scale which is choosen in settings
|
||||
* @return Temperature value (°C or °F as choosen in settings)
|
||||
*/
|
||||
inline float UsermodBME68X::getTemperature() {
|
||||
*/
|
||||
inline float UsermodBME68X::getTemperature() {
|
||||
return ValuesPtr->temperature;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the current humidity
|
||||
* @return Humididty value (%)
|
||||
*/
|
||||
inline float UsermodBME68X::getHumidity() {
|
||||
*/
|
||||
inline float UsermodBME68X::getHumidity() {
|
||||
return ValuesPtr->humidity;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the current pressure
|
||||
* @return Pressure value (hPa)
|
||||
*/
|
||||
inline float UsermodBME68X::getPressure() {
|
||||
*/
|
||||
inline float UsermodBME68X::getPressure() {
|
||||
return ValuesPtr->pressure;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the current gas resistance
|
||||
* @return Gas resistance value (kΩ)
|
||||
*/
|
||||
inline float UsermodBME68X::getGasResistance() {
|
||||
*/
|
||||
inline float UsermodBME68X::getGasResistance() {
|
||||
return ValuesPtr->gasResistance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the current absolute humidity
|
||||
* @return Absolute humidity value (g/m³)
|
||||
*/
|
||||
inline float UsermodBME68X::getAbsoluteHumidity() {
|
||||
*/
|
||||
inline float UsermodBME68X::getAbsoluteHumidity() {
|
||||
return ValuesPtr->absHumidity;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the current dew point
|
||||
* @return Dew point (°C or °F as choosen in settings)
|
||||
*/
|
||||
inline float UsermodBME68X::getDewPoint() {
|
||||
*/
|
||||
inline float UsermodBME68X::getDewPoint() {
|
||||
return ValuesPtr->drewPoint;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the current iaq (Indoor Air Quallity)
|
||||
* @return Iaq value (0-500)
|
||||
*/
|
||||
inline float UsermodBME68X::getIaq() {
|
||||
*/
|
||||
inline float UsermodBME68X::getIaq() {
|
||||
return ValuesPtr->iaq;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the current static iaq (Indoor Air Quallity) (NOTE: Static iaq is the better choice than iaq for fixed devices such as the wled module)
|
||||
* @return Static iaq value (float)
|
||||
*/
|
||||
inline float UsermodBME68X::getStaticIaq() {
|
||||
*/
|
||||
inline float UsermodBME68X::getStaticIaq() {
|
||||
return ValuesPtr->staticIaq;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the current co2
|
||||
* @return Co2 value (ppm)
|
||||
*/
|
||||
inline float UsermodBME68X::getCo2() {
|
||||
*/
|
||||
inline float UsermodBME68X::getCo2() {
|
||||
return ValuesPtr->co2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the current voc (Breath VOC concentration estimate [ppm])
|
||||
* @return Voc value (ppm)
|
||||
*/
|
||||
inline float UsermodBME68X::getVoc() {
|
||||
*/
|
||||
inline float UsermodBME68X::getVoc() {
|
||||
return ValuesPtr->Voc;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the current gas percentage
|
||||
* @return Gas percentage value (%)
|
||||
*/
|
||||
inline float UsermodBME68X::getGasPerc() {
|
||||
*/
|
||||
inline float UsermodBME68X::getGasPerc() {
|
||||
return ValuesPtr->gasPerc;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the current iaq accuracy (0 = not calibrated, 2 = being calibrated, 3 = calibrated)
|
||||
* @return Iaq accuracy value (0-3)
|
||||
*/
|
||||
inline uint8_t UsermodBME68X::getIaqAccuracy() {
|
||||
*/
|
||||
inline uint8_t UsermodBME68X::getIaqAccuracy() {
|
||||
return ValuesPtr->iaqAccuracy ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the current static iaq accuracy accuracy (0 = not calibrated, 2 = being calibrated, 3 = calibrated)
|
||||
* @return Static iaq accuracy value (0-3)
|
||||
*/
|
||||
inline uint8_t UsermodBME68X::getStaticIaqAccuracy() {
|
||||
*/
|
||||
inline uint8_t UsermodBME68X::getStaticIaqAccuracy() {
|
||||
return ValuesPtr->staticIaqAccuracy;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the current co2 accuracy (0 = not calibrated, 2 = being calibrated, 3 = calibrated)
|
||||
* @return Co2 accuracy value (0-3)
|
||||
*/
|
||||
inline uint8_t UsermodBME68X::getCo2Accuracy() {
|
||||
*/
|
||||
inline uint8_t UsermodBME68X::getCo2Accuracy() {
|
||||
return ValuesPtr->co2Accuracy;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the current voc accuracy (0 = not calibrated, 2 = being calibrated, 3 = calibrated)
|
||||
* @return Voc accuracy value (0-3)
|
||||
*/
|
||||
inline uint8_t UsermodBME68X::getVocAccuracy() {
|
||||
*/
|
||||
inline uint8_t UsermodBME68X::getVocAccuracy() {
|
||||
return ValuesPtr->VocAccuracy;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the current gas percentage accuracy (0 = not calibrated, 2 = being calibrated, 3 = calibrated)
|
||||
* @return Gas percentage accuracy value (0-3)
|
||||
*/
|
||||
inline uint8_t UsermodBME68X::getGasPercAccuracy() {
|
||||
*/
|
||||
inline uint8_t UsermodBME68X::getGasPercAccuracy() {
|
||||
return ValuesPtr->gasPercAccuracy;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the current stab status.
|
||||
* Indicates when the sensor is ready after after switch-on
|
||||
* @return stab status value (0 = switched on / 1 = stabilized)
|
||||
*/
|
||||
inline bool UsermodBME68X::getStabStatus() {
|
||||
*/
|
||||
inline bool UsermodBME68X::getStabStatus() {
|
||||
return ValuesPtr->stabStatus;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the current run in status.
|
||||
* Indicates if the sensor is undergoing initial stabilization during its first use after production
|
||||
* @return Tun status accuracy value (0 = switched on first time / 1 = stabilized)
|
||||
*/
|
||||
inline bool UsermodBME68X::getRunInStatus() {
|
||||
*/
|
||||
inline bool UsermodBME68X::getRunInStatus() {
|
||||
return ValuesPtr->runInStatus;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Checks whether the library and the sensor are running.
|
||||
*/
|
||||
void UsermodBME68X::checkIaqSensorStatus() {
|
||||
void UsermodBME68X::checkIaqSensorStatus() {
|
||||
|
||||
if (iaqSensor.bsecStatus != BSEC_OK) {
|
||||
InfoPageStatusLine = "BSEC Library ";
|
||||
@ -1026,11 +1023,11 @@ void UsermodBME68X::checkIaqSensorStatus() {
|
||||
flags.InitSuccessful = false;
|
||||
if (iaqSensor.bsecStatus < BSEC_OK) {
|
||||
InfoPageStatusLine += " Error Code : " + String(iaqSensor.bsecStatus);
|
||||
DEBUG_PRINTLN(FAIL);
|
||||
DEBUG_PRINTLN(GOGAB_FAIL);
|
||||
}
|
||||
else {
|
||||
InfoPageStatusLine += " Warning Code : " + String(iaqSensor.bsecStatus);
|
||||
DEBUG_PRINTLN(WARN);
|
||||
DEBUG_PRINTLN(GOGAB_WARN);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -1041,57 +1038,57 @@ void UsermodBME68X::checkIaqSensorStatus() {
|
||||
flags.InitSuccessful = false;
|
||||
if (iaqSensor.bme68xStatus < BME68X_OK) {
|
||||
InfoPageStatusLine += "error code: " + String(iaqSensor.bme68xStatus);
|
||||
DEBUG_PRINTLN(FAIL);
|
||||
DEBUG_PRINTLN(GOGAB_FAIL);
|
||||
}
|
||||
else {
|
||||
InfoPageStatusLine += "warning code: " + String(iaqSensor.bme68xStatus);
|
||||
DEBUG_PRINTLN(WARN);
|
||||
DEBUG_PRINTLN(GOGAB_WARN);
|
||||
}
|
||||
}
|
||||
else {
|
||||
InfoPageStatusLine += F("OK");
|
||||
DEBUG_PRINTLN(OK);
|
||||
DEBUG_PRINTLN(GOGAB_OK);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Loads the calibration data from the file system of the device
|
||||
*/
|
||||
void UsermodBME68X::loadState() {
|
||||
void UsermodBME68X::loadState() {
|
||||
if (WLED_FS.exists(CALIB_FILE_NAME)) {
|
||||
DEBUG_PRINT(F(UMOD_DEBUG_NAME "Read the calibration file: "));
|
||||
File file = WLED_FS.open(CALIB_FILE_NAME, FILE_READ);
|
||||
if (!file) {
|
||||
DEBUG_PRINTLN(FAIL);
|
||||
DEBUG_PRINTLN(GOGAB_FAIL);
|
||||
}
|
||||
else {
|
||||
file.read(bsecState, BSEC_MAX_STATE_BLOB_SIZE);
|
||||
file.close();
|
||||
DEBUG_PRINTLN(OK);
|
||||
DEBUG_PRINTLN(GOGAB_OK);
|
||||
iaqSensor.setState(bsecState);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DEBUG_PRINTLN(F(UMOD_DEBUG_NAME "Calibration file not found."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Saves the calibration data from the file system of the device
|
||||
*/
|
||||
void UsermodBME68X::saveState() {
|
||||
void UsermodBME68X::saveState() {
|
||||
DEBUG_PRINT(F(UMOD_DEBUG_NAME "Write the calibration file "));
|
||||
File file = WLED_FS.open(CALIB_FILE_NAME, FILE_WRITE);
|
||||
if (!file) {
|
||||
DEBUG_PRINTLN(FAIL);
|
||||
DEBUG_PRINTLN(GOGAB_FAIL);
|
||||
}
|
||||
else {
|
||||
iaqSensor.getState(bsecState);
|
||||
file.write(bsecState, BSEC_MAX_STATE_BLOB_SIZE);
|
||||
file.close();
|
||||
stateUpdateCounter++;
|
||||
DEBUG_PRINTF("(saved %d times)" OK "\n", stateUpdateCounter);
|
||||
DEBUG_PRINTF("(saved %d times)" GOGAB_OK "\n", stateUpdateCounter);
|
||||
flags.SaveState = false; // Clear save state flag
|
||||
|
||||
char contbuffer[30];
|
||||
@ -1110,8 +1107,8 @@ void UsermodBME68X::saveState() {
|
||||
snprintf_P(charbuffer, 127, PSTR("%s/%s"), mqttDeviceTopic, UMOD_NAME "/Calib Count");
|
||||
if (WLED_MQTT_CONNECTED) mqtt->publish(charbuffer, 0, false, contbuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static UsermodBME68X bme68x_v2;
|
||||
REGISTER_USERMOD(bme68x_v2);
|
||||
static UsermodBME68X bme68x_v2;
|
||||
REGISTER_USERMOD(bme68x_v2);
|
@ -1,53 +1,58 @@
|
||||
# Usermod BME68X
|
||||
|
||||
This usermod was developed for a BME680/BME68X sensor. The BME68X is not compatible with the BME280/BMP280 chip. It has its own library. The original 'BSEC Software Library' from Bosch was used to develop the code. The measured values are displayed on the WLED info page.
|
||||
|
||||
<p align="center"><img src="pics/pic1.png" style="width:60%;"></p>
|
||||
|
||||
In addition, the values are published on MQTT if this is active. The topic used for this is: 'wled/[MQTT Client ID]'. The Client ID is set in the WLED MQTT settings.
|
||||
|
||||
<p align="center"><img src="pics/pic2.png"></p>
|
||||
|
||||
If you use HomeAssistance discovery, the device tree for HomeAssistance is created. This is published under the topic 'homeassistant/sensor/[MQTT Client ID]' via MQTT.
|
||||
|
||||
<p align="center"><img src="pics/pic3.png"></p>
|
||||
|
||||
A device with the following sensors appears in HomeAssistant. Please note that MQTT must be activated in HomeAssistant.
|
||||
|
||||
<p align="center"><img src="pics/pic4.png" style="width:60%;"></p>
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
Raw sensor types
|
||||
|
||||
Sensor Accuracy Scale Range
|
||||
--------------------------------------------------------------------------------------------------
|
||||
Temperature +/- 1.0 °C/°F -40 to 85 °C
|
||||
Humidity +/- 3 % 0 to 100 %
|
||||
Pressure +/- 1 hPa 300 to 1100 hPa
|
||||
Gas Resistance Ohm
|
||||
Sensor Accuracy Scale Range
|
||||
-----------------------------
|
||||
|
||||
Temperature +/- 1.0 °C/°F -40 to 85 °C
|
||||
Humidity +/- 3 % 0 to 100 %
|
||||
Pressure +/- 1 hPa 300 to 1100 hPa
|
||||
Gas Resistance Ohm
|
||||
The BSEC Library calculates the following values via the gas resistance
|
||||
|
||||
Sensor Accuracy Scale Range
|
||||
--------------------------------------------------------------------------------------------------
|
||||
IAQ value between 0 and 500
|
||||
Static IAQ same as IAQ but for permanently installed devices
|
||||
CO2 PPM
|
||||
VOC PPM
|
||||
Gas-Percentage %
|
||||
|
||||
Sensor Accuracy Scale Range
|
||||
-----------------------------
|
||||
|
||||
IAQ value between 0 and 500
|
||||
Static IAQ same as IAQ but for permanently installed devices
|
||||
CO2 PPM
|
||||
VOC PPM
|
||||
Gas-Percentage %
|
||||
In addition the usermod calculates
|
||||
|
||||
Sensor Accuracy Scale Range
|
||||
--------------------------------------------------------------------------------------------------
|
||||
Absolute humidity g/m³
|
||||
Dew point °C/°F
|
||||
Sensor Accuracy Scale Range
|
||||
-----------------------------
|
||||
|
||||
Absolute humidity g/m³
|
||||
Dew point °C/°F
|
||||
|
||||
### IAQ (Indoor Air Quality)
|
||||
|
||||
The IAQ is divided into the following value groups.
|
||||
|
||||
<p align="center"><img src="pics/pic5.png"></p>
|
||||
|
||||
For more detailed information, please consult the enclosed Bosch product description (BME680.pdf).
|
||||
|
||||
|
||||
## Calibration of the device
|
||||
|
||||
The gas sensor of the BME68X must be calibrated. This differs from the BME280, which does not require any calibration.
|
||||
@ -67,10 +72,10 @@ The IAQ index is therefore only meaningful if IAQ Accuracy = 3. In addition to t
|
||||
|
||||
Reasonably reliable values are therefore only achieved when accuracy displays the value 3.
|
||||
|
||||
|
||||
|
||||
## Settings
|
||||
|
||||
The settings of the usermods are set in the usermod section of wled.
|
||||
|
||||
<p align="center"><img src="pics/pic6.png"></p>
|
||||
|
||||
The possible settings are
|
||||
@ -88,6 +93,7 @@ The possible settings are
|
||||
- **Del Calibration Hist:** If a check mark is set here, the calibration file saved in the file system is deleted when the settings are saved.
|
||||
|
||||
### Sensors
|
||||
|
||||
Applies to all sensors. The number of decimal places is set here. If the sensor is set to -1, it will no longer be published. In addition, the IAQ values can be activated here in verbal form.
|
||||
|
||||
It is recommended to use the Static IAQ for the IAQ values. This is recommended by Bosch for statically placed devices.
|
||||
@ -99,6 +105,7 @@ Data is published over MQTT - make sure you've enabled the MQTT sync interface.
|
||||
In addition to outputting via MQTT, you can read the values from the Info Screen on the dashboard page of the device's web interface.
|
||||
|
||||
Methods also exist to read the read/calculated values from other WLED modules through code.
|
||||
|
||||
- getTemperature(); The scale °C/°F is depended to the settings
|
||||
- getHumidity();
|
||||
- getPressure();
|
||||
@ -118,15 +125,36 @@ Methods also exist to read the read/calculated values from other WLED modules th
|
||||
- getStabStatus();
|
||||
- getRunInStatus();
|
||||
|
||||
## Compilation
|
||||
|
||||
To enable, compile with `BME68X` in `custom_usermods` (e.g. in `platformio_override.ini`)
|
||||
|
||||
Example:
|
||||
|
||||
```[env:esp32_mySpecial]
|
||||
extends = env:esp32dev
|
||||
custom_usermods = ${env:esp32dev.custom_usermods} BME68X
|
||||
```
|
||||
|
||||
## Revision History
|
||||
|
||||
### Version 1.0.0
|
||||
|
||||
- First version of the BME68X_v user module
|
||||
|
||||
### Version 1.0.1
|
||||
|
||||
- Rebased to WELD Version 0.15
|
||||
- Reworked some default settings
|
||||
- A problem with the default settings has been fixed
|
||||
|
||||
### Version 1.0.2
|
||||
|
||||
* Rebased to WELD Version 0.16
|
||||
* Fixed: Solved compilation problems related to some macro naming interferences.
|
||||
|
||||
## Known problems
|
||||
|
||||
- MQTT goes online at device start. Shortly afterwards it goes offline and takes quite a while until it goes online again. The problem does not come from this user module, but from the WLED core.
|
||||
- If you save the settings often, WLED can get stuck.
|
||||
- If many LEDS are connected to WLED, reading the sensor can cause a small but noticeable hang. The "Pause While WLED Active" option was introduced as a workaround.
|
||||
|
@ -1,6 +1,5 @@
|
||||
{
|
||||
"name:": "BME68X_v2",
|
||||
"build": { "libArchive": false},
|
||||
"name:": "BME68X",
|
||||
"dependencies": {
|
||||
"boschsensortec/BSEC Software Library":"^1.8.1492"
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
* Usermods allow you to add own functionality to WLED more easily
|
||||
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
|
||||
* See: https://github.com/wled-dev/WLED/wiki/Add-own-functionality
|
||||
*
|
||||
* This is an example for a v2 usermod.
|
||||
* v2 usermods are class inheritance based and can (but don't have to) implement more functions, each of them is shown in this example.
|
||||
|
@ -8,7 +8,7 @@
|
||||
* By this procedure the net services of WLED remains accessible in some problematic WLAN environments.
|
||||
*
|
||||
* Usermods allow you to add own functionality to WLED more easily
|
||||
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
|
||||
* See: https://github.com/wled-dev/WLED/wiki/Add-own-functionality
|
||||
*
|
||||
* v2 usermods are class inheritance based and can (but don't have to) implement more functions, each of them is shown in this example.
|
||||
* Multiple v2 usermods can be added to one compilation easily.
|
||||
|
4
usermods/MY9291/library.json
Normal file
4
usermods/MY9291/library.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"name:": "MY9291",
|
||||
"platforms": ["espressif8266"]
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"name:": "MY9291"
|
||||
}
|
@ -42,7 +42,7 @@
|
||||
*
|
||||
*
|
||||
* Usermods allow you to add own functionality to WLED more easily
|
||||
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
|
||||
* See: https://github.com/wled-dev/WLED/wiki/Add-own-functionality
|
||||
*
|
||||
* v2 usermods are class inheritance based and can (but don't have to) implement more functions, each of them is shown in this example.
|
||||
* Multiple v2 usermods can be added to one compilation easily.
|
||||
|
@ -24,7 +24,7 @@
|
||||
* Maintained by: @blazoncek
|
||||
*
|
||||
* Usermods allow you to add own functionality to WLED more easily
|
||||
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
|
||||
* See: https://github.com/wled-dev/WLED/wiki/Add-own-functionality
|
||||
*
|
||||
* v2 usermods are class inheritance based and can (but don't have to) implement more functions, each of them is shown in this example.
|
||||
* Multiple v2 usermods can be added to one compilation easily.
|
||||
|
@ -5,7 +5,7 @@ This usermod-v2 modification allows the connection of a PIR sensor to switch on
|
||||
_Story:_
|
||||
|
||||
I use the PIR Sensor to automatically turn on the WLED analog clock in my home office room when I am there.
|
||||
The LED strip is switched [using a relay](https://github.com/Aircoookie/WLED/wiki/Control-a-relay-with-WLED) to keep the power consumption low when it is switched off.
|
||||
The LED strip is switched [using a relay](https://kno.wled.ge/features/relay-control/) to keep the power consumption low when it is switched off.
|
||||
|
||||
## Web interface
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
/*
|
||||
* This file allows you to add own functionality to WLED more easily
|
||||
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
|
||||
* See: https://github.com/wled-dev/WLED/wiki/Add-own-functionality
|
||||
* EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in const.h)
|
||||
* bytes 2400+ are currently unused, but might be used for future wled features
|
||||
*/
|
||||
|
@ -11,11 +11,19 @@ Maintained by @blazoncek
|
||||
|
||||
## Installation
|
||||
|
||||
Copy the example `platformio_override.ini` to the root directory. This file should be placed in the same directory as `platformio.ini`.
|
||||
Add `Temperature` to `custom_usermods` in your platformio_override.ini.
|
||||
|
||||
Example **platformio_override.ini**:
|
||||
|
||||
```ini
|
||||
[env:usermod_temperature_esp32dev]
|
||||
extends = env:esp32dev
|
||||
custom_usermods = ${env:esp32dev.custom_usermods}
|
||||
Temperature
|
||||
```
|
||||
|
||||
### Define Your Options
|
||||
|
||||
* `USERMOD_DALLASTEMPERATURE` - enables this user mod wled00/usermods_list.cpp
|
||||
* `USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL` - number of milliseconds between measurements, defaults to 60000 ms (60s)
|
||||
|
||||
All parameters can be configured at runtime via the Usermods settings page, including pin, temperature in degrees Celsius or Fahrenheit and measurement interval.
|
||||
@ -25,28 +33,6 @@ All parameters can be configured at runtime via the Usermods settings page, incl
|
||||
* [QuinLED-Dig-Uno](https://quinled.info/2018/09/15/quinled-dig-uno/) - Project link
|
||||
* [Srg74-WLED-Wemos-shield](https://github.com/srg74/WLED-wemos-shield) - another great DIY WLED board
|
||||
|
||||
### PlatformIO requirements
|
||||
|
||||
If you are using `platformio_override.ini`, you should be able to refresh the task list and see your custom task, for example `env:d1_mini_usermod_dallas_temperature_C`.
|
||||
|
||||
If you are not using `platformio_override.ini`, you might have to uncomment `OneWire@~2.3.5 under` `[common]` section in `platformio.ini`:
|
||||
|
||||
```ini
|
||||
# platformio.ini
|
||||
...
|
||||
[platformio]
|
||||
...
|
||||
; default_envs = esp07
|
||||
default_envs = d1_mini
|
||||
...
|
||||
[common]
|
||||
...
|
||||
lib_deps =
|
||||
...
|
||||
#For Dallas sensor uncomment following
|
||||
paulstoffregen/OneWire @ ~2.3.8
|
||||
```
|
||||
|
||||
## Change Log
|
||||
|
||||
2020-09-12
|
||||
|
@ -6,7 +6,7 @@ Version 1.0
|
||||
|
||||
## Installation
|
||||
|
||||
Just activate the usermod with `-D USERMOD_TETRISAI` and the effect will become available under the name 'Tetris AI'. If you are running out of flash memory, use a different memory layout (e.g. [WLED_ESP32_4MB_256KB_FS.csv](https://github.com/Aircoookie/WLED/blob/main/tools/WLED_ESP32_4MB_256KB_FS.csv)).
|
||||
Just activate the usermod with `-D USERMOD_TETRISAI` and the effect will become available under the name 'Tetris AI'. If you are running out of flash memory, use a different memory layout (e.g. [WLED_ESP32_4MB_256KB_FS.csv](https://github.com/wled-dev/WLED/blob/main/tools/WLED_ESP32_4MB_256KB_FS.csv)).
|
||||
|
||||
If needed simply add to `platformio_override.ini` (or `platformio_override.ini`):
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
/*
|
||||
* Usermods allow you to add own functionality to WLED more easily
|
||||
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
|
||||
* See: https://github.com/wled-dev/WLED/wiki/Add-own-functionality
|
||||
*
|
||||
* This is an audioreactive v2 usermod.
|
||||
* ....
|
||||
@ -869,7 +869,7 @@ class AudioReactive : public Usermod {
|
||||
const int AGC_preset = (soundAgc > 0)? (soundAgc-1): 0; // make sure the _compiler_ knows this value will not change while we are inside the function
|
||||
|
||||
#ifdef WLED_DISABLE_SOUND
|
||||
micIn = inoise8(millis(), millis()); // Simulated analog read
|
||||
micIn = perlin8(millis(), millis()); // Simulated analog read
|
||||
micDataReal = micIn;
|
||||
#else
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
/*
|
||||
* Usermods allow you to add own functionality to WLED more easily
|
||||
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
|
||||
* See: https://github.com/wled-dev/WLED/wiki/Add-own-functionality
|
||||
*
|
||||
* Using a usermod:
|
||||
* 1. Copy the usermod into the sketch folder (same folder as wled00.ino)
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "wled.h"
|
||||
/*
|
||||
* This v1 usermod file allows you to add own functionality to WLED more easily
|
||||
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
|
||||
* See: https://github.com/wled-dev/WLED/wiki/Add-own-functionality
|
||||
* EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in const.h)
|
||||
* If you just need 8 bytes, use 2551-2559 (you do not need to increase EEPSIZE)
|
||||
*
|
||||
|
@ -102,7 +102,7 @@ lib_deps = ${esp32s3.lib_deps}
|
||||
# parallel. Also not clear exactly what difference between the ESP32 and the
|
||||
# ESP32S3 would be causing this, though they do run different BLE versions.
|
||||
# May be related to some of the issues discussed in:
|
||||
# https://github.com/Aircoookie/WLED/issues/1382
|
||||
# https://github.com/wled-dev/WLED/issues/1382
|
||||
; [env:esp32dev_dice]
|
||||
; extends = env:esp32dev
|
||||
; build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=ESP32
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
* Usermods allow you to add own functionality to WLED more easily
|
||||
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
|
||||
* See: https://github.com/wled-dev/WLED/wiki/Add-own-functionality
|
||||
*
|
||||
* This is Stairway-Wipe as a v2 usermod.
|
||||
*
|
||||
|
@ -37,6 +37,7 @@ Open Usermod Settings in WLED to change settings:
|
||||
No special requirements.
|
||||
|
||||
## Change Log
|
||||
|
||||
2021-07
|
||||
* Upgraded to work with the latest WLED code, and make settings configurable in Usermod Settings
|
||||
- 2021-07<br>
|
||||
Upgraded to work with the latest WLED code, and make settings configurable in Usermod Settings
|
||||
- 2025-03<br>
|
||||
Upgraded to work with the latest WLED code
|
||||
|
@ -93,9 +93,9 @@ public:
|
||||
}
|
||||
else
|
||||
{
|
||||
fastled_col.red = col[0];
|
||||
fastled_col.green = col[1];
|
||||
fastled_col.blue = col[2];
|
||||
fastled_col.red = colPri[0];
|
||||
fastled_col.green = colPri[1];
|
||||
fastled_col.blue = colPri[2];
|
||||
prim_hsv = rgb2hsv_approximate(fastled_col);
|
||||
new_val = (int16_t)prim_hsv.h + fadeAmount;
|
||||
if (new_val > 255)
|
||||
@ -104,9 +104,9 @@ public:
|
||||
new_val += 255; // roll-over if smaller than 0
|
||||
prim_hsv.h = (byte)new_val;
|
||||
hsv2rgb_rainbow(prim_hsv, fastled_col);
|
||||
col[0] = fastled_col.red;
|
||||
col[1] = fastled_col.green;
|
||||
col[2] = fastled_col.blue;
|
||||
colPri[0] = fastled_col.red;
|
||||
colPri[1] = fastled_col.green;
|
||||
colPri[2] = fastled_col.blue;
|
||||
}
|
||||
}
|
||||
else if (Enc_B == LOW)
|
||||
@ -118,9 +118,9 @@ public:
|
||||
}
|
||||
else
|
||||
{
|
||||
fastled_col.red = col[0];
|
||||
fastled_col.green = col[1];
|
||||
fastled_col.blue = col[2];
|
||||
fastled_col.red = colPri[0];
|
||||
fastled_col.green = colPri[1];
|
||||
fastled_col.blue = colPri[2];
|
||||
prim_hsv = rgb2hsv_approximate(fastled_col);
|
||||
new_val = (int16_t)prim_hsv.h - fadeAmount;
|
||||
if (new_val > 255)
|
||||
@ -129,9 +129,9 @@ public:
|
||||
new_val += 255; // roll-over if smaller than 0
|
||||
prim_hsv.h = (byte)new_val;
|
||||
hsv2rgb_rainbow(prim_hsv, fastled_col);
|
||||
col[0] = fastled_col.red;
|
||||
col[1] = fastled_col.green;
|
||||
col[2] = fastled_col.blue;
|
||||
colPri[0] = fastled_col.red;
|
||||
colPri[1] = fastled_col.green;
|
||||
colPri[2] = fastled_col.blue;
|
||||
}
|
||||
}
|
||||
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification)
|
||||
|
35
usermods/usermod_v2_brightness_follow_sun/README.md
Normal file
35
usermods/usermod_v2_brightness_follow_sun/README.md
Normal file
@ -0,0 +1,35 @@
|
||||
# Update Brightness Follow Sun
|
||||
|
||||
This UserMod can set brightness by mapping [minimum-maximum-minimum] from [sunrise-suntop-sunset], I use this UserMod to adjust the brightness of my plant growth light (pwm led), and I think it will make my plants happy.
|
||||
|
||||
This UserMod will adjust brightness from sunrise to sunset, reaching maximum brightness at the zenith of the sun. It can also maintain the lowest brightness within 0-6 hours before sunrise and after sunset according to the settings.
|
||||
|
||||
## Installation
|
||||
|
||||
define `USERMOD_BRIGHTNESS_FOLLOW_SUN` e.g. `#define USERMOD_BRIGHTNESS_FOLLOW_SUN` in my_config.h
|
||||
|
||||
or add `-D USERMOD_BRIGHTNESS_FOLLOW_SUN` to `build_flags` in platformio_override.ini
|
||||
|
||||
|
||||
### Options
|
||||
Open Usermod Settings in WLED to change settings:
|
||||
|
||||
`Enable` - When checked `Enable`, turn on the `Brightness Follow Sun` Usermod, which will automatically turn on the lights, adjust the brightness, and turn off the lights. If you need to completely turn off the lights, please unchecked `Enable`.
|
||||
|
||||
`Update Interval Sec` - The unit is seconds, and the brightness will be automatically refreshed according to the set parameters.
|
||||
|
||||
`Min Brightness` - set brightness by map of min-max-min : sunrise-suntop-sunset
|
||||
|
||||
`Max Brightness` - It needs to be set to a value greater than `Min Brightness`, otherwise it will always remain at `Min Brightness`.
|
||||
|
||||
`Relax Hour` - The unit is in hours, with an effective range of 0-6. According to the settings, maintain the lowest brightness for 0-6 hours before sunrise and after sunset.
|
||||
|
||||
|
||||
### PlatformIO requirements
|
||||
|
||||
No special requirements.
|
||||
|
||||
## Change Log
|
||||
|
||||
2025-01-02
|
||||
* init
|
@ -0,0 +1,130 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
//v2 usermod that allows to change brightness and color using a rotary encoder,
|
||||
//change between modes by pressing a button (many encoders have one included)
|
||||
class UsermodBrightnessFollowSun : public Usermod
|
||||
{
|
||||
private:
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
static const char _update_interval[];
|
||||
static const char _min_bri[];
|
||||
static const char _max_bri[];
|
||||
static const char _relax_hour[];
|
||||
|
||||
private:
|
||||
bool enabled = false; //WLEDMM
|
||||
unsigned long update_interval = 60;
|
||||
unsigned long update_interval_ms = 60000;
|
||||
int min_bri = 1;
|
||||
int max_bri = 255;
|
||||
float relax_hour = 0;
|
||||
int relaxSec = 0;
|
||||
unsigned long lastUMRun = 0;
|
||||
public:
|
||||
|
||||
void setup() {};
|
||||
|
||||
float mapFloat(float inputValue, float inMin, float inMax, float outMin, float outMax) {
|
||||
if (inMax == inMin)
|
||||
return outMin;
|
||||
|
||||
inputValue = constrain(inputValue, inMin, inMax);
|
||||
|
||||
return ((inputValue - inMin) * (outMax - outMin) / (inMax - inMin)) + outMin;
|
||||
}
|
||||
|
||||
uint16_t getId() override
|
||||
{
|
||||
return USERMOD_ID_BRIGHTNESS_FOLLOW_SUN;
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
if (sunrise == 0 || sunset == 0 || localTime == 0)
|
||||
return;
|
||||
|
||||
int curSec = elapsedSecsToday(localTime);
|
||||
int sunriseSec = elapsedSecsToday(sunrise);
|
||||
int sunsetSec = elapsedSecsToday(sunset);
|
||||
int sunMiddleSec = sunriseSec + (sunsetSec-sunriseSec)/2;
|
||||
|
||||
int relaxSecH = sunriseSec-relaxSec;
|
||||
int relaxSecE = sunsetSec+relaxSec;
|
||||
|
||||
int briSet = 0;
|
||||
if (curSec >= relaxSecH && curSec <= relaxSecE) {
|
||||
float timeMapToAngle = curSec < sunMiddleSec ?
|
||||
mapFloat(curSec, sunriseSec, sunMiddleSec, 0, M_PI/2.0) :
|
||||
mapFloat(curSec, sunMiddleSec, sunsetSec, M_PI/2.0, M_PI);
|
||||
float sinValue = sin_t(timeMapToAngle);
|
||||
briSet = min_bri + (max_bri-min_bri)*sinValue;
|
||||
}
|
||||
|
||||
bri = briSet;
|
||||
stateUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
}
|
||||
|
||||
void loop() override
|
||||
{
|
||||
if (!enabled || strip.isUpdating())
|
||||
return;
|
||||
|
||||
if (millis() - lastUMRun < update_interval_ms)
|
||||
return;
|
||||
lastUMRun = millis();
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void addToConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[FPSTR(_update_interval)] = update_interval;
|
||||
top[FPSTR(_min_bri)] = min_bri;
|
||||
top[FPSTR(_max_bri)] = max_bri;
|
||||
top[FPSTR(_relax_hour)] = relax_hour;
|
||||
}
|
||||
|
||||
bool readFromConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINTF("[%s] No config found. (Using defaults.)\n", _name);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool configComplete = true;
|
||||
|
||||
configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled, false);
|
||||
configComplete &= getJsonValue(top[FPSTR(_update_interval)], update_interval, 60);
|
||||
configComplete &= getJsonValue(top[FPSTR(_min_bri)], min_bri, 1);
|
||||
configComplete &= getJsonValue(top[FPSTR(_max_bri)], max_bri, 255);
|
||||
configComplete &= getJsonValue(top[FPSTR(_relax_hour)], relax_hour, 0);
|
||||
|
||||
update_interval = constrain(update_interval, 1, SECS_PER_HOUR);
|
||||
min_bri = constrain(min_bri, 1, 255);
|
||||
max_bri = constrain(max_bri, 1, 255);
|
||||
relax_hour = constrain(relax_hour, 0, 6);
|
||||
|
||||
update_interval_ms = update_interval*1000;
|
||||
relaxSec = SECS_PER_HOUR*relax_hour;
|
||||
|
||||
lastUMRun = 0;
|
||||
update();
|
||||
|
||||
return configComplete;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const char UsermodBrightnessFollowSun::_name[] PROGMEM = "Brightness Follow Sun";
|
||||
const char UsermodBrightnessFollowSun::_enabled[] PROGMEM = "Enabled";
|
||||
const char UsermodBrightnessFollowSun::_update_interval[] PROGMEM = "Update Interval Sec";
|
||||
const char UsermodBrightnessFollowSun::_min_bri[] PROGMEM = "Min Brightness";
|
||||
const char UsermodBrightnessFollowSun::_max_bri[] PROGMEM = "Max Brightness";
|
||||
const char UsermodBrightnessFollowSun::_relax_hour[] PROGMEM = "Relax Hour";
|
@ -516,7 +516,7 @@ void RotaryEncoderUIUsermod::setup()
|
||||
|
||||
loopTime = millis();
|
||||
|
||||
currentCCT = (approximateKelvinFromRGB(RGBW32(col[0], col[1], col[2], col[3])) - 1900) >> 5;
|
||||
currentCCT = (approximateKelvinFromRGB(RGBW32(colPri[0], colPri[1], colPri[2], colPri[3])) - 1900) >> 5;
|
||||
|
||||
if (!initDone) sortModesAndPalettes();
|
||||
|
||||
@ -918,17 +918,17 @@ void RotaryEncoderUIUsermod::changeHue(bool increase){
|
||||
display->updateRedrawTime();
|
||||
#endif
|
||||
currentHue1 = max(min((increase ? currentHue1+fadeAmount : currentHue1-fadeAmount), 255), 0);
|
||||
colorHStoRGB(currentHue1*256, currentSat1, col);
|
||||
colorHStoRGB(currentHue1*256, currentSat1, colPri);
|
||||
stateChanged = true;
|
||||
if (applyToAll) {
|
||||
for (unsigned i=0; i<strip.getSegmentsNum(); i++) {
|
||||
Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive()) continue;
|
||||
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
|
||||
seg.colors[0] = RGBW32(colPri[0], colPri[1], colPri[2], colPri[3]);
|
||||
}
|
||||
} else {
|
||||
Segment& seg = strip.getSegment(strip.getMainSegmentId());
|
||||
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
|
||||
seg.colors[0] = RGBW32(colPri[0], colPri[1], colPri[2], colPri[3]);
|
||||
}
|
||||
lampUdated();
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
@ -948,16 +948,16 @@ void RotaryEncoderUIUsermod::changeSat(bool increase){
|
||||
display->updateRedrawTime();
|
||||
#endif
|
||||
currentSat1 = max(min((increase ? currentSat1+fadeAmount : currentSat1-fadeAmount), 255), 0);
|
||||
colorHStoRGB(currentHue1*256, currentSat1, col);
|
||||
colorHStoRGB(currentHue1*256, currentSat1, colPri);
|
||||
if (applyToAll) {
|
||||
for (unsigned i=0; i<strip.getSegmentsNum(); i++) {
|
||||
Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive()) continue;
|
||||
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
|
||||
seg.colors[0] = RGBW32(colPri[0], colPri[1], colPri[2], colPri[3]);
|
||||
}
|
||||
} else {
|
||||
Segment& seg = strip.getSegment(strip.getMainSegmentId());
|
||||
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
|
||||
seg.colors[0] = RGBW32(colPri[0], colPri[1], colPri[2], colPri[3]);
|
||||
}
|
||||
lampUdated();
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
* Usermods allow you to add own functionality to WLED more easily
|
||||
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
|
||||
* See: https://github.com/wled-dev/WLED/wiki/Add-own-functionality
|
||||
*
|
||||
* This usermod can be used to drive a wordclock with a 11x10 pixel matrix with WLED. There are also 4 additional dots for the minutes.
|
||||
* The visualisation is described in 4 mask with LED numbers (single dots for minutes, minutes, hours and "clock/Uhr").
|
||||
|
3113
wled00/FX.cpp
3113
wled00/FX.cpp
File diff suppressed because it is too large
Load Diff
60
wled00/FX.h
60
wled00/FX.h
@ -1,3 +1,4 @@
|
||||
#pragma once
|
||||
/*
|
||||
WS2812FX.h - Library for WS2812 LED effects.
|
||||
Harm Aldick - 2016
|
||||
@ -8,12 +9,15 @@
|
||||
Adapted from code originally licensed under the MIT license
|
||||
|
||||
Modified for WLED
|
||||
|
||||
Segment class/struct (c) 2022 Blaz Kristan (@blazoncek)
|
||||
*/
|
||||
|
||||
#ifndef WS2812FX_h
|
||||
#define WS2812FX_h
|
||||
|
||||
#include <vector>
|
||||
#include "wled.h"
|
||||
|
||||
#include "const.h"
|
||||
#include "bus_manager.h"
|
||||
@ -74,15 +78,12 @@ extern byte realtimeMode; // used in getMappedPixelIndex()
|
||||
#define MAX_NUM_SEGMENTS 16
|
||||
/* How much data bytes all segments combined may allocate */
|
||||
#define MAX_SEGMENT_DATA 5120
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
#define MAX_NUM_SEGMENTS 20
|
||||
#define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*512) // 10k by default (S2 is short on free RAM)
|
||||
#else
|
||||
#ifndef MAX_NUM_SEGMENTS
|
||||
#define MAX_NUM_SEGMENTS 32
|
||||
#endif
|
||||
#if defined(ARDUINO_ARCH_ESP32S2)
|
||||
#define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*768) // 24k by default (S2 is short on free RAM)
|
||||
#else
|
||||
#define MAX_NUM_SEGMENTS 32 // warning: going beyond 32 may consume too much RAM for stable operation
|
||||
#define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*1280) // 40k by default
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* How much data bytes each segment should max allocate to leave enough space for other segments,
|
||||
@ -322,8 +323,37 @@ extern byte realtimeMode; // used in getMappedPixelIndex()
|
||||
#define FX_MODE_WAVESINS 184
|
||||
#define FX_MODE_ROCKTAVES 185
|
||||
#define FX_MODE_2DAKEMI 186
|
||||
|
||||
#define MODE_COUNT 187
|
||||
#define FX_MODE_PARTICLEVOLCANO 187
|
||||
#define FX_MODE_PARTICLEFIRE 188
|
||||
#define FX_MODE_PARTICLEFIREWORKS 189
|
||||
#define FX_MODE_PARTICLEVORTEX 190
|
||||
#define FX_MODE_PARTICLEPERLIN 191
|
||||
#define FX_MODE_PARTICLEPIT 192
|
||||
#define FX_MODE_PARTICLEBOX 193
|
||||
#define FX_MODE_PARTICLEATTRACTOR 194
|
||||
#define FX_MODE_PARTICLEIMPACT 195
|
||||
#define FX_MODE_PARTICLEWATERFALL 196
|
||||
#define FX_MODE_PARTICLESPRAY 197
|
||||
#define FX_MODE_PARTICLESGEQ 198
|
||||
#define FX_MODE_PARTICLECENTERGEQ 199
|
||||
#define FX_MODE_PARTICLEGHOSTRIDER 200
|
||||
#define FX_MODE_PARTICLEBLOBS 201
|
||||
#define FX_MODE_PSDRIP 202
|
||||
#define FX_MODE_PSPINBALL 203
|
||||
#define FX_MODE_PSDANCINGSHADOWS 204
|
||||
#define FX_MODE_PSFIREWORKS1D 205
|
||||
#define FX_MODE_PSSPARKLER 206
|
||||
#define FX_MODE_PSHOURGLASS 207
|
||||
#define FX_MODE_PS1DSPRAY 208
|
||||
#define FX_MODE_PSBALANCE 209
|
||||
#define FX_MODE_PSCHASE 210
|
||||
#define FX_MODE_PSSTARBURST 211
|
||||
#define FX_MODE_PS1DGEQ 212
|
||||
#define FX_MODE_PSFIRE1D 213
|
||||
#define FX_MODE_PS1DSONICSTREAM 214
|
||||
#define FX_MODE_PS1DSONICBOOM 215
|
||||
#define FX_MODE_PS1DSPRINGY 216
|
||||
#define MODE_COUNT 217
|
||||
|
||||
|
||||
#define BLEND_STYLE_FADE 0x00 // universal
|
||||
@ -480,6 +510,7 @@ typedef struct Segment {
|
||||
uint8_t _prevPaletteBlends; // number of previous palette blends (there are max 255 blends possible)
|
||||
unsigned long _start; // must accommodate millis()
|
||||
uint16_t _dur;
|
||||
// -> here is one byte of padding
|
||||
Transition(uint16_t dur=750)
|
||||
: _palT(CRGBPalette16(CRGB::Black))
|
||||
, _prevPaletteBlends(0)
|
||||
@ -571,11 +602,14 @@ typedef struct Segment {
|
||||
inline uint16_t groupLength() const { return grouping + spacing; }
|
||||
inline uint8_t getLightCapabilities() const { return _capabilities; }
|
||||
inline void deactivate() { setGeometry(0,0); }
|
||||
inline Segment &clearName() { if (name) free(name); name = nullptr; return *this; }
|
||||
inline Segment &setName(const String &name) { return setName(name.c_str()); }
|
||||
|
||||
inline static unsigned getUsedSegmentData() { return Segment::_usedSegmentData; }
|
||||
inline static void addUsedSegmentData(int len) { Segment::_usedSegmentData += len; }
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
inline static void modeBlend(bool blend) { _modeBlend = blend; }
|
||||
inline static bool getmodeBlend(void) { return _modeBlend; }
|
||||
#endif
|
||||
inline static unsigned vLength() { return Segment::_vLength; }
|
||||
inline static unsigned vWidth() { return Segment::_vWidth; }
|
||||
@ -593,6 +627,7 @@ typedef struct Segment {
|
||||
Segment &setOption(uint8_t n, bool val);
|
||||
Segment &setMode(uint8_t fx, bool loadDefaults = false);
|
||||
Segment &setPalette(uint8_t pal);
|
||||
Segment &setName(const char* name);
|
||||
uint8_t differs(const Segment& b) const;
|
||||
void refreshLightCapabilities();
|
||||
|
||||
@ -623,6 +658,7 @@ typedef struct Segment {
|
||||
uint8_t currentMode() const; // currently active effect/mode (while in transition)
|
||||
[[gnu::hot]] uint32_t currentColor(uint8_t slot) const; // currently active segment color (blended while in transition)
|
||||
CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal);
|
||||
void loadOldPalette(); // loads old FX palette into _currentPalette
|
||||
|
||||
// 1D strip
|
||||
[[gnu::hot]] uint16_t virtualLength() const;
|
||||
@ -642,8 +678,10 @@ typedef struct Segment {
|
||||
[[gnu::hot]] uint32_t getPixelColor(int i) const;
|
||||
// 1D support functions (some implement 2D as well)
|
||||
void blur(uint8_t, bool smear = false);
|
||||
void clear();
|
||||
void fill(uint32_t c);
|
||||
void fade_out(uint8_t r);
|
||||
void fadeToSecondaryBy(uint8_t fadeBy);
|
||||
void fadeToBlackBy(uint8_t fadeBy);
|
||||
inline void blendPixelColor(int n, uint32_t color, uint8_t blend) { setPixelColor(n, color_blend(getPixelColor(n), color, blend)); }
|
||||
inline void blendPixelColor(int n, CRGB c, uint8_t blend) { blendPixelColor(n, RGBW32(c.r,c.g,c.b,0), blend); }
|
||||
@ -674,7 +712,6 @@ typedef struct Segment {
|
||||
}
|
||||
#ifndef WLED_DISABLE_2D
|
||||
inline bool is2D() const { return (width()>1 && height()>1); }
|
||||
[[gnu::hot]] int XY(int x, int y) const; // support function to get relative index within segment
|
||||
[[gnu::hot]] void setPixelColorXY(int x, int y, uint32_t c) const; // set relative pixel within segment with color
|
||||
inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) const { setPixelColorXY(int(x), int(y), c); }
|
||||
inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) const { setPixelColorXY(x, y, RGBW32(r,g,b,w)); }
|
||||
@ -711,8 +748,7 @@ typedef struct Segment {
|
||||
void wu_pixel(uint32_t x, uint32_t y, CRGB c);
|
||||
inline void fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); }
|
||||
#else
|
||||
inline constexpr bool is2D() const { return false; }
|
||||
inline int XY(int x, int y) const { return x; }
|
||||
inline bool is2D() const { return false; }
|
||||
inline void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(x, c); }
|
||||
inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColor(int(x), c); }
|
||||
inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColor(x, RGBW32(r,g,b,w)); }
|
||||
|
@ -145,14 +145,6 @@ void WS2812FX::setUpMatrix() {
|
||||
|
||||
#ifndef WLED_DISABLE_2D
|
||||
|
||||
// XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element)
|
||||
int IRAM_ATTR_YN Segment::XY(int x, int y) const
|
||||
{
|
||||
const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
|
||||
const int vH = vHeight(); // segment height in logical pixels (is always >= 1)
|
||||
return isActive() ? (x%vW) + (y%vH) * vW : 0;
|
||||
}
|
||||
|
||||
// raw setColor function without checks (checks are done in setPixelColorXY())
|
||||
void IRAM_ATTR_YN Segment::_setPixelColorXY_raw(const int& x, const int& y, uint32_t& col) const
|
||||
{
|
||||
@ -166,16 +158,11 @@ void IRAM_ATTR_YN Segment::_setPixelColorXY_raw(const int& x, const int& y, uint
|
||||
|
||||
// Apply mirroring
|
||||
if (mirror || mirror_y) {
|
||||
auto setMirroredPixel = [&](int mx, int my) {
|
||||
strip.setPixelColorXY(mx, my, col);
|
||||
};
|
||||
|
||||
const int mirrorX = start + width() - x - 1;
|
||||
const int mirrorY = startY + height() - y - 1;
|
||||
|
||||
if (mirror) setMirroredPixel(transpose ? baseX : mirrorX, transpose ? mirrorY : baseY);
|
||||
if (mirror_y) setMirroredPixel(transpose ? mirrorX : baseX, transpose ? baseY : mirrorY);
|
||||
if (mirror && mirror_y) setMirroredPixel(mirrorX, mirrorY);
|
||||
if (mirror) strip.setPixelColorXY(transpose ? baseX : mirrorX, transpose ? mirrorY : baseY, col);
|
||||
if (mirror_y) strip.setPixelColorXY(transpose ? mirrorX : baseX, transpose ? baseY : mirrorY, col);
|
||||
if (mirror && mirror_y) strip.setPixelColorXY(mirrorX, mirrorY, col);
|
||||
}
|
||||
}
|
||||
|
||||
@ -697,9 +684,7 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
|
||||
case 60: bits = pgm_read_byte_near(&console_font_5x12[(chr * h) + i]); break; // 5x12 font
|
||||
default: return;
|
||||
}
|
||||
uint32_t c = ColorFromPaletteWLED(grad, (i+1)*255/h, 255, NOBLEND);
|
||||
// pre-scale color for all pixels
|
||||
c = color_fade(c, _segBri);
|
||||
CRGBW c = ColorFromPalette(grad, (i+1)*255/h, _segBri, LINEARBLEND_NOWRAP);
|
||||
_colorScaled = true;
|
||||
for (int j = 0; j<w; j++) { // character width
|
||||
int x0, y0;
|
||||
@ -712,7 +697,7 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
|
||||
}
|
||||
if (x0 < 0 || x0 >= (int)vWidth() || y0 < 0 || y0 >= (int)vHeight()) continue; // drawing off-screen
|
||||
if (((bits>>(j+(8-w))) & 0x01)) { // bit set
|
||||
setPixelColorXY(x0, y0, c);
|
||||
setPixelColorXY(x0, y0, c.color32);
|
||||
}
|
||||
}
|
||||
_colorScaled = false;
|
||||
|
@ -11,6 +11,7 @@
|
||||
*/
|
||||
#include "wled.h"
|
||||
#include "FX.h"
|
||||
#include "FXparticleSystem.h" // TODO: better define the required function (mem service) in FX.h?
|
||||
#include "palettes.h"
|
||||
|
||||
/*
|
||||
@ -269,7 +270,7 @@ void Segment::startTransition(uint16_t dur) {
|
||||
_t->_briT = on ? opacity : 0;
|
||||
_t->_cctT = cct;
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
swapSegenv(_t->_segT);
|
||||
swapSegenv(_t->_segT); // copy runtime data to temporary
|
||||
_t->_modeT = mode;
|
||||
_t->_segT._dataLenT = 0;
|
||||
_t->_segT._dataT = nullptr;
|
||||
@ -281,6 +282,13 @@ void Segment::startTransition(uint16_t dur) {
|
||||
_t->_segT._dataLenT = _dataLen;
|
||||
}
|
||||
}
|
||||
DEBUG_PRINTF_P(PSTR("-- pal: %d, bri: %d, C:[%08X,%08X,%08X], m: %d\n"),
|
||||
(int)_t->_palTid,
|
||||
(int)_t->_briT,
|
||||
_t->_segT._colorT[0],
|
||||
_t->_segT._colorT[1],
|
||||
_t->_segT._colorT[2],
|
||||
(int)_t->_modeT);
|
||||
#else
|
||||
for (size_t i=0; i<NUM_COLORS; i++) _t->_colorT[i] = colors[i];
|
||||
#endif
|
||||
@ -470,6 +478,12 @@ void Segment::beginDraw() {
|
||||
}
|
||||
}
|
||||
|
||||
// loads palette of the old FX during transitions (used by particle system)
|
||||
void Segment::loadOldPalette(void) {
|
||||
if(isInTransition())
|
||||
loadPalette(_currentPalette, _t->_palTid);
|
||||
}
|
||||
|
||||
// relies on WS2812FX::service() to call it for each frame
|
||||
void Segment::handleRandomPalette() {
|
||||
// is it time to generate a new palette?
|
||||
@ -495,21 +509,17 @@ void Segment::setGeometry(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, ui
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (Segment::maxHeight>1) boundsUnchanged &= (startY == i1Y && stopY == i2Y); // 2D
|
||||
#endif
|
||||
|
||||
if (stop && (spc > 0 || m12 != map1D2D)) clear();
|
||||
/*
|
||||
if (boundsUnchanged
|
||||
&& (!grp || (grouping == grp && spacing == spc))
|
||||
&& (ofs == UINT16_MAX || ofs == offset)
|
||||
&& (m12 == map1D2D)
|
||||
) return;
|
||||
|
||||
*/
|
||||
stateChanged = true; // send UDP/WS broadcast
|
||||
|
||||
if (stop || spc != spacing || m12 != map1D2D) {
|
||||
_vWidth = virtualWidth();
|
||||
_vHeight = virtualHeight();
|
||||
_vLength = virtualLength();
|
||||
_segBri = currentBri();
|
||||
fill(BLACK); // turn old segment range off or clears pixels if changing spacing (requires _vWidth/_vHeight/_vLength/_segBri)
|
||||
}
|
||||
if (grp) { // prevent assignment of 0
|
||||
grouping = grp;
|
||||
spacing = spc;
|
||||
@ -520,10 +530,7 @@ void Segment::setGeometry(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, ui
|
||||
if (ofs < UINT16_MAX) offset = ofs;
|
||||
map1D2D = constrain(m12, 0, 7);
|
||||
|
||||
DEBUG_PRINT(F("setUp segment: ")); DEBUG_PRINT(i1);
|
||||
DEBUG_PRINT(','); DEBUG_PRINT(i2);
|
||||
DEBUG_PRINT(F(" -> ")); DEBUG_PRINT(i1Y);
|
||||
DEBUG_PRINT(','); DEBUG_PRINTLN(i2Y);
|
||||
DEBUG_PRINTF_P(PSTR("Segment geometry: %d,%d -> %d,%d\n"), (int)i1, (int)i2, (int)i1Y, (int)i2Y);
|
||||
markForReset();
|
||||
if (boundsUnchanged) return;
|
||||
|
||||
@ -649,6 +656,20 @@ Segment &Segment::setPalette(uint8_t pal) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Segment &Segment::setName(const char *newName) {
|
||||
if (newName) {
|
||||
const int newLen = min(strlen(newName), (size_t)WLED_MAX_SEGNAME_LEN);
|
||||
if (newLen) {
|
||||
if (name) name = static_cast<char*>(realloc(name, newLen+1));
|
||||
else name = static_cast<char*>(malloc(newLen+1));
|
||||
if (name) strlcpy(name, newName, newLen+1);
|
||||
name[newLen] = 0;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
return clearName();
|
||||
}
|
||||
|
||||
// 2D matrix
|
||||
unsigned Segment::virtualWidth() const {
|
||||
unsigned groupLen = groupLength();
|
||||
@ -666,37 +687,25 @@ unsigned Segment::virtualHeight() const {
|
||||
|
||||
// Constants for mapping mode "Pinwheel"
|
||||
#ifndef WLED_DISABLE_2D
|
||||
constexpr int Pinwheel_Steps_Small = 72; // no holes up to 16x16
|
||||
constexpr int Pinwheel_Size_Small = 16; // larger than this -> use "Medium"
|
||||
constexpr int Pinwheel_Steps_Medium = 192; // no holes up to 32x32
|
||||
constexpr int Pinwheel_Size_Medium = 32; // larger than this -> use "Big"
|
||||
constexpr int Pinwheel_Steps_Big = 304; // no holes up to 50x50
|
||||
constexpr int Pinwheel_Size_Big = 50; // larger than this -> use "XL"
|
||||
constexpr int Pinwheel_Steps_XL = 368;
|
||||
constexpr float Int_to_Rad_Small = (DEG_TO_RAD * 360) / Pinwheel_Steps_Small; // conversion: from 0...72 to Radians
|
||||
constexpr float Int_to_Rad_Med = (DEG_TO_RAD * 360) / Pinwheel_Steps_Medium; // conversion: from 0...192 to Radians
|
||||
constexpr float Int_to_Rad_Big = (DEG_TO_RAD * 360) / Pinwheel_Steps_Big; // conversion: from 0...304 to Radians
|
||||
constexpr float Int_to_Rad_XL = (DEG_TO_RAD * 360) / Pinwheel_Steps_XL; // conversion: from 0...368 to Radians
|
||||
|
||||
constexpr int Fixed_Scale = 512; // fixpoint scaling factor (9bit for fraction)
|
||||
|
||||
// Pinwheel helper function: pixel index to radians
|
||||
static float getPinwheelAngle(int i, int vW, int vH) {
|
||||
int maxXY = max(vW, vH);
|
||||
if (maxXY <= Pinwheel_Size_Small) return float(i) * Int_to_Rad_Small;
|
||||
if (maxXY <= Pinwheel_Size_Medium) return float(i) * Int_to_Rad_Med;
|
||||
if (maxXY <= Pinwheel_Size_Big) return float(i) * Int_to_Rad_Big;
|
||||
// else
|
||||
return float(i) * Int_to_Rad_XL;
|
||||
}
|
||||
constexpr int Fixed_Scale = 16384; // fixpoint scaling factor (14bit for fraction)
|
||||
// Pinwheel helper function: matrix dimensions to number of rays
|
||||
static int getPinwheelLength(int vW, int vH) {
|
||||
int maxXY = max(vW, vH);
|
||||
if (maxXY <= Pinwheel_Size_Small) return Pinwheel_Steps_Small;
|
||||
if (maxXY <= Pinwheel_Size_Medium) return Pinwheel_Steps_Medium;
|
||||
if (maxXY <= Pinwheel_Size_Big) return Pinwheel_Steps_Big;
|
||||
// else
|
||||
return Pinwheel_Steps_XL;
|
||||
// Returns multiple of 8, prevents over drawing
|
||||
return (max(vW, vH) + 15) & ~7;
|
||||
}
|
||||
static void setPinwheelParameters(int i, int vW, int vH, int& startx, int& starty, int* cosVal, int* sinVal, bool getPixel = false) {
|
||||
int steps = getPinwheelLength(vW, vH);
|
||||
int baseAngle = ((0xFFFF + steps / 2) / steps); // 360° / steps, in 16 bit scale round to nearest integer
|
||||
int rotate = 0;
|
||||
if (getPixel) rotate = baseAngle / 2; // rotate by half a ray width when reading pixel color
|
||||
for (int k = 0; k < 2; k++) // angular steps for two consecutive rays
|
||||
{
|
||||
int angle = (i + k) * baseAngle + rotate;
|
||||
cosVal[k] = (cos16_t(angle) * Fixed_Scale) >> 15; // step per pixel in fixed point, cos16 output is -0x7FFF to +0x7FFF
|
||||
sinVal[k] = (sin16_t(angle) * Fixed_Scale) >> 15; // using explicit bit shifts as dividing negative numbers is not equivalent (rounding error is acceptable)
|
||||
}
|
||||
startx = (vW * Fixed_Scale) / 2; // + cosVal[0] / 4; // starting position = center + 1/4 pixel (in fixed point)
|
||||
starty = (vH * Fixed_Scale) / 2; // + sinVal[0] / 4;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -754,7 +763,7 @@ bool IRAM_ATTR_YN Segment::isPixelClipped(int i) const {
|
||||
//if (!invert && iInside) return _modeBlend;
|
||||
//if ( invert && !iInside) return _modeBlend;
|
||||
//return !_modeBlend;
|
||||
return !iInside ^ invert ^ _modeBlend; // thanks @willmmiles (https://github.com/Aircoookie/WLED/pull/3877#discussion_r1554633876)
|
||||
return !iInside ^ invert ^ _modeBlend; // thanks @willmmiles (https://github.com/wled-dev/WLED/pull/3877#discussion_r1554633876)
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
@ -832,53 +841,101 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) const
|
||||
for (int y = 0; y < i; y++) setPixelColorXY(i, y, col);
|
||||
break;
|
||||
case M12_sPinwheel: {
|
||||
// i = angle --> 0 - 296 (Big), 0 - 192 (Medium), 0 - 72 (Small)
|
||||
float centerX = roundf((vW-1) / 2.0f);
|
||||
float centerY = roundf((vH-1) / 2.0f);
|
||||
float angleRad = getPinwheelAngle(i, vW, vH); // angle in radians
|
||||
float cosVal = cos_t(angleRad);
|
||||
float sinVal = sin_t(angleRad);
|
||||
// Uses Bresenham's algorithm to place coordinates of two lines in arrays then draws between them
|
||||
int startX, startY, cosVal[2], sinVal[2]; // in fixed point scale
|
||||
setPinwheelParameters(i, vW, vH, startX, startY, cosVal, sinVal);
|
||||
|
||||
// avoid re-painting the same pixel
|
||||
int lastX = INT_MIN; // impossible position
|
||||
int lastY = INT_MIN; // impossible position
|
||||
// draw line at angle, starting at center and ending at the segment edge
|
||||
// we use fixed point math for better speed. Starting distance is 0.5 for better rounding
|
||||
// int_fast16_t and int_fast32_t types changed to int, minimum bits commented
|
||||
int posx = (centerX + 0.5f * cosVal) * Fixed_Scale; // X starting position in fixed point 18 bit
|
||||
int posy = (centerY + 0.5f * sinVal) * Fixed_Scale; // Y starting position in fixed point 18 bit
|
||||
int inc_x = cosVal * Fixed_Scale; // X increment per step (fixed point) 10 bit
|
||||
int inc_y = sinVal * Fixed_Scale; // Y increment per step (fixed point) 10 bit
|
||||
unsigned maxLineLength = max(vW, vH) + 2; // pixels drawn is always smaller than dx or dy, +1 pair for rounding errors
|
||||
uint16_t lineCoords[2][maxLineLength]; // uint16_t to save ram
|
||||
int lineLength[2] = {0};
|
||||
|
||||
int32_t maxX = vW * Fixed_Scale; // X edge in fixedpoint
|
||||
int32_t maxY = vH * Fixed_Scale; // Y edge in fixedpoint
|
||||
static int prevRays[2] = {INT_MAX, INT_MAX}; // previous two ray numbers
|
||||
int closestEdgeIdx = INT_MAX; // index of the closest edge pixel
|
||||
|
||||
// Odd rays start further from center if prevRay started at center.
|
||||
static int prevRay = INT_MIN; // previous ray number
|
||||
if ((i % 2 == 1) && (i - 1 == prevRay || i + 1 == prevRay)) {
|
||||
int jump = min(vW/3, vH/3); // can add 2 if using medium pinwheel
|
||||
posx += inc_x * jump;
|
||||
posy += inc_y * jump;
|
||||
for (int lineNr = 0; lineNr < 2; lineNr++) {
|
||||
int x0 = startX; // x, y coordinates in fixed scale
|
||||
int y0 = startY;
|
||||
int x1 = (startX + (cosVal[lineNr] << 9)); // outside of grid
|
||||
int y1 = (startY + (sinVal[lineNr] << 9)); // outside of grid
|
||||
const int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1; // x distance & step
|
||||
const int dy = -abs(y1-y0), sy = y0<y1 ? 1 : -1; // y distance & step
|
||||
uint16_t* coordinates = lineCoords[lineNr]; // 1D access is faster
|
||||
int* length = &lineLength[lineNr]; // faster access
|
||||
x0 /= Fixed_Scale; // convert to pixel coordinates
|
||||
y0 /= Fixed_Scale;
|
||||
|
||||
// Bresenham's algorithm
|
||||
int idx = 0;
|
||||
int err = dx + dy;
|
||||
while (true) {
|
||||
if (unsigned(x0) >= vW || unsigned(y0) >= vH) {
|
||||
closestEdgeIdx = min(closestEdgeIdx, idx-2);
|
||||
break; // stop if outside of grid (exploit unsigned int overflow)
|
||||
}
|
||||
prevRay = i;
|
||||
|
||||
// draw ray until we hit any edge
|
||||
while ((posx >= 0) && (posy >= 0) && (posx < maxX) && (posy < maxY)) {
|
||||
// scale down to integer (compiler will replace division with appropriate bitshift)
|
||||
int x = posx / Fixed_Scale;
|
||||
int y = posy / Fixed_Scale;
|
||||
// set pixel
|
||||
if (x != lastX || y != lastY) setPixelColorXY(x, y, col); // only paint if pixel position is different
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
// advance to next position
|
||||
posx += inc_x;
|
||||
posy += inc_y;
|
||||
coordinates[idx++] = x0;
|
||||
coordinates[idx++] = y0;
|
||||
(*length)++;
|
||||
// note: since endpoint is out of grid, no need to check if endpoint is reached
|
||||
int e2 = 2 * err;
|
||||
if (e2 >= dy) { err += dy; x0 += sx; }
|
||||
if (e2 <= dx) { err += dx; y0 += sy; }
|
||||
}
|
||||
}
|
||||
|
||||
// fill up the shorter line with missing coordinates, so block filling works correctly and efficiently
|
||||
int diff = lineLength[0] - lineLength[1];
|
||||
int longLineIdx = (diff > 0) ? 0 : 1;
|
||||
int shortLineIdx = longLineIdx ? 0 : 1;
|
||||
if (diff != 0) {
|
||||
int idx = (lineLength[shortLineIdx] - 1) * 2; // last valid coordinate index
|
||||
int lastX = lineCoords[shortLineIdx][idx++];
|
||||
int lastY = lineCoords[shortLineIdx][idx++];
|
||||
bool keepX = lastX == 0 || lastX == vW - 1;
|
||||
for (int d = 0; d < abs(diff); d++) {
|
||||
lineCoords[shortLineIdx][idx] = keepX ? lastX :lineCoords[longLineIdx][idx];
|
||||
idx++;
|
||||
lineCoords[shortLineIdx][idx] = keepX ? lineCoords[longLineIdx][idx] : lastY;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
// draw and block-fill the line coordinates. Note: block filling only efficient if angle between lines is small
|
||||
closestEdgeIdx += 2;
|
||||
int max_i = getPinwheelLength(vW, vH) - 1;
|
||||
bool drawFirst = !(prevRays[0] == i - 1 || (i == 0 && prevRays[0] == max_i)); // draw first line if previous ray was not adjacent including wrap
|
||||
bool drawLast = !(prevRays[0] == i + 1 || (i == max_i && prevRays[0] == 0)); // same as above for last line
|
||||
for (int idx = 0; idx < lineLength[longLineIdx] * 2;) { //!! should be long line idx!
|
||||
int x1 = lineCoords[0][idx];
|
||||
int x2 = lineCoords[1][idx++];
|
||||
int y1 = lineCoords[0][idx];
|
||||
int y2 = lineCoords[1][idx++];
|
||||
int minX, maxX, minY, maxY;
|
||||
(x1 < x2) ? (minX = x1, maxX = x2) : (minX = x2, maxX = x1);
|
||||
(y1 < y2) ? (minY = y1, maxY = y2) : (minY = y2, maxY = y1);
|
||||
|
||||
// fill the block between the two x,y points
|
||||
bool alwaysDraw = (drawFirst && drawLast) || // No adjacent rays, draw all pixels
|
||||
(idx > closestEdgeIdx) || // Edge pixels on uneven lines are always drawn
|
||||
(i == 0 && idx == 2) || // Center pixel special case
|
||||
(i == prevRays[1]); // Effect drawing twice in 1 frame
|
||||
for (int x = minX; x <= maxX; x++) {
|
||||
for (int y = minY; y <= maxY; y++) {
|
||||
bool onLine1 = x == x1 && y == y1;
|
||||
bool onLine2 = x == x2 && y == y2;
|
||||
if ((alwaysDraw) ||
|
||||
(!onLine1 && (!onLine2 || drawLast)) || // Middle pixels and line2 if drawLast
|
||||
(!onLine2 && (!onLine1 || drawFirst)) // Middle pixels and line1 if drawFirst
|
||||
) {
|
||||
setPixelColorXY(x, y, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
prevRays[1] = prevRays[0];
|
||||
prevRays[0] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_colorScaled = false;
|
||||
return;
|
||||
} else if (Segment::maxHeight != 1 && (width() == 1 || height() == 1)) {
|
||||
if (start < Segment::maxWidth*Segment::maxHeight) {
|
||||
@ -1011,31 +1068,17 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColor(int i) const
|
||||
break;
|
||||
case M12_sPinwheel:
|
||||
// not 100% accurate, returns pixel at outer edge
|
||||
// i = angle --> 0 - 296 (Big), 0 - 192 (Medium), 0 - 72 (Small)
|
||||
float centerX = roundf((vW-1) / 2.0f);
|
||||
float centerY = roundf((vH-1) / 2.0f);
|
||||
float angleRad = getPinwheelAngle(i, vW, vH); // angle in radians
|
||||
float cosVal = cos_t(angleRad);
|
||||
float sinVal = sin_t(angleRad);
|
||||
|
||||
int posx = (centerX + 0.5f * cosVal) * Fixed_Scale; // X starting position in fixed point 18 bit
|
||||
int posy = (centerY + 0.5f * sinVal) * Fixed_Scale; // Y starting position in fixed point 18 bit
|
||||
int inc_x = cosVal * Fixed_Scale; // X increment per step (fixed point) 10 bit
|
||||
int inc_y = sinVal * Fixed_Scale; // Y increment per step (fixed point) 10 bit
|
||||
int32_t maxX = vW * Fixed_Scale; // X edge in fixedpoint
|
||||
int32_t maxY = vH * Fixed_Scale; // Y edge in fixedpoint
|
||||
|
||||
// trace ray from center until we hit any edge - to avoid rounding problems, we use the same method as in setPixelColor
|
||||
int x = INT_MIN;
|
||||
int y = INT_MIN;
|
||||
while ((posx >= 0) && (posy >= 0) && (posx < maxX) && (posy < maxY)) {
|
||||
// scale down to integer (compiler will replace division with appropriate bitshift)
|
||||
x = posx / Fixed_Scale;
|
||||
y = posy / Fixed_Scale;
|
||||
// advance to next position
|
||||
posx += inc_x;
|
||||
posy += inc_y;
|
||||
int x, y, cosVal[2], sinVal[2];
|
||||
setPinwheelParameters(i, vW, vH, x, y, cosVal, sinVal, true);
|
||||
int maxX = (vW-1) * Fixed_Scale;
|
||||
int maxY = (vH-1) * Fixed_Scale;
|
||||
// trace ray from center until we hit any edge - to avoid rounding problems, we use fixed point coordinates
|
||||
while ((x < maxX) && (y < maxY) && (x > Fixed_Scale) && (y > Fixed_Scale)) {
|
||||
x += cosVal[0]; // advance to next position
|
||||
y += sinVal[0];
|
||||
}
|
||||
x /= Fixed_Scale;
|
||||
y /= Fixed_Scale;
|
||||
return getPixelColorXY(x, y);
|
||||
break;
|
||||
}
|
||||
@ -1117,12 +1160,9 @@ void Segment::refreshLightCapabilities() {
|
||||
}
|
||||
|
||||
for (unsigned b = 0; b < BusManager::getNumBusses(); b++) {
|
||||
Bus *bus = BusManager::getBus(b);
|
||||
if (bus == nullptr || bus->getLength()==0) break;
|
||||
if (!bus->isOk()) continue;
|
||||
if (bus->getStart() >= segStopIdx) continue;
|
||||
if (bus->getStart() + bus->getLength() <= segStartIdx) continue;
|
||||
|
||||
const Bus *bus = BusManager::getBus(b);
|
||||
if (!bus || !bus->isOk()) break;
|
||||
if (bus->getStart() >= segStopIdx || bus->getStart() + bus->getLength() <= segStartIdx) continue;
|
||||
if (bus->hasRGB() || (strip.cctFromRgb && bus->hasCCT())) capabilities |= SEG_CAPABILITY_RGB;
|
||||
if (!strip.cctFromRgb && bus->hasCCT()) capabilities |= SEG_CAPABILITY_CCT;
|
||||
if (strip.correctWB && (bus->hasRGB() || bus->hasCCT())) capabilities |= SEG_CAPABILITY_CCT; //white balance correction (CCT slider)
|
||||
@ -1138,6 +1178,26 @@ void Segment::refreshLightCapabilities() {
|
||||
_capabilities = capabilities;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fills segment with black
|
||||
*/
|
||||
void Segment::clear() {
|
||||
if (!isActive()) return; // not active
|
||||
unsigned oldVW = _vWidth;
|
||||
unsigned oldVH = _vHeight;
|
||||
unsigned oldVL = _vLength;
|
||||
unsigned oldSB = _segBri;
|
||||
_vWidth = virtualWidth();
|
||||
_vHeight = virtualHeight();
|
||||
_vLength = virtualLength();
|
||||
_segBri = currentBri();
|
||||
fill(BLACK);
|
||||
_vWidth = oldVW;
|
||||
_vHeight = oldVH;
|
||||
_vLength = oldVL;
|
||||
_segBri = oldSB;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fills segment with color
|
||||
*/
|
||||
@ -1157,42 +1217,45 @@ void Segment::fill(uint32_t c) {
|
||||
|
||||
/*
|
||||
* fade out function, higher rate = quicker fade
|
||||
* fading is highly dependant on frame rate (higher frame rates, faster fading)
|
||||
* each frame will fade at max 9% or as little as 0.8%
|
||||
*/
|
||||
void Segment::fade_out(uint8_t rate) {
|
||||
if (!isActive()) return; // not active
|
||||
const int cols = is2D() ? vWidth() : vLength();
|
||||
const int rows = vHeight(); // will be 1 for 1D
|
||||
|
||||
rate = (255-rate) >> 1;
|
||||
float mappedRate = 1.0f / (float(rate) + 1.1f);
|
||||
|
||||
uint32_t color = colors[1]; // SEGCOLOR(1); // target color
|
||||
int w2 = W(color);
|
||||
int r2 = R(color);
|
||||
int g2 = G(color);
|
||||
int b2 = B(color);
|
||||
rate = (256-rate) >> 1;
|
||||
const int mappedRate = 256 / (rate + 1);
|
||||
|
||||
for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
|
||||
color = is2D() ? getPixelColorXY(x, y) : getPixelColor(x);
|
||||
uint32_t color = is2D() ? getPixelColorXY(x, y) : getPixelColor(x);
|
||||
if (color == colors[1]) continue; // already at target color
|
||||
int w1 = W(color);
|
||||
int r1 = R(color);
|
||||
int g1 = G(color);
|
||||
int b1 = B(color);
|
||||
|
||||
int wdelta = (w2 - w1) * mappedRate;
|
||||
int rdelta = (r2 - r1) * mappedRate;
|
||||
int gdelta = (g2 - g1) * mappedRate;
|
||||
int bdelta = (b2 - b1) * mappedRate;
|
||||
|
||||
for (int i = 0; i < 32; i += 8) {
|
||||
uint8_t c2 = (colors[1]>>i); // get background channel
|
||||
uint8_t c1 = (color>>i); // get foreground channel
|
||||
// we can't use bitshift since we are using int
|
||||
int delta = (c2 - c1) * mappedRate / 256;
|
||||
// if fade isn't complete, make sure delta is at least 1 (fixes rounding issues)
|
||||
wdelta += (w2 == w1) ? 0 : (w2 > w1) ? 1 : -1;
|
||||
rdelta += (r2 == r1) ? 0 : (r2 > r1) ? 1 : -1;
|
||||
gdelta += (g2 == g1) ? 0 : (g2 > g1) ? 1 : -1;
|
||||
bdelta += (b2 == b1) ? 0 : (b2 > b1) ? 1 : -1;
|
||||
if (delta == 0) delta += (c2 == c1) ? 0 : (c2 > c1) ? 1 : -1;
|
||||
// stuff new value back into color
|
||||
color &= ~(0xFF<<i);
|
||||
color |= ((c1 + delta) & 0xFF) << i;
|
||||
}
|
||||
if (is2D()) setPixelColorXY(x, y, color);
|
||||
else setPixelColor(x, color);
|
||||
}
|
||||
}
|
||||
|
||||
if (is2D()) setPixelColorXY(x, y, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta);
|
||||
else setPixelColor(x, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta);
|
||||
// fades all pixels to secondary color
|
||||
void Segment::fadeToSecondaryBy(uint8_t fadeBy) {
|
||||
if (!isActive() || fadeBy == 0) return; // optimization - no scaling to apply
|
||||
const int cols = is2D() ? vWidth() : vLength();
|
||||
const int rows = vHeight(); // will be 1 for 1D
|
||||
|
||||
for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
|
||||
if (is2D()) setPixelColorXY(x, y, color_blend(getPixelColorXY(x,y), colors[1], fadeBy));
|
||||
else setPixelColor(x, color_blend(getPixelColor(x), colors[1], fadeBy));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1270,12 +1333,12 @@ uint32_t Segment::color_wheel(uint8_t pos) const {
|
||||
* Gets a single color from the currently selected palette.
|
||||
* @param i Palette Index (if mapping is true, the full palette will be _virtualSegmentLength long, if false, 255). Will wrap around automatically.
|
||||
* @param mapping if true, LED position in segment is considered for color
|
||||
* @param wrap FastLED palettes will usually wrap back to the start smoothly. Set false to get a hard edge
|
||||
* @param moving FastLED palettes will usually wrap back to the start smoothly. Set to true if effect has moving palette and you want wrap.
|
||||
* @param mcol If the default palette 0 is selected, return the standard color 0, 1 or 2 instead. If >2, Party palette is used instead
|
||||
* @param pbri Value to scale the brightness of the returned color by. Default is 255. (no scaling)
|
||||
* @returns Single color from palette
|
||||
*/
|
||||
uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri) const {
|
||||
uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool moving, uint8_t mcol, uint8_t pbri) const {
|
||||
uint32_t color = getCurrentColor(mcol < NUM_COLORS ? mcol : 0);
|
||||
// default palette or no RGB support on segment
|
||||
if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) {
|
||||
@ -1285,9 +1348,15 @@ uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_
|
||||
const int vL = vLength();
|
||||
unsigned paletteIndex = i;
|
||||
if (mapping && vL > 1) paletteIndex = (i*255)/(vL -1);
|
||||
// paletteBlend: 0 - wrap when moving, 1 - always wrap, 2 - never wrap, 3 - none (undefined)
|
||||
if (!wrap && strip.paletteBlend != 3) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
|
||||
CRGBW palcol = ColorFromPaletteWLED(_currentPalette, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global
|
||||
// paletteBlend: 0 - wrap when moving, 1 - always wrap, 2 - never wrap, 3 - none (undefined/no interpolation of palette entries)
|
||||
// ColorFromPalette interpolations are: NOBLEND, LINEARBLEND, LINEARBLEND_NOWRAP
|
||||
TBlendType blend = NOBLEND;
|
||||
switch (strip.paletteBlend) { // NOTE: paletteBlend should be global
|
||||
case 0: blend = moving ? LINEARBLEND : LINEARBLEND_NOWRAP; break;
|
||||
case 1: blend = LINEARBLEND; break;
|
||||
case 2: blend = LINEARBLEND_NOWRAP; break;
|
||||
}
|
||||
CRGBW palcol = ColorFromPalette(_currentPalette, paletteIndex, pbri, blend);
|
||||
palcol.w = W(color);
|
||||
|
||||
return palcol.color32;
|
||||
@ -1311,6 +1380,34 @@ void WS2812FX::finalizeInit() {
|
||||
|
||||
_hasWhiteChannel = _isOffRefreshRequired = false;
|
||||
|
||||
unsigned digitalCount = 0;
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
// determine if it is sensible to use parallel I2S outputs on ESP32 (i.e. more than 5 outputs = 1 I2S + 4 RMT)
|
||||
unsigned maxLedsOnBus = 0;
|
||||
for (const auto &bus : busConfigs) {
|
||||
if (Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type)) {
|
||||
digitalCount++;
|
||||
if (bus.count > maxLedsOnBus) maxLedsOnBus = bus.count;
|
||||
}
|
||||
}
|
||||
DEBUG_PRINTF_P(PSTR("Maximum LEDs on a bus: %u\nDigital buses: %u\n"), maxLedsOnBus, digitalCount);
|
||||
// we may remove 300 LEDs per bus limit when NeoPixelBus is updated beyond 2.9.0
|
||||
if (maxLedsOnBus <= 300 && useParallelI2S) BusManager::useParallelOutput(); // must call before creating buses
|
||||
else useParallelI2S = false; // enforce single I2S
|
||||
#endif
|
||||
|
||||
// create buses/outputs
|
||||
unsigned mem = 0;
|
||||
digitalCount = 0;
|
||||
for (const auto &bus : busConfigs) {
|
||||
mem += bus.memUsage(Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type) ? digitalCount++ : 0); // includes global buffer
|
||||
if (mem <= MAX_LED_MEMORY) {
|
||||
if (BusManager::add(bus) == -1) break;
|
||||
} else DEBUG_PRINTF_P(PSTR("Out of LED memory! Bus %d (%d) #%u not created."), (int)bus.type, (int)bus.count, digitalCount);
|
||||
}
|
||||
busConfigs.clear();
|
||||
busConfigs.shrink_to_fit();
|
||||
|
||||
//if busses failed to load, add default (fresh install, FS issue, ...)
|
||||
if (BusManager::getNumBusses() == 0) {
|
||||
DEBUG_PRINTLN(F("No busses, init default"));
|
||||
@ -1326,6 +1423,7 @@ void WS2812FX::finalizeInit() {
|
||||
|
||||
unsigned prevLen = 0;
|
||||
unsigned pinsIndex = 0;
|
||||
digitalCount = 0;
|
||||
for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
|
||||
uint8_t defPin[OUTPUT_MAX_PINS];
|
||||
// if we have less types than requested outputs and they do not align, use last known type to set current type
|
||||
@ -1390,15 +1488,16 @@ void WS2812FX::finalizeInit() {
|
||||
if (Bus::isPWM(dataType) || Bus::isOnOff(dataType)) count = 1;
|
||||
prevLen += count;
|
||||
BusConfig defCfg = BusConfig(dataType, defPin, start, count, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY, 0, useGlobalLedBuffer);
|
||||
mem += defCfg.memUsage(Bus::isDigital(dataType) && !Bus::is2Pin(dataType) ? digitalCount++ : 0);
|
||||
if (BusManager::add(defCfg) == -1) break;
|
||||
}
|
||||
}
|
||||
DEBUG_PRINTF_P(PSTR("LED buffer size: %uB/%uB\n"), mem, BusManager::memUsage());
|
||||
|
||||
_length = 0;
|
||||
for (int i=0; i<BusManager::getNumBusses(); i++) {
|
||||
Bus *bus = BusManager::getBus(i);
|
||||
if (bus == nullptr) continue;
|
||||
if (bus->getStart() + bus->getLength() > MAX_LEDS) break;
|
||||
if (!bus || !bus->isOk() || bus->getStart() + bus->getLength() > MAX_LEDS) break;
|
||||
//RGBW mode is enabled if at least one of the strips is RGBW
|
||||
_hasWhiteChannel |= bus->hasWhite();
|
||||
//refresh is required to remain off if at least one of the strips requires the refresh.
|
||||
@ -1408,7 +1507,9 @@ void WS2812FX::finalizeInit() {
|
||||
|
||||
// This must be done after all buses have been created, as some kinds (parallel I2S) interact
|
||||
bus->begin();
|
||||
bus->setBrightness(bri);
|
||||
}
|
||||
DEBUG_PRINTF_P(PSTR("Heap after buses: %d\n"), ESP.getFreeHeap());
|
||||
|
||||
Segment::maxWidth = _length;
|
||||
Segment::maxHeight = 1;
|
||||
@ -1437,7 +1538,7 @@ void WS2812FX::service() {
|
||||
_segment_index = 0;
|
||||
|
||||
for (segment &seg : _segments) {
|
||||
if (_suspend) return; // immediately stop processing segments if suspend requested during service()
|
||||
if (_suspend) break; // immediately stop processing segments if suspend requested during service()
|
||||
|
||||
// process transition (mode changes in the middle of transition)
|
||||
seg.handleTransition();
|
||||
@ -1468,6 +1569,11 @@ void WS2812FX::service() {
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
Segment::setClippingRect(0, 0); // disable clipping (just in case)
|
||||
if (seg.isInTransition()) {
|
||||
// a hack to determine if effect has changed
|
||||
uint8_t m = seg.currentMode();
|
||||
Segment::modeBlend(true); // set semaphore
|
||||
bool sameEffect = (m == seg.currentMode());
|
||||
Segment::modeBlend(false); // clear semaphore
|
||||
// set clipping rectangle
|
||||
// new mode is run inside clipping area and old mode outside clipping area
|
||||
unsigned p = seg.progress();
|
||||
@ -1476,7 +1582,20 @@ void WS2812FX::service() {
|
||||
unsigned dw = p * w / 0xFFFFU + 1;
|
||||
unsigned dh = p * h / 0xFFFFU + 1;
|
||||
unsigned orgBS = blendingStyle;
|
||||
if (w*h == 1) blendingStyle = BLEND_STYLE_FADE; // disable belending for single pixel segments (use fade instead)
|
||||
if (w*h == 1) blendingStyle = BLEND_STYLE_FADE; // disable style for single pixel segments (use fade instead)
|
||||
else if (sameEffect && (blendingStyle & BLEND_STYLE_PUSH_MASK)) {
|
||||
// when effect stays the same push will look awful, change it to swipe
|
||||
switch (blendingStyle) {
|
||||
case BLEND_STYLE_PUSH_BR:
|
||||
case BLEND_STYLE_PUSH_TR:
|
||||
case BLEND_STYLE_PUSH_RIGHT: blendingStyle = BLEND_STYLE_SWIPE_RIGHT; break;
|
||||
case BLEND_STYLE_PUSH_BL:
|
||||
case BLEND_STYLE_PUSH_TL:
|
||||
case BLEND_STYLE_PUSH_LEFT: blendingStyle = BLEND_STYLE_SWIPE_LEFT; break;
|
||||
case BLEND_STYLE_PUSH_DOWN: blendingStyle = BLEND_STYLE_SWIPE_DOWN; break;
|
||||
case BLEND_STYLE_PUSH_UP: blendingStyle = BLEND_STYLE_SWIPE_UP; break;
|
||||
}
|
||||
}
|
||||
switch (blendingStyle) {
|
||||
case BLEND_STYLE_FAIRY_DUST: // fairy dust (must set entire segment, see isPixelXYClipped())
|
||||
Segment::setClippingRect(0, w, 0, h);
|
||||
@ -1522,7 +1641,7 @@ void WS2812FX::service() {
|
||||
Segment::setClippingRect(0, dw, h - dh, h);
|
||||
break;
|
||||
}
|
||||
frameDelay = (*_mode[seg.currentMode()])(); // run new/current mode
|
||||
frameDelay = (*_mode[m])(); // run new/current mode
|
||||
// now run old/previous mode
|
||||
Segment::tmpsegd_t _tmpSegData;
|
||||
Segment::modeBlend(true); // set semaphore
|
||||
@ -1555,8 +1674,8 @@ void WS2812FX::service() {
|
||||
if (doShow) {
|
||||
yield();
|
||||
Segment::handleRandomPalette(); // slowly transition random palette; move it into for loop when each segment has individual random palette
|
||||
show();
|
||||
_lastServiceShow = nowUp; // update timestamp, for precise FPS control
|
||||
if (!_suspend) show();
|
||||
}
|
||||
#ifdef WLED_DEBUG
|
||||
if ((_targetFps != FPS_UNLIMITED) && (millis() - nowUp > _frametime)) DEBUG_PRINTF_P(PSTR("Slow strip %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
|
||||
@ -1691,8 +1810,8 @@ uint16_t WS2812FX::getLengthPhysical() const {
|
||||
//not influenced by auto-white mode, also true if white slider does not affect output white channel
|
||||
bool WS2812FX::hasRGBWBus() const {
|
||||
for (size_t b = 0; b < BusManager::getNumBusses(); b++) {
|
||||
Bus *bus = BusManager::getBus(b);
|
||||
if (bus == nullptr || bus->getLength()==0) break;
|
||||
const Bus *bus = BusManager::getBus(b);
|
||||
if (!bus || !bus->isOk()) break;
|
||||
if (bus->hasRGB() && bus->hasWhite()) return true;
|
||||
}
|
||||
return false;
|
||||
@ -1701,8 +1820,8 @@ bool WS2812FX::hasRGBWBus() const {
|
||||
bool WS2812FX::hasCCTBus() const {
|
||||
if (cctFromRgb && !correctWB) return false;
|
||||
for (size_t b = 0; b < BusManager::getNumBusses(); b++) {
|
||||
Bus *bus = BusManager::getBus(b);
|
||||
if (bus == nullptr || bus->getLength()==0) break;
|
||||
const Bus *bus = BusManager::getBus(b);
|
||||
if (!bus || !bus->isOk()) break;
|
||||
if (bus->hasCCT()) return true;
|
||||
}
|
||||
return false;
|
||||
@ -1755,10 +1874,11 @@ void WS2812FX::makeAutoSegments(bool forceReset) {
|
||||
#endif
|
||||
|
||||
for (size_t i = s; i < BusManager::getNumBusses(); i++) {
|
||||
Bus* b = BusManager::getBus(i);
|
||||
const Bus *bus = BusManager::getBus(i);
|
||||
if (!bus || !bus->isOk()) break;
|
||||
|
||||
segStarts[s] = b->getStart();
|
||||
segStops[s] = segStarts[s] + b->getLength();
|
||||
segStarts[s] = bus->getStart();
|
||||
segStops[s] = segStarts[s] + bus->getLength();
|
||||
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (isMatrix && segStops[s] <= Segment::maxWidth*Segment::maxHeight) continue; // ignore buses comprising matrix
|
||||
@ -1848,7 +1968,8 @@ bool WS2812FX::checkSegmentAlignment() const {
|
||||
bool aligned = false;
|
||||
for (const segment &seg : _segments) {
|
||||
for (unsigned b = 0; b<BusManager::getNumBusses(); b++) {
|
||||
Bus *bus = BusManager::getBus(b);
|
||||
const Bus *bus = BusManager::getBus(b);
|
||||
if (!bus || !bus->isOk()) break;
|
||||
if (seg.start == bus->getStart() && seg.stop == bus->getStart() + bus->getLength()) aligned = true;
|
||||
}
|
||||
if (seg.start == 0 && seg.stop == _length) aligned = true;
|
||||
@ -1945,12 +2066,17 @@ bool WS2812FX::deserializeMap(unsigned n) {
|
||||
|
||||
if (!isFile || !requestJSONBufferLock(7)) return false;
|
||||
|
||||
if (!readObjectFromFile(fileName, nullptr, pDoc)) {
|
||||
StaticJsonDocument<64> filter;
|
||||
filter[F("width")] = true;
|
||||
filter[F("height")] = true;
|
||||
if (!readObjectFromFile(fileName, nullptr, pDoc, &filter)) {
|
||||
DEBUG_PRINT(F("ERROR Invalid ledmap in ")); DEBUG_PRINTLN(fileName);
|
||||
releaseJSONBufferLock();
|
||||
return false; // if file does not load properly then exit
|
||||
}
|
||||
|
||||
suspend();
|
||||
|
||||
JsonObject root = pDoc->as<JsonObject>();
|
||||
// if we are loading default ledmap (at boot) set matrix width and height from the ledmap (compatible with WLED MM ledmaps)
|
||||
if (isMatrix && n == 0 && (!root[F("width")].isNull() || !root[F("height")].isNull())) {
|
||||
@ -1963,16 +2089,52 @@ bool WS2812FX::deserializeMap(unsigned n) {
|
||||
|
||||
if (customMappingTable) {
|
||||
DEBUG_PRINT(F("Reading LED map from ")); DEBUG_PRINTLN(fileName);
|
||||
File f = WLED_FS.open(fileName, "r");
|
||||
f.find("\"map\":[");
|
||||
while (f.available()) { // f.position() < f.size() - 1
|
||||
char number[32];
|
||||
size_t numRead = f.readBytesUntil(',', number, sizeof(number)-1); // read a single number (may include array terminating "]" but not number separator ',')
|
||||
number[numRead] = 0;
|
||||
if (numRead > 0) {
|
||||
char *end = strchr(number,']'); // we encountered end of array so stop processing if no digit found
|
||||
bool foundDigit = (end == nullptr);
|
||||
int i = 0;
|
||||
if (end != nullptr) do {
|
||||
if (number[i] >= '0' && number[i] <= '9') foundDigit = true;
|
||||
if (foundDigit || &number[i++] == end) break;
|
||||
} while (i < 32);
|
||||
if (!foundDigit) break;
|
||||
int index = atoi(number);
|
||||
if (index < 0 || index > 16384) index = 0xFFFF;
|
||||
customMappingTable[customMappingSize++] = index;
|
||||
if (customMappingSize > getLengthTotal()) break;
|
||||
} else break; // there was nothing to read, stop
|
||||
}
|
||||
currentLedmap = n;
|
||||
f.close();
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
DEBUG_PRINT(F("Loaded ledmap:"));
|
||||
for (unsigned i=0; i<customMappingSize; i++) {
|
||||
if (!(i%Segment::maxWidth)) DEBUG_PRINTLN();
|
||||
DEBUG_PRINTF_P(PSTR("%4d,"), customMappingTable[i]);
|
||||
}
|
||||
DEBUG_PRINTLN();
|
||||
#endif
|
||||
/*
|
||||
JsonArray map = root[F("map")];
|
||||
if (!map.isNull() && map.size()) { // not an empty map
|
||||
customMappingSize = min((unsigned)map.size(), (unsigned)getLengthTotal());
|
||||
for (unsigned i=0; i<customMappingSize; i++) customMappingTable[i] = (uint16_t) (map[i]<0 ? 0xFFFFU : map[i]);
|
||||
currentLedmap = n;
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
DEBUG_PRINTLN(F("ERROR LED map allocation error."));
|
||||
}
|
||||
|
||||
resume();
|
||||
|
||||
releaseJSONBufferLock();
|
||||
return (customMappingSize > 0);
|
||||
}
|
||||
|
1931
wled00/FXparticleSystem.cpp
Normal file
1931
wled00/FXparticleSystem.cpp
Normal file
File diff suppressed because it is too large
Load Diff
390
wled00/FXparticleSystem.h
Normal file
390
wled00/FXparticleSystem.h
Normal file
@ -0,0 +1,390 @@
|
||||
/*
|
||||
FXparticleSystem.cpp
|
||||
|
||||
Particle system with functions for particle generation, particle movement and particle rendering to RGB matrix.
|
||||
by DedeHai (Damian Schneider) 2013-2024
|
||||
|
||||
Copyright (c) 2024 Damian Schneider
|
||||
Licensed under the EUPL v. 1.2 or later
|
||||
*/
|
||||
|
||||
#ifdef WLED_DISABLE_2D
|
||||
#define WLED_DISABLE_PARTICLESYSTEM2D
|
||||
#endif
|
||||
|
||||
#if !(defined(WLED_DISABLE_PARTICLESYSTEM2D) && defined(WLED_DISABLE_PARTICLESYSTEM1D)) // not both disabled
|
||||
|
||||
#include <stdint.h>
|
||||
#include "wled.h"
|
||||
|
||||
#define PS_P_MAXSPEED 120 // maximum speed a particle can have (vx/vy is int8)
|
||||
#define MAX_MEMIDLE 10 // max idle time (in frames) before memory is deallocated (if deallocated during an effect, it will crash!)
|
||||
|
||||
//#define WLED_DEBUG_PS // note: enabling debug uses ~3k of flash
|
||||
|
||||
#ifdef WLED_DEBUG_PS
|
||||
#define PSPRINT(x) Serial.print(x)
|
||||
#define PSPRINTLN(x) Serial.println(x)
|
||||
#else
|
||||
#define PSPRINT(x)
|
||||
#define PSPRINTLN(x)
|
||||
#endif
|
||||
|
||||
// limit speed of particles (used in 1D and 2D)
|
||||
static inline int32_t limitSpeed(const int32_t speed) {
|
||||
return speed > PS_P_MAXSPEED ? PS_P_MAXSPEED : (speed < -PS_P_MAXSPEED ? -PS_P_MAXSPEED : speed); // note: this is slightly faster than using min/max at the cost of 50bytes of flash
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef WLED_DISABLE_PARTICLESYSTEM2D
|
||||
// memory allocation
|
||||
#define ESP8266_MAXPARTICLES 256 // enough up to 16x16 pixels
|
||||
#define ESP8266_MAXSOURCES 24
|
||||
#define ESP32S2_MAXPARTICLES 1024 // enough up to 32x32 pixels
|
||||
#define ESP32S2_MAXSOURCES 64
|
||||
#define ESP32_MAXPARTICLES 2048 // enough up to 64x32 pixels
|
||||
#define ESP32_MAXSOURCES 128
|
||||
|
||||
// particle dimensions (subpixel division)
|
||||
#define PS_P_RADIUS 64 // subpixel size, each pixel is divided by this for particle movement (must be a power of 2)
|
||||
#define PS_P_HALFRADIUS (PS_P_RADIUS >> 1)
|
||||
#define PS_P_RADIUS_SHIFT 6 // shift for RADIUS
|
||||
#define PS_P_SURFACE 12 // shift: 2^PS_P_SURFACE = (PS_P_RADIUS)^2
|
||||
#define PS_P_MINHARDRADIUS 64 // minimum hard surface radius for collisions
|
||||
#define PS_P_MINSURFACEHARDNESS 128 // minimum hardness used in collision impulse calculation, below this hardness, particles become sticky
|
||||
|
||||
// struct for PS settings (shared for 1D and 2D class)
|
||||
typedef union {
|
||||
struct{ // one byte bit field for 2D settings
|
||||
bool wrapX : 1;
|
||||
bool wrapY : 1;
|
||||
bool bounceX : 1;
|
||||
bool bounceY : 1;
|
||||
bool killoutofbounds : 1; // if set, out of bound particles are killed immediately
|
||||
bool useGravity : 1; // set to 1 if gravity is used, disables bounceY at the top
|
||||
bool useCollisions : 1;
|
||||
bool colorByAge : 1; // if set, particle hue is set by ttl value in render function
|
||||
};
|
||||
byte asByte; // access as a byte, order is: LSB is first entry in the list above
|
||||
} PSsettings2D;
|
||||
|
||||
//struct for a single particle
|
||||
typedef struct { // 10 bytes
|
||||
int16_t x; // x position in particle system
|
||||
int16_t y; // y position in particle system
|
||||
uint16_t ttl; // time to live in frames
|
||||
int8_t vx; // horizontal velocity
|
||||
int8_t vy; // vertical velocity
|
||||
uint8_t hue; // color hue
|
||||
uint8_t sat; // particle color saturation
|
||||
} PSparticle;
|
||||
|
||||
//struct for particle flags note: this is separate from the particle struct to save memory (ram alignment)
|
||||
typedef union {
|
||||
struct { // 1 byte
|
||||
bool outofbounds : 1; // out of bounds flag, set to true if particle is outside of display area
|
||||
bool collide : 1; // if set, particle takes part in collisions
|
||||
bool perpetual : 1; // if set, particle does not age (TTL is not decremented in move function, it still dies from killoutofbounds)
|
||||
bool custom1 : 1; // unused custom flags, can be used by FX to track particle states
|
||||
bool custom2 : 1;
|
||||
bool custom3 : 1;
|
||||
bool custom4 : 1;
|
||||
bool custom5 : 1;
|
||||
};
|
||||
byte asByte; // access as a byte, order is: LSB is first entry in the list above
|
||||
} PSparticleFlags;
|
||||
|
||||
// struct for additional particle settings (option)
|
||||
typedef struct { // 2 bytes
|
||||
uint8_t size; // particle size, 255 means 10 pixels in diameter
|
||||
uint8_t forcecounter; // counter for applying forces to individual particles
|
||||
} PSadvancedParticle;
|
||||
|
||||
// struct for advanced particle size control (option)
|
||||
typedef struct { // 8 bytes
|
||||
uint8_t asymmetry; // asymmetrical size (0=symmetrical, 255 fully asymmetric)
|
||||
uint8_t asymdir; // direction of asymmetry, 64 is x, 192 is y (0 and 128 is symmetrical)
|
||||
uint8_t maxsize; // target size for growing
|
||||
uint8_t minsize; // target size for shrinking
|
||||
uint8_t sizecounter : 4; // counters used for size contol (grow/shrink/wobble)
|
||||
uint8_t wobblecounter : 4;
|
||||
uint8_t growspeed : 4;
|
||||
uint8_t shrinkspeed : 4;
|
||||
uint8_t wobblespeed : 4;
|
||||
bool grow : 1; // flags
|
||||
bool shrink : 1;
|
||||
bool pulsate : 1; // grows & shrinks & grows & ...
|
||||
bool wobble : 1; // alternate x and y size
|
||||
} PSsizeControl;
|
||||
|
||||
|
||||
//struct for a particle source (20 bytes)
|
||||
typedef struct {
|
||||
uint16_t minLife; // minimum ttl of emittet particles
|
||||
uint16_t maxLife; // maximum ttl of emitted particles
|
||||
PSparticle source; // use a particle as the emitter source (speed, position, color)
|
||||
PSparticleFlags sourceFlags; // flags for the source particle
|
||||
int8_t var; // variation of emitted speed (adds random(+/- var) to speed)
|
||||
int8_t vx; // emitting speed
|
||||
int8_t vy;
|
||||
uint8_t size; // particle size (advanced property)
|
||||
} PSsource;
|
||||
|
||||
// class uses approximately 60 bytes
|
||||
class ParticleSystem2D {
|
||||
public:
|
||||
ParticleSystem2D(const uint32_t width, const uint32_t height, const uint32_t numberofparticles, const uint32_t numberofsources, const bool isadvanced = false, const bool sizecontrol = false); // constructor
|
||||
// note: memory is allcated in the FX function, no deconstructor needed
|
||||
void update(void); //update the particles according to set options and render to the matrix
|
||||
void updateFire(const uint8_t intensity, const bool renderonly); // update function for fire, if renderonly is set, particles are not updated (required to fix transitions with frameskips)
|
||||
void updateSystem(void); // call at the beginning of every FX, updates pointers and dimensions
|
||||
void particleMoveUpdate(PSparticle &part, PSparticleFlags &partFlags, PSsettings2D *options = NULL, PSadvancedParticle *advancedproperties = NULL); // move function
|
||||
// particle emitters
|
||||
int32_t sprayEmit(const PSsource &emitter);
|
||||
void flameEmit(const PSsource &emitter);
|
||||
int32_t angleEmit(PSsource& emitter, const uint16_t angle, const int32_t speed);
|
||||
//particle physics
|
||||
void applyGravity(PSparticle &part); // applies gravity to single particle (use this for sources)
|
||||
[[gnu::hot]] void applyForce(PSparticle &part, const int8_t xforce, const int8_t yforce, uint8_t &counter);
|
||||
[[gnu::hot]] void applyForce(const uint32_t particleindex, const int8_t xforce, const int8_t yforce); // use this for advanced property particles
|
||||
void applyForce(const int8_t xforce, const int8_t yforce); // apply a force to all particles
|
||||
void applyAngleForce(PSparticle &part, const int8_t force, const uint16_t angle, uint8_t &counter);
|
||||
void applyAngleForce(const uint32_t particleindex, const int8_t force, const uint16_t angle); // use this for advanced property particles
|
||||
void applyAngleForce(const int8_t force, const uint16_t angle); // apply angular force to all particles
|
||||
void applyFriction(PSparticle &part, const int32_t coefficient); // apply friction to specific particle
|
||||
void applyFriction(const int32_t coefficient); // apply friction to all used particles
|
||||
void pointAttractor(const uint32_t particleindex, PSparticle &attractor, const uint8_t strength, const bool swallow);
|
||||
// set options note: inlining the set function uses more flash so dont optimize
|
||||
void setUsedParticles(const uint8_t percentage); // set the percentage of particles used in the system, 255=100%
|
||||
void setCollisionHardness(const uint8_t hardness); // hardness for particle collisions (255 means full hard)
|
||||
void setWallHardness(const uint8_t hardness); // hardness for bouncing on the wall if bounceXY is set
|
||||
void setWallRoughness(const uint8_t roughness); // wall roughness randomizes wall collisions
|
||||
void setMatrixSize(const uint32_t x, const uint32_t y);
|
||||
void setWrapX(const bool enable);
|
||||
void setWrapY(const bool enable);
|
||||
void setBounceX(const bool enable);
|
||||
void setBounceY(const bool enable);
|
||||
void setKillOutOfBounds(const bool enable); // if enabled, particles outside of matrix instantly die
|
||||
void setSaturation(const uint8_t sat); // set global color saturation
|
||||
void setColorByAge(const bool enable);
|
||||
void setMotionBlur(const uint8_t bluramount); // note: motion blur can only be used if 'particlesize' is set to zero
|
||||
void setSmearBlur(const uint8_t bluramount); // enable 2D smeared blurring of full frame
|
||||
void setParticleSize(const uint8_t size);
|
||||
void setGravity(const int8_t force = 8);
|
||||
void enableParticleCollisions(const bool enable, const uint8_t hardness = 255);
|
||||
|
||||
PSparticle *particles; // pointer to particle array
|
||||
PSparticleFlags *particleFlags; // pointer to particle flags array
|
||||
PSsource *sources; // pointer to sources
|
||||
PSadvancedParticle *advPartProps; // pointer to advanced particle properties (can be NULL)
|
||||
PSsizeControl *advPartSize; // pointer to advanced particle size control (can be NULL)
|
||||
uint8_t* PSdataEnd; // points to first available byte after the PSmemory, is set in setPointers(). use this for FX custom data
|
||||
int32_t maxX, maxY; // particle system size i.e. width-1 / height-1 in subpixels, Note: all "max" variables must be signed to compare to coordinates (which are signed)
|
||||
int32_t maxXpixel, maxYpixel; // last physical pixel that can be drawn to (FX can read this to read segment size if required), equal to width-1 / height-1
|
||||
uint32_t numSources; // number of sources
|
||||
uint32_t usedParticles; // number of particles used in animation, is relative to 'numParticles'
|
||||
//note: some variables are 32bit for speed and code size at the cost of ram
|
||||
|
||||
private:
|
||||
//rendering functions
|
||||
void render();
|
||||
[[gnu::hot]] void renderParticle(const uint32_t particleindex, const uint8_t brightness, const CRGB& color, const bool wrapX, const bool wrapY);
|
||||
//paricle physics applied by system if flags are set
|
||||
void applyGravity(); // applies gravity to all particles
|
||||
void handleCollisions();
|
||||
[[gnu::hot]] void collideParticles(PSparticle &particle1, PSparticle &particle2, const int32_t dx, const int32_t dy, const uint32_t collDistSq);
|
||||
void fireParticleupdate();
|
||||
//utility functions
|
||||
void updatePSpointers(const bool isadvanced, const bool sizecontrol); // update the data pointers to current segment data space
|
||||
bool updateSize(PSadvancedParticle *advprops, PSsizeControl *advsize); // advanced size control
|
||||
void getParticleXYsize(PSadvancedParticle *advprops, PSsizeControl *advsize, uint32_t &xsize, uint32_t &ysize);
|
||||
[[gnu::hot]] void bounce(int8_t &incomingspeed, int8_t ¶llelspeed, int32_t &position, const uint32_t maxposition); // bounce on a wall
|
||||
// note: variables that are accessed often are 32bit for speed
|
||||
CRGB *framebuffer; // local frame buffer for rendering
|
||||
PSsettings2D particlesettings; // settings used when updating particles (can also used by FX to move sources), do not edit properties directly, use functions above
|
||||
uint32_t numParticles; // total number of particles allocated by this system
|
||||
uint32_t emitIndex; // index to count through particles to emit so searching for dead pixels is faster
|
||||
int32_t collisionHardness;
|
||||
uint32_t wallHardness;
|
||||
uint32_t wallRoughness; // randomizes wall collisions
|
||||
uint32_t particleHardRadius; // hard surface radius of a particle, used for collision detection (32bit for speed)
|
||||
uint16_t collisionStartIdx; // particle array start index for collision detection
|
||||
uint8_t fireIntesity = 0; // fire intensity, used for fire mode (flash use optimization, better than passing an argument to render function)
|
||||
uint8_t forcecounter; // counter for globally applied forces
|
||||
uint8_t gforcecounter; // counter for global gravity
|
||||
int8_t gforce; // gravity strength, default is 8 (negative is allowed, positive is downwards)
|
||||
// global particle properties for basic particles
|
||||
uint8_t particlesize; // global particle size, 0 = 1 pixel, 1 = 2 pixels, 255 = 10 pixels (note: this is also added to individual sized particles)
|
||||
uint8_t motionBlur; // motion blur, values > 100 gives smoother animations. Note: motion blurring does not work if particlesize is > 0
|
||||
uint8_t smearBlur; // 2D smeared blurring of full frame
|
||||
};
|
||||
|
||||
void blur2D(CRGB *colorbuffer, const uint32_t xsize, uint32_t ysize, const uint32_t xblur, const uint32_t yblur, const uint32_t xstart = 0, uint32_t ystart = 0, const bool isparticle = false);
|
||||
// initialization functions (not part of class)
|
||||
bool initParticleSystem2D(ParticleSystem2D *&PartSys, const uint32_t requestedsources, const uint32_t additionalbytes = 0, const bool advanced = false, const bool sizecontrol = false);
|
||||
uint32_t calculateNumberOfParticles2D(const uint32_t pixels, const bool advanced, const bool sizecontrol);
|
||||
uint32_t calculateNumberOfSources2D(const uint32_t pixels, const uint32_t requestedsources);
|
||||
bool allocateParticleSystemMemory2D(const uint32_t numparticles, const uint32_t numsources, const bool advanced, const bool sizecontrol, const uint32_t additionalbytes);
|
||||
#endif // WLED_DISABLE_PARTICLESYSTEM2D
|
||||
|
||||
////////////////////////
|
||||
// 1D Particle System //
|
||||
////////////////////////
|
||||
#ifndef WLED_DISABLE_PARTICLESYSTEM1D
|
||||
// memory allocation
|
||||
#define ESP8266_MAXPARTICLES_1D 320
|
||||
#define ESP8266_MAXSOURCES_1D 16
|
||||
#define ESP32S2_MAXPARTICLES_1D 1300
|
||||
#define ESP32S2_MAXSOURCES_1D 32
|
||||
#define ESP32_MAXPARTICLES_1D 2600
|
||||
#define ESP32_MAXSOURCES_1D 64
|
||||
|
||||
// particle dimensions (subpixel division)
|
||||
#define PS_P_RADIUS_1D 32 // subpixel size, each pixel is divided by this for particle movement, if this value is changed, also change the shift defines (next two lines)
|
||||
#define PS_P_HALFRADIUS_1D (PS_P_RADIUS_1D >> 1)
|
||||
#define PS_P_RADIUS_SHIFT_1D 5 // 1 << PS_P_RADIUS_SHIFT = PS_P_RADIUS
|
||||
#define PS_P_SURFACE_1D 5 // shift: 2^PS_P_SURFACE = PS_P_RADIUS_1D
|
||||
#define PS_P_MINHARDRADIUS_1D 32 // minimum hard surface radius note: do not change or hourglass effect will be broken
|
||||
#define PS_P_MINSURFACEHARDNESS_1D 120 // minimum hardness used in collision impulse calculation
|
||||
|
||||
// struct for PS settings (shared for 1D and 2D class)
|
||||
typedef union {
|
||||
struct{
|
||||
// one byte bit field for 1D settings
|
||||
bool wrap : 1;
|
||||
bool bounce : 1;
|
||||
bool killoutofbounds : 1; // if set, out of bound particles are killed immediately
|
||||
bool useGravity : 1; // set to 1 if gravity is used, disables bounceY at the top
|
||||
bool useCollisions : 1;
|
||||
bool colorByAge : 1; // if set, particle hue is set by ttl value in render function
|
||||
bool colorByPosition : 1; // if set, particle hue is set by its position in the strip segment
|
||||
bool unused : 1;
|
||||
};
|
||||
byte asByte; // access as a byte, order is: LSB is first entry in the list above
|
||||
} PSsettings1D;
|
||||
|
||||
//struct for a single particle (8 bytes)
|
||||
typedef struct {
|
||||
int32_t x; // x position in particle system
|
||||
uint16_t ttl; // time to live in frames
|
||||
int8_t vx; // horizontal velocity
|
||||
uint8_t hue; // color hue
|
||||
} PSparticle1D;
|
||||
|
||||
//struct for particle flags
|
||||
typedef union {
|
||||
struct { // 1 byte
|
||||
bool outofbounds : 1; // out of bounds flag, set to true if particle is outside of display area
|
||||
bool collide : 1; // if set, particle takes part in collisions
|
||||
bool perpetual : 1; // if set, particle does not age (TTL is not decremented in move function, it still dies from killoutofbounds)
|
||||
bool reversegrav : 1; // if set, gravity is reversed on this particle
|
||||
bool forcedirection : 1; // direction the force was applied, 1 is positive x-direction (used for collision stacking, similar to reversegrav) TODO: not used anymore, can be removed
|
||||
bool fixed : 1; // if set, particle does not move (and collisions make other particles revert direction),
|
||||
bool custom1 : 1; // unused custom flags, can be used by FX to track particle states
|
||||
bool custom2 : 1;
|
||||
};
|
||||
byte asByte; // access as a byte, order is: LSB is first entry in the list above
|
||||
} PSparticleFlags1D;
|
||||
|
||||
// struct for additional particle settings (optional)
|
||||
typedef struct {
|
||||
uint8_t sat; //color saturation
|
||||
uint8_t size; // particle size, 255 means 10 pixels in diameter
|
||||
uint8_t forcecounter;
|
||||
} PSadvancedParticle1D;
|
||||
|
||||
//struct for a particle source (20 bytes)
|
||||
typedef struct {
|
||||
uint16_t minLife; // minimum ttl of emittet particles
|
||||
uint16_t maxLife; // maximum ttl of emitted particles
|
||||
PSparticle1D source; // use a particle as the emitter source (speed, position, color)
|
||||
PSparticleFlags1D sourceFlags; // flags for the source particle
|
||||
int8_t var; // variation of emitted speed (adds random(+/- var) to speed)
|
||||
int8_t v; // emitting speed
|
||||
uint8_t sat; // color saturation (advanced property)
|
||||
uint8_t size; // particle size (advanced property)
|
||||
// note: there is 3 bytes of padding added here
|
||||
} PSsource1D;
|
||||
|
||||
class ParticleSystem1D
|
||||
{
|
||||
public:
|
||||
ParticleSystem1D(const uint32_t length, const uint32_t numberofparticles, const uint32_t numberofsources, const bool isadvanced = false); // constructor
|
||||
// note: memory is allcated in the FX function, no deconstructor needed
|
||||
void update(void); //update the particles according to set options and render to the matrix
|
||||
void updateSystem(void); // call at the beginning of every FX, updates pointers and dimensions
|
||||
// particle emitters
|
||||
int32_t sprayEmit(const PSsource1D &emitter);
|
||||
void particleMoveUpdate(PSparticle1D &part, PSparticleFlags1D &partFlags, PSsettings1D *options = NULL, PSadvancedParticle1D *advancedproperties = NULL); // move function
|
||||
//particle physics
|
||||
[[gnu::hot]] void applyForce(PSparticle1D &part, const int8_t xforce, uint8_t &counter); //apply a force to a single particle
|
||||
void applyForce(const int8_t xforce); // apply a force to all particles
|
||||
void applyGravity(PSparticle1D &part, PSparticleFlags1D &partFlags); // applies gravity to single particle (use this for sources)
|
||||
void applyFriction(const int32_t coefficient); // apply friction to all used particles
|
||||
// set options
|
||||
void setUsedParticles(const uint8_t percentage); // set the percentage of particles used in the system, 255=100%
|
||||
void setWallHardness(const uint8_t hardness); // hardness for bouncing on the wall if bounceXY is set
|
||||
void setSize(const uint32_t x); //set particle system size (= strip length)
|
||||
void setWrap(const bool enable);
|
||||
void setBounce(const bool enable);
|
||||
void setKillOutOfBounds(const bool enable); // if enabled, particles outside of matrix instantly die
|
||||
// void setSaturation(uint8_t sat); // set global color saturation
|
||||
void setColorByAge(const bool enable);
|
||||
void setColorByPosition(const bool enable);
|
||||
void setMotionBlur(const uint8_t bluramount); // note: motion blur can only be used if 'particlesize' is set to zero
|
||||
void setSmearBlur(const uint8_t bluramount); // enable 1D smeared blurring of full frame
|
||||
void setParticleSize(const uint8_t size); //size 0 = 1 pixel, size 1 = 2 pixels, is overruled by advanced particle size
|
||||
void setGravity(int8_t force = 8);
|
||||
void enableParticleCollisions(bool enable, const uint8_t hardness = 255);
|
||||
|
||||
PSparticle1D *particles; // pointer to particle array
|
||||
PSparticleFlags1D *particleFlags; // pointer to particle flags array
|
||||
PSsource1D *sources; // pointer to sources
|
||||
PSadvancedParticle1D *advPartProps; // pointer to advanced particle properties (can be NULL)
|
||||
//PSsizeControl *advPartSize; // pointer to advanced particle size control (can be NULL)
|
||||
uint8_t* PSdataEnd; // points to first available byte after the PSmemory, is set in setPointers(). use this for FX custom data
|
||||
int32_t maxX; // particle system size i.e. width-1, Note: all "max" variables must be signed to compare to coordinates (which are signed)
|
||||
int32_t maxXpixel; // last physical pixel that can be drawn to (FX can read this to read segment size if required), equal to width-1
|
||||
uint32_t numSources; // number of sources
|
||||
uint32_t usedParticles; // number of particles used in animation, is relative to 'numParticles'
|
||||
|
||||
private:
|
||||
//rendering functions
|
||||
void render(void);
|
||||
[[gnu::hot]] void renderParticle(const uint32_t particleindex, const uint8_t brightness, const CRGB &color, const bool wrap);
|
||||
|
||||
//paricle physics applied by system if flags are set
|
||||
void applyGravity(); // applies gravity to all particles
|
||||
void handleCollisions();
|
||||
[[gnu::hot]] void collideParticles(PSparticle1D &particle1, const PSparticleFlags1D &particle1flags, PSparticle1D &particle2, const PSparticleFlags1D &particle2flags, const int32_t dx, const uint32_t dx_abs, const uint32_t collisiondistance);
|
||||
|
||||
//utility functions
|
||||
void updatePSpointers(const bool isadvanced); // update the data pointers to current segment data space
|
||||
//void updateSize(PSadvancedParticle *advprops, PSsizeControl *advsize); // advanced size control
|
||||
[[gnu::hot]] void bounce(int8_t &incomingspeed, int8_t ¶llelspeed, int32_t &position, const uint32_t maxposition); // bounce on a wall
|
||||
// note: variables that are accessed often are 32bit for speed
|
||||
#ifndef ESP8266
|
||||
CRGB *framebuffer; // local frame buffer for rendering
|
||||
#endif
|
||||
PSsettings1D particlesettings; // settings used when updating particles
|
||||
uint32_t numParticles; // total number of particles allocated by this system
|
||||
uint32_t emitIndex; // index to count through particles to emit so searching for dead pixels is faster
|
||||
int32_t collisionHardness;
|
||||
uint32_t particleHardRadius; // hard surface radius of a particle, used for collision detection
|
||||
uint32_t wallHardness;
|
||||
uint8_t gforcecounter; // counter for global gravity
|
||||
int8_t gforce; // gravity strength, default is 8 (negative is allowed, positive is downwards)
|
||||
uint8_t forcecounter; // counter for globally applied forces
|
||||
uint16_t collisionStartIdx; // particle array start index for collision detection
|
||||
//global particle properties for basic particles
|
||||
uint8_t particlesize; // global particle size, 0 = 1 pixel, 1 = 2 pixels
|
||||
uint8_t motionBlur; // enable motion blur, values > 100 gives smoother animations
|
||||
uint8_t smearBlur; // smeared blurring of full frame
|
||||
};
|
||||
|
||||
bool initParticleSystem1D(ParticleSystem1D *&PartSys, const uint32_t requestedsources, const uint8_t fractionofparticles = 255, const uint32_t additionalbytes = 0, const bool advanced = false);
|
||||
uint32_t calculateNumberOfParticles1D(const uint32_t fraction, const bool isadvanced);
|
||||
uint32_t calculateNumberOfSources1D(const uint32_t requestedsources);
|
||||
bool allocateParticleSystemMemory1D(const uint32_t numparticles, const uint32_t numsources, const bool isadvanced, const uint32_t additionalbytes);
|
||||
void blur1D(CRGB *colorbuffer, uint32_t size, uint32_t blur, uint32_t start);
|
||||
#endif // WLED_DISABLE_PARTICLESYSTEM1D
|
@ -21,10 +21,12 @@
|
||||
#endif
|
||||
#include "const.h"
|
||||
#include "pin_manager.h"
|
||||
#include "bus_wrapper.h"
|
||||
#include "bus_manager.h"
|
||||
#include "bus_wrapper.h"
|
||||
#include <bits/unique_ptr.h>
|
||||
|
||||
extern bool cctICused;
|
||||
extern bool useParallelI2S;
|
||||
|
||||
//colors.cpp
|
||||
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
|
||||
@ -32,28 +34,6 @@ uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
|
||||
//udp.cpp
|
||||
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, const uint8_t* buffer, uint8_t bri=255, bool isRGBW=false);
|
||||
|
||||
// enable additional debug output
|
||||
#if defined(WLED_DEBUG_HOST)
|
||||
#include "net_debug.h"
|
||||
#define DEBUGOUT NetDebug
|
||||
#else
|
||||
#define DEBUGOUT Serial
|
||||
#endif
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
#ifndef ESP8266
|
||||
#include <rom/rtc.h>
|
||||
#endif
|
||||
#define DEBUG_PRINT(x) DEBUGOUT.print(x)
|
||||
#define DEBUG_PRINTLN(x) DEBUGOUT.println(x)
|
||||
#define DEBUG_PRINTF(x...) DEBUGOUT.printf(x)
|
||||
#define DEBUG_PRINTF_P(x...) DEBUGOUT.printf_P(x)
|
||||
#else
|
||||
#define DEBUG_PRINT(x)
|
||||
#define DEBUG_PRINTLN(x)
|
||||
#define DEBUG_PRINTF(x...)
|
||||
#define DEBUG_PRINTF_P(x...)
|
||||
#endif
|
||||
|
||||
//color mangling macros
|
||||
#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b))))
|
||||
@ -63,19 +43,20 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, const
|
||||
#define W(c) (byte((c) >> 24))
|
||||
|
||||
|
||||
static ColorOrderMap _colorOrderMap = {};
|
||||
|
||||
bool ColorOrderMap::add(uint16_t start, uint16_t len, uint8_t colorOrder) {
|
||||
if (count() >= WLED_MAX_COLOR_ORDER_MAPPINGS || len == 0 || (colorOrder & 0x0F) > COL_ORDER_MAX) return false; // upper nibble contains W swap information
|
||||
_mappings.push_back({start,len,colorOrder});
|
||||
DEBUGBUS_PRINTF_P(PSTR("Bus: Add COM (%d,%d,%d)\n"), (int)start, (int)len, (int)colorOrder);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t IRAM_ATTR ColorOrderMap::getPixelColorOrder(uint16_t pix, uint8_t defaultColorOrder) const {
|
||||
// upper nibble contains W swap information
|
||||
// when ColorOrderMap's upper nibble contains value >0 then swap information is used from it, otherwise global swap is used
|
||||
for (unsigned i = 0; i < count(); i++) {
|
||||
if (pix >= _mappings[i].start && pix < (_mappings[i].start + _mappings[i].len)) {
|
||||
return _mappings[i].colorOrder | ((_mappings[i].colorOrder >> 4) ? 0 : (defaultColorOrder & 0xF0));
|
||||
}
|
||||
for (const auto& map : _mappings) {
|
||||
if (pix >= map.start && pix < (map.start + map.len)) return map.colorOrder | ((map.colorOrder >> 4) ? 0 : (defaultColorOrder & 0xF0));
|
||||
}
|
||||
return defaultColorOrder;
|
||||
}
|
||||
@ -118,44 +99,52 @@ uint32_t Bus::autoWhiteCalc(uint32_t c) const {
|
||||
return RGBW32(r, g, b, w);
|
||||
}
|
||||
|
||||
uint8_t *Bus::allocateData(size_t size) {
|
||||
if (_data) free(_data); // should not happen, but for safety
|
||||
return _data = (uint8_t *)(size>0 ? calloc(size, sizeof(uint8_t)) : nullptr);
|
||||
}
|
||||
|
||||
|
||||
BusDigital::BusDigital(const BusConfig &bc, uint8_t nr, const ColorOrderMap &com)
|
||||
BusDigital::BusDigital(const BusConfig &bc, uint8_t nr)
|
||||
: Bus(bc.type, bc.start, bc.autoWhite, bc.count, bc.reversed, (bc.refreshReq || bc.type == TYPE_TM1814))
|
||||
, _skip(bc.skipAmount) //sacrificial pixels
|
||||
, _colorOrder(bc.colorOrder)
|
||||
, _milliAmpsPerLed(bc.milliAmpsPerLed)
|
||||
, _milliAmpsMax(bc.milliAmpsMax)
|
||||
, _colorOrderMap(com)
|
||||
, _data(nullptr)
|
||||
{
|
||||
if (!isDigital(bc.type) || !bc.count) return;
|
||||
if (!PinManager::allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return;
|
||||
DEBUGBUS_PRINTLN(F("Bus: Creating digital bus."));
|
||||
if (!isDigital(bc.type) || !bc.count) { DEBUGBUS_PRINTLN(F("Not digial or empty bus!")); return; }
|
||||
if (!PinManager::allocatePin(bc.pins[0], true, PinOwner::BusDigital)) { DEBUGBUS_PRINTLN(F("Pin 0 allocated!")); return; }
|
||||
_frequencykHz = 0U;
|
||||
_pins[0] = bc.pins[0];
|
||||
if (is2Pin(bc.type)) {
|
||||
if (!PinManager::allocatePin(bc.pins[1], true, PinOwner::BusDigital)) {
|
||||
cleanup();
|
||||
DEBUGBUS_PRINTLN(F("Pin 1 allocated!"));
|
||||
return;
|
||||
}
|
||||
_pins[1] = bc.pins[1];
|
||||
_frequencykHz = bc.frequency ? bc.frequency : 2000U; // 2MHz clock if undefined
|
||||
}
|
||||
_iType = PolyBus::getI(bc.type, _pins, nr);
|
||||
if (_iType == I_NONE) return;
|
||||
if (_iType == I_NONE) { DEBUGBUS_PRINTLN(F("Incorrect iType!")); return; }
|
||||
_hasRgb = hasRGB(bc.type);
|
||||
_hasWhite = hasWhite(bc.type);
|
||||
_hasCCT = hasCCT(bc.type);
|
||||
if (bc.doubleBuffer && !allocateData(bc.count * Bus::getNumberOfChannels(bc.type))) return;
|
||||
//_buffering = bc.doubleBuffer;
|
||||
if (bc.doubleBuffer) {
|
||||
_data = (uint8_t*)calloc(_len, Bus::getNumberOfChannels(_type));
|
||||
if (!_data) DEBUGBUS_PRINTLN(F("Bus: Buffer allocation failed!"));
|
||||
}
|
||||
uint16_t lenToCreate = bc.count;
|
||||
if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(bc.count); // only needs a third of "RGB" LEDs for NeoPixelBus
|
||||
_busPtr = PolyBus::create(_iType, _pins, lenToCreate + _skip, nr);
|
||||
_valid = (_busPtr != nullptr);
|
||||
DEBUG_PRINTF_P(PSTR("%successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u). mA=%d/%d\n"), _valid?"S":"Uns", nr, bc.count, bc.type, _pins[0], is2Pin(bc.type)?_pins[1]:255, _iType, _milliAmpsPerLed, _milliAmpsMax);
|
||||
_valid = (_busPtr != nullptr) && bc.count > 0;
|
||||
DEBUGBUS_PRINTF_P(PSTR("Bus: %successfully inited #%u (len:%u, type:%u (RGB:%d, W:%d, CCT:%d), pins:%u,%u [itype:%u] mA=%d/%d)\n"),
|
||||
_valid?"S":"Uns",
|
||||
(int)nr,
|
||||
(int)bc.count,
|
||||
(int)bc.type,
|
||||
(int)_hasRgb, (int)_hasWhite, (int)_hasCCT,
|
||||
(unsigned)_pins[0], is2Pin(bc.type)?(unsigned)_pins[1]:255U,
|
||||
(unsigned)_iType,
|
||||
(int)_milliAmpsPerLed, (int)_milliAmpsMax
|
||||
);
|
||||
}
|
||||
|
||||
//DISCLAIMER
|
||||
@ -166,7 +155,7 @@ BusDigital::BusDigital(const BusConfig &bc, uint8_t nr, const ColorOrderMap &com
|
||||
//I am NOT to be held liable for burned down garages or houses!
|
||||
|
||||
// To disable brightness limiter we either set output max current to 0 or single LED current to 0
|
||||
uint8_t BusDigital::estimateCurrentAndLimitBri() {
|
||||
uint8_t BusDigital::estimateCurrentAndLimitBri() const {
|
||||
bool useWackyWS2815PowerModel = false;
|
||||
byte actualMilliampsPerLed = _milliAmpsPerLed;
|
||||
|
||||
@ -179,7 +168,7 @@ uint8_t BusDigital::estimateCurrentAndLimitBri() {
|
||||
actualMilliampsPerLed = 12; // from testing an actual strip
|
||||
}
|
||||
|
||||
size_t powerBudget = (_milliAmpsMax - MA_FOR_ESP/BusManager::getNumBusses()); //80/120mA for ESP power
|
||||
unsigned powerBudget = (_milliAmpsMax - MA_FOR_ESP/BusManager::getNumBusses()); //80/120mA for ESP power
|
||||
if (powerBudget > getLength()) { //each LED uses about 1mA in standby, exclude that from power budget
|
||||
powerBudget -= getLength();
|
||||
} else {
|
||||
@ -204,26 +193,25 @@ uint8_t BusDigital::estimateCurrentAndLimitBri() {
|
||||
}
|
||||
|
||||
// powerSum has all the values of channels summed (max would be getLength()*765 as white is excluded) so convert to milliAmps
|
||||
busPowerSum = (busPowerSum * actualMilliampsPerLed) / 765;
|
||||
_milliAmpsTotal = busPowerSum * _bri / 255;
|
||||
BusDigital::_milliAmpsTotal = (busPowerSum * actualMilliampsPerLed * _bri) / (765*255);
|
||||
|
||||
uint8_t newBri = _bri;
|
||||
if (busPowerSum * _bri / 255 > powerBudget) { //scale brightness down to stay in current limit
|
||||
float scale = (float)(powerBudget * 255) / (float)(busPowerSum * _bri);
|
||||
if (scale >= 1.0f) return _bri;
|
||||
_milliAmpsTotal = ceilf((float)_milliAmpsTotal * scale);
|
||||
uint8_t scaleB = min((int)(scale * 255), 255);
|
||||
newBri = unsigned(_bri * scaleB) / 256 + 1;
|
||||
if (BusDigital::_milliAmpsTotal > powerBudget) {
|
||||
//scale brightness down to stay in current limit
|
||||
unsigned scaleB = powerBudget * 255 / BusDigital::_milliAmpsTotal;
|
||||
newBri = (_bri * scaleB) / 256 + 1;
|
||||
BusDigital::_milliAmpsTotal = powerBudget;
|
||||
//_milliAmpsTotal = (busPowerSum * actualMilliampsPerLed * newBri) / (765*255);
|
||||
}
|
||||
return newBri;
|
||||
}
|
||||
|
||||
void BusDigital::show() {
|
||||
_milliAmpsTotal = 0;
|
||||
BusDigital::_milliAmpsTotal = 0;
|
||||
if (!_valid) return;
|
||||
|
||||
uint8_t cctWW = 0, cctCW = 0;
|
||||
unsigned newBri = estimateCurrentAndLimitBri(); // will fill _milliAmpsTotal
|
||||
unsigned newBri = estimateCurrentAndLimitBri(); // will fill _milliAmpsTotal (TODO: could use PolyBus::CalcTotalMilliAmpere())
|
||||
if (newBri < _bri) PolyBus::setBrightness(_busPtr, _iType, newBri); // limit brightness to stay within current limits
|
||||
|
||||
if (_data) {
|
||||
@ -249,6 +237,7 @@ void BusDigital::show() {
|
||||
// TODO: there is an issue if CCT is calculated from RGB value (_cct==-1), we cannot do that with double buffer
|
||||
Bus::_cct = _data[offset+channels-1];
|
||||
Bus::calculateCCT(c, cctWW, cctCW);
|
||||
if (_type == TYPE_WS2812_WWA) c = RGBW32(cctWW, cctCW, 0, W(c)); // may need swapping
|
||||
}
|
||||
unsigned pix = i;
|
||||
if (_reversed) pix = _len - pix -1;
|
||||
@ -272,7 +261,7 @@ void BusDigital::show() {
|
||||
}
|
||||
}
|
||||
}
|
||||
PolyBus::show(_busPtr, _iType, !_data); // faster if buffer consistency is not important (use !_buffering this causes 20% FPS drop)
|
||||
PolyBus::show(_busPtr, _iType, !_data); // faster if buffer consistency is not important
|
||||
// restore bus brightness to its original value
|
||||
// this is done right after show, so this is only OK if LED updates are completed before show() returns
|
||||
// or async show has a separate buffer (ESP32 RMT and I2S are ok)
|
||||
@ -334,8 +323,8 @@ void IRAM_ATTR BusDigital::setPixelColor(unsigned pix, uint32_t c) {
|
||||
uint8_t cctWW = 0, cctCW = 0;
|
||||
Bus::calculateCCT(c, cctWW, cctCW);
|
||||
wwcw = (cctCW<<8) | cctWW;
|
||||
if (_type == TYPE_WS2812_WWA) c = RGBW32(cctWW, cctCW, 0, W(c)); // may need swapping
|
||||
}
|
||||
|
||||
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, wwcw);
|
||||
}
|
||||
}
|
||||
@ -367,23 +356,31 @@ uint32_t IRAM_ATTR BusDigital::getPixelColor(unsigned pix) const {
|
||||
case 2: c = RGBW32(b, b, b, b); break;
|
||||
}
|
||||
}
|
||||
if (_type == TYPE_WS2812_WWA) {
|
||||
uint8_t w = R(c) | G(c);
|
||||
c = RGBW32(w, w, 0, w);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t BusDigital::getPins(uint8_t* pinArray) const {
|
||||
unsigned BusDigital::getPins(uint8_t* pinArray) const {
|
||||
unsigned numPins = is2Pin(_type) + 1;
|
||||
if (pinArray) for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i];
|
||||
return numPins;
|
||||
}
|
||||
|
||||
unsigned BusDigital::getBusSize() const {
|
||||
return sizeof(BusDigital) + (isOk() ? PolyBus::getDataSize(_busPtr, _iType) + (_data ? _len * getNumberOfChannels() : 0) : 0);
|
||||
}
|
||||
|
||||
void BusDigital::setColorOrder(uint8_t colorOrder) {
|
||||
// upper nibble contains W swap information
|
||||
if ((colorOrder & 0x0F) > 5) return;
|
||||
_colorOrder = colorOrder;
|
||||
}
|
||||
|
||||
// credit @willmmiles & @netmindz https://github.com/Aircoookie/WLED/pull/4056
|
||||
// credit @willmmiles & @netmindz https://github.com/wled-dev/WLED/pull/4056
|
||||
std::vector<LEDType> BusDigital::getLEDTypes() {
|
||||
return {
|
||||
{TYPE_WS2812_RGB, "D", PSTR("WS281x")},
|
||||
@ -399,8 +396,8 @@ std::vector<LEDType> BusDigital::getLEDTypes() {
|
||||
{TYPE_WS2805, "D", PSTR("WS2805 RGBCW")},
|
||||
{TYPE_SM16825, "D", PSTR("SM16825 RGBCW")},
|
||||
{TYPE_WS2812_1CH_X3, "D", PSTR("WS2811 White")},
|
||||
//{TYPE_WS2812_2CH_X3, "D", PSTR("WS2811 CCT")}, // not implemented
|
||||
//{TYPE_WS2812_WWA, "D", PSTR("WS2811 WWA")}, // not implemented
|
||||
//{TYPE_WS2812_2CH_X3, "D", PSTR("WS281x CCT")}, // not implemented
|
||||
{TYPE_WS2812_WWA, "D", PSTR("WS281x WWA")}, // amber ignored
|
||||
{TYPE_WS2801, "2P", PSTR("WS2801")},
|
||||
{TYPE_APA102, "2P", PSTR("APA102")},
|
||||
{TYPE_LPD8806, "2P", PSTR("LPD8806")},
|
||||
@ -415,12 +412,13 @@ void BusDigital::begin() {
|
||||
}
|
||||
|
||||
void BusDigital::cleanup() {
|
||||
DEBUG_PRINTLN(F("Digital Cleanup."));
|
||||
DEBUGBUS_PRINTLN(F("Digital Cleanup."));
|
||||
PolyBus::cleanup(_busPtr, _iType);
|
||||
free(_data);
|
||||
_data = nullptr;
|
||||
_iType = I_NONE;
|
||||
_valid = false;
|
||||
_busPtr = nullptr;
|
||||
if (_data != nullptr) freeData();
|
||||
PinManager::deallocatePin(_pins[1], PinOwner::BusDigital);
|
||||
PinManager::deallocatePin(_pins[0], PinOwner::BusDigital);
|
||||
}
|
||||
@ -492,9 +490,8 @@ BusPwm::BusPwm(const BusConfig &bc)
|
||||
_hasRgb = hasRGB(bc.type);
|
||||
_hasWhite = hasWhite(bc.type);
|
||||
_hasCCT = hasCCT(bc.type);
|
||||
_data = _pwmdata; // avoid malloc() and use stack
|
||||
_valid = true;
|
||||
DEBUG_PRINTF_P(PSTR("%successfully inited PWM strip with type %u, frequency %u, bit depth %u and pins %u,%u,%u,%u,%u\n"), _valid?"S":"Uns", bc.type, _frequency, _depth, _pins[0], _pins[1], _pins[2], _pins[3], _pins[4]);
|
||||
DEBUGBUS_PRINTF_P(PSTR("%successfully inited PWM strip with type %u, frequency %u, bit depth %u and pins %u,%u,%u,%u,%u\n"), _valid?"S":"Uns", bc.type, _frequency, _depth, _pins[0], _pins[1], _pins[2], _pins[3], _pins[4]);
|
||||
}
|
||||
|
||||
void BusPwm::setPixelColor(unsigned pix, uint32_t c) {
|
||||
@ -556,7 +553,7 @@ uint32_t BusPwm::getPixelColor(unsigned pix) const {
|
||||
|
||||
void BusPwm::show() {
|
||||
if (!_valid) return;
|
||||
const unsigned numPins = getPins();
|
||||
const size_t numPins = getPins();
|
||||
#ifdef ESP8266
|
||||
const unsigned analogPeriod = F_CPU / _frequency;
|
||||
const unsigned maxBri = analogPeriod; // compute to clock cycle accuracy
|
||||
@ -564,7 +561,7 @@ void BusPwm::show() {
|
||||
constexpr unsigned bitShift = 8; // 256 clocks for dead time, ~3us at 80MHz
|
||||
#else
|
||||
// if _needsRefresh is true (UI hack) we are using dithering (credit @dedehai & @zalatnaicsongor)
|
||||
// https://github.com/Aircoookie/WLED/pull/4115 and https://github.com/zalatnaicsongor/WLED/pull/1)
|
||||
// https://github.com/wled-dev/WLED/pull/4115 and https://github.com/zalatnaicsongor/WLED/pull/1)
|
||||
const bool dithering = _needsRefresh; // avoid working with bitfield
|
||||
const unsigned maxBri = (1<<_depth); // possible values: 16384 (14), 8192 (13), 4096 (12), 2048 (11), 1024 (10), 512 (9) and 256 (8)
|
||||
const unsigned bitShift = dithering * 4; // if dithering, _depth is 12 bit but LEDC channel is set to 8 bit (using 4 fractional bits)
|
||||
@ -623,14 +620,14 @@ void BusPwm::show() {
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t BusPwm::getPins(uint8_t* pinArray) const {
|
||||
unsigned BusPwm::getPins(uint8_t* pinArray) const {
|
||||
if (!_valid) return 0;
|
||||
unsigned numPins = numPWMPins(_type);
|
||||
if (pinArray) for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i];
|
||||
return numPins;
|
||||
}
|
||||
|
||||
// credit @willmmiles & @netmindz https://github.com/Aircoookie/WLED/pull/4056
|
||||
// credit @willmmiles & @netmindz https://github.com/wled-dev/WLED/pull/4056
|
||||
std::vector<LEDType> BusPwm::getLEDTypes() {
|
||||
return {
|
||||
{TYPE_ANALOG_1CH, "A", PSTR("PWM White")},
|
||||
@ -643,7 +640,7 @@ std::vector<LEDType> BusPwm::getLEDTypes() {
|
||||
}
|
||||
|
||||
void BusPwm::deallocatePins() {
|
||||
unsigned numPins = getPins();
|
||||
size_t numPins = getPins();
|
||||
for (unsigned i = 0; i < numPins; i++) {
|
||||
PinManager::deallocatePin(_pins[i], PinOwner::BusPwm);
|
||||
if (!PinManager::isPinOk(_pins[i])) continue;
|
||||
@ -661,7 +658,7 @@ void BusPwm::deallocatePins() {
|
||||
|
||||
BusOnOff::BusOnOff(const BusConfig &bc)
|
||||
: Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed)
|
||||
, _onoffdata(0)
|
||||
, _data(0)
|
||||
{
|
||||
if (!Bus::isOnOff(bc.type)) return;
|
||||
|
||||
@ -674,9 +671,8 @@ BusOnOff::BusOnOff(const BusConfig &bc)
|
||||
_hasRgb = false;
|
||||
_hasWhite = false;
|
||||
_hasCCT = false;
|
||||
_data = &_onoffdata; // avoid malloc() and use stack
|
||||
_valid = true;
|
||||
DEBUG_PRINTF_P(PSTR("%successfully inited On/Off strip with pin %u\n"), _valid?"S":"Uns", _pin);
|
||||
DEBUGBUS_PRINTF_P(PSTR("%successfully inited On/Off strip with pin %u\n"), _valid?"S":"Uns", _pin);
|
||||
}
|
||||
|
||||
void BusOnOff::setPixelColor(unsigned pix, uint32_t c) {
|
||||
@ -686,26 +682,26 @@ void BusOnOff::setPixelColor(unsigned pix, uint32_t c) {
|
||||
uint8_t g = G(c);
|
||||
uint8_t b = B(c);
|
||||
uint8_t w = W(c);
|
||||
_data[0] = bool(r|g|b|w) && bool(_bri) ? 0xFF : 0;
|
||||
_data = bool(r|g|b|w) && bool(_bri) ? 0xFF : 0;
|
||||
}
|
||||
|
||||
uint32_t BusOnOff::getPixelColor(unsigned pix) const {
|
||||
if (!_valid) return 0;
|
||||
return RGBW32(_data[0], _data[0], _data[0], _data[0]);
|
||||
return RGBW32(_data, _data, _data, _data);
|
||||
}
|
||||
|
||||
void BusOnOff::show() {
|
||||
if (!_valid) return;
|
||||
digitalWrite(_pin, _reversed ? !(bool)_data[0] : (bool)_data[0]);
|
||||
digitalWrite(_pin, _reversed ? !(bool)_data : (bool)_data);
|
||||
}
|
||||
|
||||
uint8_t BusOnOff::getPins(uint8_t* pinArray) const {
|
||||
unsigned BusOnOff::getPins(uint8_t* pinArray) const {
|
||||
if (!_valid) return 0;
|
||||
if (pinArray) pinArray[0] = _pin;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// credit @willmmiles & @netmindz https://github.com/Aircoookie/WLED/pull/4056
|
||||
// credit @willmmiles & @netmindz https://github.com/wled-dev/WLED/pull/4056
|
||||
std::vector<LEDType> BusOnOff::getLEDTypes() {
|
||||
return {
|
||||
{TYPE_ONOFF, "", PSTR("On/Off")},
|
||||
@ -735,8 +731,9 @@ BusNetwork::BusNetwork(const BusConfig &bc)
|
||||
_hasCCT = false;
|
||||
_UDPchannels = _hasWhite + 3;
|
||||
_client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]);
|
||||
_valid = (allocateData(_len * _UDPchannels) != nullptr);
|
||||
DEBUG_PRINTF_P(PSTR("%successfully inited virtual strip with type %u and IP %u.%u.%u.%u\n"), _valid?"S":"Uns", bc.type, bc.pins[0], bc.pins[1], bc.pins[2], bc.pins[3]);
|
||||
_data = (uint8_t*)calloc(_len, _UDPchannels);
|
||||
_valid = (_data != nullptr);
|
||||
DEBUGBUS_PRINTF_P(PSTR("%successfully inited virtual strip with type %u and IP %u.%u.%u.%u\n"), _valid?"S":"Uns", bc.type, bc.pins[0], bc.pins[1], bc.pins[2], bc.pins[3]);
|
||||
}
|
||||
|
||||
void BusNetwork::setPixelColor(unsigned pix, uint32_t c) {
|
||||
@ -763,12 +760,12 @@ void BusNetwork::show() {
|
||||
_broadcastLock = false;
|
||||
}
|
||||
|
||||
uint8_t BusNetwork::getPins(uint8_t* pinArray) const {
|
||||
unsigned BusNetwork::getPins(uint8_t* pinArray) const {
|
||||
if (pinArray) for (unsigned i = 0; i < 4; i++) pinArray[i] = _client[i];
|
||||
return 4;
|
||||
}
|
||||
|
||||
// credit @willmmiles & @netmindz https://github.com/Aircoookie/WLED/pull/4056
|
||||
// credit @willmmiles & @netmindz https://github.com/wled-dev/WLED/pull/4056
|
||||
std::vector<LEDType> BusNetwork::getLEDTypes() {
|
||||
return {
|
||||
{TYPE_NET_DDP_RGB, "N", PSTR("DDP RGB (network)")}, // should be "NNNN" to determine 4 "pin" fields
|
||||
@ -779,55 +776,80 @@ std::vector<LEDType> BusNetwork::getLEDTypes() {
|
||||
//{TYPE_VIRTUAL_I2C_W, "V", PSTR("I2C White (virtual)")}, // allows setting I2C address in _pin[0]
|
||||
//{TYPE_VIRTUAL_I2C_CCT, "V", PSTR("I2C CCT (virtual)")}, // allows setting I2C address in _pin[0]
|
||||
//{TYPE_VIRTUAL_I2C_RGB, "VVV", PSTR("I2C RGB (virtual)")}, // allows setting I2C address in _pin[0] and 2 additional values in _pin[1] & _pin[2]
|
||||
//{TYPE_USERMOD, "VVVVV", PSTR("Usermod (virtual)")}, // 5 data fields (see https://github.com/Aircoookie/WLED/pull/4123)
|
||||
//{TYPE_USERMOD, "VVVVV", PSTR("Usermod (virtual)")}, // 5 data fields (see https://github.com/wled-dev/WLED/pull/4123)
|
||||
};
|
||||
}
|
||||
|
||||
void BusNetwork::cleanup() {
|
||||
DEBUGBUS_PRINTLN(F("Virtual Cleanup."));
|
||||
free(_data);
|
||||
_data = nullptr;
|
||||
_type = I_NONE;
|
||||
_valid = false;
|
||||
freeData();
|
||||
}
|
||||
|
||||
|
||||
//utility to get the approx. memory usage of a given BusConfig
|
||||
uint32_t BusManager::memUsage(const BusConfig &bc) {
|
||||
if (Bus::isOnOff(bc.type) || Bus::isPWM(bc.type)) return OUTPUT_MAX_PINS;
|
||||
|
||||
unsigned len = bc.count + bc.skipAmount;
|
||||
unsigned channels = Bus::getNumberOfChannels(bc.type);
|
||||
unsigned multiplier = 1;
|
||||
if (Bus::isDigital(bc.type)) { // digital types
|
||||
if (Bus::is16bit(bc.type)) len *= 2; // 16-bit LEDs
|
||||
#ifdef ESP8266
|
||||
if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem
|
||||
multiplier = 5;
|
||||
unsigned BusConfig::memUsage(unsigned nr) const {
|
||||
if (Bus::isVirtual(type)) {
|
||||
return sizeof(BusNetwork) + (count * Bus::getNumberOfChannels(type));
|
||||
} else if (Bus::isDigital(type)) {
|
||||
return sizeof(BusDigital) + PolyBus::memUsage(count + skipAmount, PolyBus::getI(type, pins, nr)) + doubleBuffer * (count + skipAmount) * Bus::getNumberOfChannels(type);
|
||||
} else if (Bus::isOnOff(type)) {
|
||||
return sizeof(BusOnOff);
|
||||
} else {
|
||||
return sizeof(BusPwm);
|
||||
}
|
||||
#else //ESP32 RMT uses double buffer, parallel I2S uses 8x buffer (3 times)
|
||||
multiplier = PolyBus::isParallelI2S1Output() ? 24 : 2;
|
||||
#endif
|
||||
}
|
||||
return (len * multiplier + bc.doubleBuffer * (bc.count + bc.skipAmount)) * channels;
|
||||
}
|
||||
|
||||
uint32_t BusManager::memUsage(unsigned maxChannels, unsigned maxCount, unsigned minBuses) {
|
||||
//ESP32 RMT uses double buffer, parallel I2S uses 8x buffer (3 times)
|
||||
unsigned multiplier = PolyBus::isParallelI2S1Output() ? 3 : 2;
|
||||
return (maxChannels * maxCount * minBuses * multiplier);
|
||||
|
||||
unsigned BusManager::memUsage() {
|
||||
// when ESP32, S2 & S3 use parallel I2S only the largest bus determines the total memory requirements for back buffers
|
||||
// front buffers are always allocated per bus
|
||||
unsigned size = 0;
|
||||
unsigned maxI2S = 0;
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(ESP8266)
|
||||
unsigned digitalCount = 0;
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
#define MAX_RMT 4
|
||||
#else
|
||||
#define MAX_RMT 8
|
||||
#endif
|
||||
#endif
|
||||
for (const auto &bus : busses) {
|
||||
unsigned busSize = bus->getBusSize();
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(ESP8266)
|
||||
if (bus->isDigital() && !bus->is2Pin()) digitalCount++;
|
||||
if (PolyBus::isParallelI2S1Output() && digitalCount > MAX_RMT) {
|
||||
unsigned i2sCommonSize = 3 * bus->getLength() * bus->getNumberOfChannels() * (bus->is16bit()+1);
|
||||
if (i2sCommonSize > maxI2S) maxI2S = i2sCommonSize;
|
||||
busSize -= i2sCommonSize;
|
||||
}
|
||||
#endif
|
||||
size += busSize;
|
||||
}
|
||||
return size + maxI2S;
|
||||
}
|
||||
|
||||
int BusManager::add(const BusConfig &bc) {
|
||||
DEBUGBUS_PRINTF_P(PSTR("Bus: Adding bus (%d - %d >= %d)\n"), getNumBusses(), getNumVirtualBusses(), WLED_MAX_BUSSES);
|
||||
if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1;
|
||||
unsigned numDigital = 0;
|
||||
for (const auto &bus : busses) if (bus->isDigital() && !bus->is2Pin()) numDigital++;
|
||||
if (Bus::isVirtual(bc.type)) {
|
||||
busses[numBusses] = new BusNetwork(bc);
|
||||
busses.push_back(make_unique<BusNetwork>(bc));
|
||||
//busses.push_back(new BusNetwork(bc));
|
||||
} else if (Bus::isDigital(bc.type)) {
|
||||
busses[numBusses] = new BusDigital(bc, numBusses, colorOrderMap);
|
||||
busses.push_back(make_unique<BusDigital>(bc, numDigital));
|
||||
//busses.push_back(new BusDigital(bc, numDigital));
|
||||
} else if (Bus::isOnOff(bc.type)) {
|
||||
busses[numBusses] = new BusOnOff(bc);
|
||||
busses.push_back(make_unique<BusOnOff>(bc));
|
||||
//busses.push_back(new BusOnOff(bc));
|
||||
} else {
|
||||
busses[numBusses] = new BusPwm(bc);
|
||||
busses.push_back(make_unique<BusPwm>(bc));
|
||||
//busses.push_back(new BusPwm(bc));
|
||||
}
|
||||
return numBusses++;
|
||||
return busses.size();
|
||||
}
|
||||
|
||||
// credit @willmmiles
|
||||
@ -843,7 +865,7 @@ static String LEDTypesToJson(const std::vector<LEDType>& types) {
|
||||
return json;
|
||||
}
|
||||
|
||||
// credit @willmmiles & @netmindz https://github.com/Aircoookie/WLED/pull/4056
|
||||
// credit @willmmiles & @netmindz https://github.com/wled-dev/WLED/pull/4056
|
||||
String BusManager::getLEDTypesJSONString() {
|
||||
String json = "[";
|
||||
json += LEDTypesToJson(BusDigital::getLEDTypes());
|
||||
@ -856,18 +878,21 @@ String BusManager::getLEDTypesJSONString() {
|
||||
}
|
||||
|
||||
void BusManager::useParallelOutput() {
|
||||
_parallelOutputs = 8; // hardcoded since we use NPB I2S x8 methods
|
||||
DEBUGBUS_PRINTLN(F("Bus: Enabling parallel I2S."));
|
||||
PolyBus::setParallelI2S1Output();
|
||||
}
|
||||
|
||||
bool BusManager::hasParallelOutput() {
|
||||
return PolyBus::isParallelI2S1Output();
|
||||
}
|
||||
|
||||
//do not call this method from system context (network callback)
|
||||
void BusManager::removeAll() {
|
||||
DEBUG_PRINTLN(F("Removing all."));
|
||||
DEBUGBUS_PRINTLN(F("Removing all."));
|
||||
//prevents crashes due to deleting busses while in use.
|
||||
while (!canAllShow()) yield();
|
||||
for (unsigned i = 0; i < numBusses; i++) delete busses[i];
|
||||
numBusses = 0;
|
||||
_parallelOutputs = 1;
|
||||
//for (auto &bus : busses) delete bus; // needed when not using std::unique_ptr C++ >11
|
||||
busses.clear();
|
||||
PolyBus::setParallelI2S1Output(false);
|
||||
}
|
||||
|
||||
@ -878,7 +903,9 @@ void BusManager::removeAll() {
|
||||
void BusManager::esp32RMTInvertIdle() {
|
||||
bool idle_out;
|
||||
unsigned rmt = 0;
|
||||
for (unsigned u = 0; u < numBusses(); u++) {
|
||||
unsigned u = 0;
|
||||
for (auto &bus : busses) {
|
||||
if (bus->getLength()==0 || !bus->isDigital() || bus->is2Pin()) continue;
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, only has 1 I2S but NPB does not support it ATM
|
||||
if (u > 1) return;
|
||||
rmt = u;
|
||||
@ -889,11 +916,11 @@ void BusManager::esp32RMTInvertIdle() {
|
||||
if (u > 3) return;
|
||||
rmt = u;
|
||||
#else
|
||||
if (u < _parallelOutputs) continue;
|
||||
if (u >= _parallelOutputs + 8) return; // only 8 RMT channels
|
||||
rmt = u - _parallelOutputs;
|
||||
unsigned numI2S = !PolyBus::isParallelI2S1Output(); // if using parallel I2S, RMT is used 1st
|
||||
if (numI2S > u) continue;
|
||||
if (u > 7 + numI2S) return;
|
||||
rmt = u - numI2S;
|
||||
#endif
|
||||
if (busses[u]->getLength()==0 || !busses[u]->isDigital() || busses[u]->is2Pin()) continue;
|
||||
//assumes that bus number to rmt channel mapping stays 1:1
|
||||
rmt_channel_t ch = static_cast<rmt_channel_t>(rmt);
|
||||
rmt_idle_level_t lvl;
|
||||
@ -902,6 +929,7 @@ void BusManager::esp32RMTInvertIdle() {
|
||||
else if (lvl == RMT_IDLE_LEVEL_LOW) lvl = RMT_IDLE_LEVEL_HIGH;
|
||||
else continue;
|
||||
rmt_set_idle_level(ch, idle_out, lvl);
|
||||
u++
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -910,12 +938,12 @@ void BusManager::on() {
|
||||
#ifdef ESP8266
|
||||
//Fix for turning off onboard LED breaking bus
|
||||
if (PinManager::getPinOwner(LED_BUILTIN) == PinOwner::BusDigital) {
|
||||
for (unsigned i = 0; i < numBusses; i++) {
|
||||
for (auto &bus : busses) {
|
||||
uint8_t pins[2] = {255,255};
|
||||
if (busses[i]->isDigital() && busses[i]->getPins(pins)) {
|
||||
if (bus->isDigital() && bus->getPins(pins)) {
|
||||
if (pins[0] == LED_BUILTIN || pins[1] == LED_BUILTIN) {
|
||||
BusDigital *bus = static_cast<BusDigital*>(busses[i]);
|
||||
bus->begin();
|
||||
BusDigital &b = static_cast<BusDigital&>(*bus);
|
||||
b.begin();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -932,7 +960,7 @@ void BusManager::off() {
|
||||
// turn off built-in LED if strip is turned off
|
||||
// this will break digital bus so will need to be re-initialised on On
|
||||
if (PinManager::getPinOwner(LED_BUILTIN) == PinOwner::BusDigital) {
|
||||
for (unsigned i = 0; i < numBusses; i++) if (busses[i]->isOffRefreshRequired()) return;
|
||||
for (const auto &bus : busses) if (bus->isOffRefreshRequired()) return;
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
}
|
||||
@ -943,30 +971,18 @@ void BusManager::off() {
|
||||
}
|
||||
|
||||
void BusManager::show() {
|
||||
_milliAmpsUsed = 0;
|
||||
for (unsigned i = 0; i < numBusses; i++) {
|
||||
busses[i]->show();
|
||||
_milliAmpsUsed += busses[i]->getUsedCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
void BusManager::setStatusPixel(uint32_t c) {
|
||||
for (unsigned i = 0; i < numBusses; i++) {
|
||||
busses[i]->setStatusPixel(c);
|
||||
_gMilliAmpsUsed = 0;
|
||||
for (auto &bus : busses) {
|
||||
bus->show();
|
||||
_gMilliAmpsUsed += bus->getUsedCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR BusManager::setPixelColor(unsigned pix, uint32_t c) {
|
||||
for (unsigned i = 0; i < numBusses; i++) {
|
||||
unsigned bstart = busses[i]->getStart();
|
||||
if (pix < bstart || pix >= bstart + busses[i]->getLength()) continue;
|
||||
busses[i]->setPixelColor(pix - bstart, c);
|
||||
}
|
||||
}
|
||||
|
||||
void BusManager::setBrightness(uint8_t b) {
|
||||
for (unsigned i = 0; i < numBusses; i++) {
|
||||
busses[i]->setBrightness(b);
|
||||
for (auto &bus : busses) {
|
||||
unsigned bstart = bus->getStart();
|
||||
if (pix < bstart || pix >= bstart + bus->getLength()) continue;
|
||||
bus->setPixelColor(pix - bstart, c);
|
||||
}
|
||||
}
|
||||
|
||||
@ -980,34 +996,23 @@ void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) {
|
||||
}
|
||||
|
||||
uint32_t BusManager::getPixelColor(unsigned pix) {
|
||||
for (unsigned i = 0; i < numBusses; i++) {
|
||||
unsigned bstart = busses[i]->getStart();
|
||||
if (!busses[i]->containsPixel(pix)) continue;
|
||||
return busses[i]->getPixelColor(pix - bstart);
|
||||
for (auto &bus : busses) {
|
||||
unsigned bstart = bus->getStart();
|
||||
if (!bus->containsPixel(pix)) continue;
|
||||
return bus->getPixelColor(pix - bstart);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool BusManager::canAllShow() {
|
||||
for (unsigned i = 0; i < numBusses; i++) {
|
||||
if (!busses[i]->canShow()) return false;
|
||||
}
|
||||
for (const auto &bus : busses) if (!bus->canShow()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Bus* BusManager::getBus(uint8_t busNr) {
|
||||
if (busNr >= numBusses) return nullptr;
|
||||
return busses[busNr];
|
||||
}
|
||||
ColorOrderMap& BusManager::getColorOrderMap() { return _colorOrderMap; }
|
||||
|
||||
//semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit())
|
||||
uint16_t BusManager::getTotalLength() {
|
||||
unsigned len = 0;
|
||||
for (unsigned i=0; i<numBusses; i++) len += busses[i]->getLength();
|
||||
return len;
|
||||
}
|
||||
|
||||
bool PolyBus::useParallelI2S = false;
|
||||
bool PolyBus::_useParallelI2S = false;
|
||||
|
||||
// Bus static member definition
|
||||
int16_t Bus::_cct = -1;
|
||||
@ -1016,9 +1021,7 @@ uint8_t Bus::_gAWM = 255;
|
||||
|
||||
uint16_t BusDigital::_milliAmpsTotal = 0;
|
||||
|
||||
uint8_t BusManager::numBusses = 0;
|
||||
Bus* BusManager::busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES];
|
||||
ColorOrderMap BusManager::colorOrderMap = {};
|
||||
uint16_t BusManager::_milliAmpsUsed = 0;
|
||||
uint16_t BusManager::_milliAmpsMax = ABL_MILLIAMPS_DEFAULT;
|
||||
uint8_t BusManager::_parallelOutputs = 1;
|
||||
std::vector<std::unique_ptr<Bus>> BusManager::busses;
|
||||
//std::vector<Bus*> BusManager::busses;
|
||||
uint16_t BusManager::_gMilliAmpsUsed = 0;
|
||||
uint16_t BusManager::_gMilliAmpsMax = ABL_MILLIAMPS_DEFAULT;
|
||||
|
@ -1,3 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef BusManager_h
|
||||
#define BusManager_h
|
||||
|
||||
@ -8,6 +9,42 @@
|
||||
#include "const.h"
|
||||
#include "pin_manager.h"
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#if __cplusplus >= 201402L
|
||||
using std::make_unique;
|
||||
#else
|
||||
// Really simple C++11 shim for non-array case; implementation from cppreference.com
|
||||
template<class T, class... Args>
|
||||
std::unique_ptr<T>
|
||||
make_unique(Args&&... args)
|
||||
{
|
||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
#endif
|
||||
|
||||
// enable additional debug output
|
||||
#if defined(WLED_DEBUG_HOST)
|
||||
#include "net_debug.h"
|
||||
#define DEBUGOUT NetDebug
|
||||
#else
|
||||
#define DEBUGOUT Serial
|
||||
#endif
|
||||
|
||||
#ifdef WLED_DEBUG_BUS
|
||||
#ifndef ESP8266
|
||||
#include <rom/rtc.h>
|
||||
#endif
|
||||
#define DEBUGBUS_PRINT(x) DEBUGOUT.print(x)
|
||||
#define DEBUGBUS_PRINTLN(x) DEBUGOUT.println(x)
|
||||
#define DEBUGBUS_PRINTF(x...) DEBUGOUT.printf(x)
|
||||
#define DEBUGBUS_PRINTF_P(x...) DEBUGOUT.printf_P(x)
|
||||
#else
|
||||
#define DEBUGBUS_PRINT(x)
|
||||
#define DEBUGBUS_PRINTLN(x)
|
||||
#define DEBUGBUS_PRINTF(x...)
|
||||
#define DEBUGBUS_PRINTF_P(x...)
|
||||
#endif
|
||||
|
||||
//colors.cpp
|
||||
uint16_t approximateKelvinFromRGB(uint32_t rgb);
|
||||
@ -69,16 +106,15 @@ class Bus {
|
||||
: _type(type)
|
||||
, _bri(255)
|
||||
, _start(start)
|
||||
, _len(len)
|
||||
, _len(std::max(len,(uint16_t)1))
|
||||
, _reversed(reversed)
|
||||
, _valid(false)
|
||||
, _needsRefresh(refresh)
|
||||
, _data(nullptr) // keep data access consistent across all types of buses
|
||||
{
|
||||
_autoWhiteMode = Bus::hasWhite(type) ? aw : RGBW_MODE_MANUAL_ONLY;
|
||||
};
|
||||
|
||||
virtual ~Bus() {} //throw the bus under the bus
|
||||
virtual ~Bus() {} //throw the bus under the bus (derived class needs to freeData())
|
||||
|
||||
virtual void begin() {};
|
||||
virtual void show() = 0;
|
||||
@ -88,14 +124,15 @@ class Bus {
|
||||
virtual void setBrightness(uint8_t b) { _bri = b; };
|
||||
virtual void setColorOrder(uint8_t co) {}
|
||||
virtual uint32_t getPixelColor(unsigned pix) const { return 0; }
|
||||
virtual uint8_t getPins(uint8_t* pinArray = nullptr) const { return 0; }
|
||||
virtual unsigned getPins(uint8_t* pinArray = nullptr) const { return 0; }
|
||||
virtual uint16_t getLength() const { return isOk() ? _len : 0; }
|
||||
virtual uint8_t getColorOrder() const { return COL_ORDER_RGB; }
|
||||
virtual uint8_t skippedLeds() const { return 0; }
|
||||
virtual unsigned skippedLeds() const { return 0; }
|
||||
virtual uint16_t getFrequency() const { return 0U; }
|
||||
virtual uint16_t getLEDCurrent() const { return 0; }
|
||||
virtual uint16_t getUsedCurrent() const { return 0; }
|
||||
virtual uint16_t getMaxCurrent() const { return 0; }
|
||||
virtual unsigned getBusSize() const { return sizeof(Bus); }
|
||||
|
||||
inline bool hasRGB() const { return _hasRgb; }
|
||||
inline bool hasWhite() const { return _hasWhite; }
|
||||
@ -111,7 +148,7 @@ class Bus {
|
||||
inline void setStart(uint16_t start) { _start = start; }
|
||||
inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; }
|
||||
inline uint8_t getAutoWhiteMode() const { return _autoWhiteMode; }
|
||||
inline uint32_t getNumberOfChannels() const { return hasWhite() + 3*hasRGB() + hasCCT(); }
|
||||
inline unsigned getNumberOfChannels() const { return hasWhite() + 3*hasRGB() + hasCCT(); }
|
||||
inline uint16_t getStart() const { return _start; }
|
||||
inline uint8_t getType() const { return _type; }
|
||||
inline bool isOk() const { return _valid; }
|
||||
@ -120,8 +157,8 @@ class Bus {
|
||||
inline bool containsPixel(uint16_t pix) const { return pix >= _start && pix < _start + _len; }
|
||||
|
||||
static inline std::vector<LEDType> getLEDTypes() { return {{TYPE_NONE, "", PSTR("None")}}; } // not used. just for reference for derived classes
|
||||
static constexpr uint32_t getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK
|
||||
static constexpr uint32_t getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); }
|
||||
static constexpr unsigned getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK
|
||||
static constexpr unsigned getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); }
|
||||
static constexpr bool hasRGB(uint8_t type) {
|
||||
return !((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_ANALOG_1CH || type == TYPE_ANALOG_2CH || type == TYPE_ONOFF);
|
||||
}
|
||||
@ -176,7 +213,6 @@ class Bus {
|
||||
bool _hasCCT;// : 1;
|
||||
//} __attribute__ ((packed));
|
||||
uint8_t _autoWhiteMode;
|
||||
uint8_t *_data;
|
||||
// global Auto White Calculation override
|
||||
static uint8_t _gAWM;
|
||||
// _cct has the following menaings (see calculateCCT() & BusManager::setSegmentCCT()):
|
||||
@ -191,14 +227,12 @@ class Bus {
|
||||
static uint8_t _cctBlend;
|
||||
|
||||
uint32_t autoWhiteCalc(uint32_t c) const;
|
||||
uint8_t *allocateData(size_t size = 1);
|
||||
void freeData() { if (_data != nullptr) free(_data); _data = nullptr; }
|
||||
};
|
||||
|
||||
|
||||
class BusDigital : public Bus {
|
||||
public:
|
||||
BusDigital(const BusConfig &bc, uint8_t nr, const ColorOrderMap &com);
|
||||
BusDigital(const BusConfig &bc, uint8_t nr);
|
||||
~BusDigital() { cleanup(); }
|
||||
|
||||
void show() override;
|
||||
@ -209,12 +243,13 @@ class BusDigital : public Bus {
|
||||
void setColorOrder(uint8_t colorOrder) override;
|
||||
[[gnu::hot]] uint32_t getPixelColor(unsigned pix) const override;
|
||||
uint8_t getColorOrder() const override { return _colorOrder; }
|
||||
uint8_t getPins(uint8_t* pinArray = nullptr) const override;
|
||||
uint8_t skippedLeds() const override { return _skip; }
|
||||
unsigned getPins(uint8_t* pinArray = nullptr) const override;
|
||||
unsigned skippedLeds() const override { return _skip; }
|
||||
uint16_t getFrequency() const override { return _frequencykHz; }
|
||||
uint16_t getLEDCurrent() const override { return _milliAmpsPerLed; }
|
||||
uint16_t getUsedCurrent() const override { return _milliAmpsTotal; }
|
||||
uint16_t getMaxCurrent() const override { return _milliAmpsMax; }
|
||||
unsigned getBusSize() const override;
|
||||
void begin() override;
|
||||
void cleanup();
|
||||
|
||||
@ -228,8 +263,8 @@ class BusDigital : public Bus {
|
||||
uint16_t _frequencykHz;
|
||||
uint8_t _milliAmpsPerLed;
|
||||
uint16_t _milliAmpsMax;
|
||||
void * _busPtr;
|
||||
const ColorOrderMap &_colorOrderMap;
|
||||
uint8_t *_data;
|
||||
void *_busPtr;
|
||||
|
||||
static uint16_t _milliAmpsTotal; // is overwitten/recalculated on each show()
|
||||
|
||||
@ -244,7 +279,7 @@ class BusDigital : public Bus {
|
||||
return c;
|
||||
}
|
||||
|
||||
uint8_t estimateCurrentAndLimitBri();
|
||||
uint8_t estimateCurrentAndLimitBri() const;
|
||||
};
|
||||
|
||||
|
||||
@ -255,16 +290,17 @@ class BusPwm : public Bus {
|
||||
|
||||
void setPixelColor(unsigned pix, uint32_t c) override;
|
||||
uint32_t getPixelColor(unsigned pix) const override; //does no index check
|
||||
uint8_t getPins(uint8_t* pinArray = nullptr) const override;
|
||||
unsigned getPins(uint8_t* pinArray = nullptr) const override;
|
||||
uint16_t getFrequency() const override { return _frequency; }
|
||||
unsigned getBusSize() const override { return sizeof(BusPwm); }
|
||||
void show() override;
|
||||
void cleanup() { deallocatePins(); }
|
||||
inline void cleanup() { deallocatePins(); }
|
||||
|
||||
static std::vector<LEDType> getLEDTypes();
|
||||
|
||||
private:
|
||||
uint8_t _pins[OUTPUT_MAX_PINS];
|
||||
uint8_t _pwmdata[OUTPUT_MAX_PINS];
|
||||
uint8_t _data[OUTPUT_MAX_PINS];
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
uint8_t _ledcStart;
|
||||
#endif
|
||||
@ -282,15 +318,16 @@ class BusOnOff : public Bus {
|
||||
|
||||
void setPixelColor(unsigned pix, uint32_t c) override;
|
||||
uint32_t getPixelColor(unsigned pix) const override;
|
||||
uint8_t getPins(uint8_t* pinArray) const override;
|
||||
unsigned getPins(uint8_t* pinArray) const override;
|
||||
unsigned getBusSize() const override { return sizeof(BusOnOff); }
|
||||
void show() override;
|
||||
void cleanup() { PinManager::deallocatePin(_pin, PinOwner::BusOnOff); }
|
||||
inline void cleanup() { PinManager::deallocatePin(_pin, PinOwner::BusOnOff); }
|
||||
|
||||
static std::vector<LEDType> getLEDTypes();
|
||||
|
||||
private:
|
||||
uint8_t _pin;
|
||||
uint8_t _onoffdata;
|
||||
uint8_t _data;
|
||||
};
|
||||
|
||||
|
||||
@ -300,9 +337,10 @@ class BusNetwork : public Bus {
|
||||
~BusNetwork() { cleanup(); }
|
||||
|
||||
bool canShow() const override { return !_broadcastLock; } // this should be a return value from UDP routine if it is still sending data out
|
||||
void setPixelColor(unsigned pix, uint32_t c) override;
|
||||
uint32_t getPixelColor(unsigned pix) const override;
|
||||
uint8_t getPins(uint8_t* pinArray = nullptr) const override;
|
||||
[[gnu::hot]] void setPixelColor(unsigned pix, uint32_t c) override;
|
||||
[[gnu::hot]] uint32_t getPixelColor(unsigned pix) const override;
|
||||
unsigned getPins(uint8_t* pinArray = nullptr) const override;
|
||||
unsigned getBusSize() const override { return sizeof(BusNetwork) + (isOk() ? _len * _UDPchannels : 0); }
|
||||
void show() override;
|
||||
void cleanup();
|
||||
|
||||
@ -313,6 +351,7 @@ class BusNetwork : public Bus {
|
||||
uint8_t _UDPtype;
|
||||
uint8_t _UDPchannels;
|
||||
bool _broadcastLock;
|
||||
uint8_t *_data;
|
||||
};
|
||||
|
||||
|
||||
@ -333,7 +372,7 @@ struct BusConfig {
|
||||
uint16_t milliAmpsMax;
|
||||
|
||||
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY, uint16_t clock_kHz=0U, bool dblBfr=false, uint8_t maPerLed=LED_MILLIAMPS_DEFAULT, uint16_t maMax=ABL_MILLIAMPS_DEFAULT)
|
||||
: count(len)
|
||||
: count(std::max(len,(uint16_t)1))
|
||||
, start(pstart)
|
||||
, colorOrder(pcolorOrder)
|
||||
, reversed(rev)
|
||||
@ -348,6 +387,16 @@ struct BusConfig {
|
||||
type = busType & 0x7F; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh)
|
||||
size_t nPins = Bus::getNumberOfPins(type);
|
||||
for (size_t i = 0; i < nPins; i++) pins[i] = ppins[i];
|
||||
DEBUGBUS_PRINTF_P(PSTR("Bus: Config (%d-%d, type:%d, CO:%d, rev:%d, skip:%d, AW:%d kHz:%d, mA:%d/%d)\n"),
|
||||
(int)start, (int)(start+len),
|
||||
(int)type,
|
||||
(int)colorOrder,
|
||||
(int)reversed,
|
||||
(int)skipAmount,
|
||||
(int)autoWhite,
|
||||
(int)frequency,
|
||||
(int)milliAmpsPerLed, (int)milliAmpsMax
|
||||
);
|
||||
}
|
||||
|
||||
//validates start and length and extends total if needed
|
||||
@ -361,6 +410,8 @@ struct BusConfig {
|
||||
if (start + count > total) total = start + count;
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned memUsage(unsigned nr = 0) const;
|
||||
};
|
||||
|
||||
|
||||
@ -374,61 +425,58 @@ struct BusConfig {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class BusManager {
|
||||
public:
|
||||
BusManager() {};
|
||||
namespace BusManager {
|
||||
|
||||
//utility to get the approx. memory usage of a given BusConfig
|
||||
static uint32_t memUsage(const BusConfig &bc);
|
||||
static uint32_t memUsage(unsigned channels, unsigned count, unsigned buses = 1);
|
||||
static uint16_t currentMilliamps() { return _milliAmpsUsed + MA_FOR_ESP; }
|
||||
static uint16_t ablMilliampsMax() { return _milliAmpsMax; }
|
||||
|
||||
static int add(const BusConfig &bc);
|
||||
static void useParallelOutput(); // workaround for inaccessible PolyBus
|
||||
|
||||
//do not call this method from system context (network callback)
|
||||
static void removeAll();
|
||||
|
||||
static void on();
|
||||
static void off();
|
||||
|
||||
static void show();
|
||||
static bool canAllShow();
|
||||
static void setStatusPixel(uint32_t c);
|
||||
[[gnu::hot]] static void setPixelColor(unsigned pix, uint32_t c);
|
||||
static void setBrightness(uint8_t b);
|
||||
// for setSegmentCCT(), cct can only be in [-1,255] range; allowWBCorrection will convert it to K
|
||||
// WARNING: setSegmentCCT() is a misleading name!!! much better would be setGlobalCCT() or just setCCT()
|
||||
static void setSegmentCCT(int16_t cct, bool allowWBCorrection = false);
|
||||
static inline void setMilliampsMax(uint16_t max) { _milliAmpsMax = max;}
|
||||
[[gnu::hot]] static uint32_t getPixelColor(unsigned pix);
|
||||
static inline int16_t getSegmentCCT() { return Bus::getCCT(); }
|
||||
|
||||
static Bus* getBus(uint8_t busNr);
|
||||
|
||||
//semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit())
|
||||
static uint16_t getTotalLength();
|
||||
static inline uint8_t getNumBusses() { return numBusses; }
|
||||
static String getLEDTypesJSONString();
|
||||
|
||||
static inline ColorOrderMap& getColorOrderMap() { return colorOrderMap; }
|
||||
|
||||
private:
|
||||
static uint8_t numBusses;
|
||||
static Bus* busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES];
|
||||
static ColorOrderMap colorOrderMap;
|
||||
static uint16_t _milliAmpsUsed;
|
||||
static uint16_t _milliAmpsMax;
|
||||
static uint8_t _parallelOutputs;
|
||||
extern std::vector<std::unique_ptr<Bus>> busses;
|
||||
//extern std::vector<Bus*> busses;
|
||||
extern uint16_t _gMilliAmpsUsed;
|
||||
extern uint16_t _gMilliAmpsMax;
|
||||
|
||||
#ifdef ESP32_DATA_IDLE_HIGH
|
||||
static void esp32RMTInvertIdle() ;
|
||||
void esp32RMTInvertIdle() ;
|
||||
#endif
|
||||
static uint8_t getNumVirtualBusses() {
|
||||
int j = 0;
|
||||
for (int i=0; i<numBusses; i++) if (busses[i]->isVirtual()) j++;
|
||||
inline size_t getNumVirtualBusses() {
|
||||
size_t j = 0;
|
||||
for (const auto &bus : busses) j += bus->isVirtual();
|
||||
return j;
|
||||
}
|
||||
|
||||
size_t memUsage();
|
||||
inline uint16_t currentMilliamps() { return _gMilliAmpsUsed + MA_FOR_ESP; }
|
||||
//inline uint16_t ablMilliampsMax() { unsigned sum = 0; for (auto &bus : busses) sum += bus->getMaxCurrent(); return sum; }
|
||||
inline uint16_t ablMilliampsMax() { return _gMilliAmpsMax; } // used for compatibility reasons (and enabling virtual global ABL)
|
||||
inline void setMilliampsMax(uint16_t max) { _gMilliAmpsMax = max;}
|
||||
|
||||
void useParallelOutput(); // workaround for inaccessible PolyBus
|
||||
bool hasParallelOutput(); // workaround for inaccessible PolyBus
|
||||
|
||||
//do not call this method from system context (network callback)
|
||||
void removeAll();
|
||||
int add(const BusConfig &bc);
|
||||
|
||||
void on();
|
||||
void off();
|
||||
|
||||
[[gnu::hot]] void setPixelColor(unsigned pix, uint32_t c);
|
||||
[[gnu::hot]] uint32_t getPixelColor(unsigned pix);
|
||||
void show();
|
||||
bool canAllShow();
|
||||
inline void setStatusPixel(uint32_t c) { for (auto &bus : busses) bus->setStatusPixel(c);}
|
||||
inline void setBrightness(uint8_t b) { for (auto &bus : busses) bus->setBrightness(b); }
|
||||
// for setSegmentCCT(), cct can only be in [-1,255] range; allowWBCorrection will convert it to K
|
||||
// WARNING: setSegmentCCT() is a misleading name!!! much better would be setGlobalCCT() or just setCCT()
|
||||
void setSegmentCCT(int16_t cct, bool allowWBCorrection = false);
|
||||
inline int16_t getSegmentCCT() { return Bus::getCCT(); }
|
||||
inline Bus* getBus(size_t busNr) { return busNr < busses.size() ? busses[busNr].get() : nullptr; }
|
||||
inline size_t getNumBusses() { return busses.size(); }
|
||||
|
||||
//semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit())
|
||||
inline uint16_t getTotalLength(bool onlyPhysical = false) {
|
||||
unsigned len = 0;
|
||||
for (const auto &bus : busses) if (!(bus->isVirtual() && onlyPhysical)) len += bus->getLength();
|
||||
return len;
|
||||
}
|
||||
String getLEDTypesJSONString();
|
||||
ColorOrderMap& getColorOrderMap();
|
||||
};
|
||||
#endif
|
||||
|
@ -1,23 +1,9 @@
|
||||
#pragma once
|
||||
#ifndef BusWrapper_h
|
||||
#define BusWrapper_h
|
||||
|
||||
//#define NPB_CONF_4STEP_CADENCE
|
||||
#include "NeoPixelBusLg.h"
|
||||
#include "bus_manager.h"
|
||||
|
||||
// temporary - these defines should actually be set in platformio.ini
|
||||
// C3: I2S0 and I2S1 methods not supported (has one I2S bus)
|
||||
// S2: I2S1 methods not supported (has one I2S bus)
|
||||
// S3: I2S0 and I2S1 methods not supported yet (has two I2S buses)
|
||||
// https://github.com/Makuna/NeoPixelBus/blob/b32f719e95ef3c35c46da5c99538017ef925c026/src/internal/Esp32_i2s.h#L4
|
||||
// https://github.com/Makuna/NeoPixelBus/blob/b32f719e95ef3c35c46da5c99538017ef925c026/src/internal/NeoEsp32RmtMethod.h#L857
|
||||
|
||||
#if !defined(WLED_NO_I2S0_PIXELBUS) && (defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3))
|
||||
#define WLED_NO_I2S0_PIXELBUS
|
||||
#endif
|
||||
#if !defined(WLED_NO_I2S1_PIXELBUS) && (defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2))
|
||||
#define WLED_NO_I2S1_PIXELBUS
|
||||
#endif
|
||||
// temporary end
|
||||
|
||||
//Hardware SPI Pins
|
||||
#define P_8266_HS_MOSI 13
|
||||
@ -55,110 +41,98 @@
|
||||
#define I_8266_DM_TM2_3 19
|
||||
#define I_8266_BB_TM2_3 20
|
||||
//UCS8903 (RGB)
|
||||
#define I_8266_U0_UCS_3 49
|
||||
#define I_8266_U1_UCS_3 50
|
||||
#define I_8266_DM_UCS_3 51
|
||||
#define I_8266_BB_UCS_3 52
|
||||
#define I_8266_U0_UCS_3 21
|
||||
#define I_8266_U1_UCS_3 22
|
||||
#define I_8266_DM_UCS_3 23
|
||||
#define I_8266_BB_UCS_3 24
|
||||
//UCS8904 (RGBW)
|
||||
#define I_8266_U0_UCS_4 53
|
||||
#define I_8266_U1_UCS_4 54
|
||||
#define I_8266_DM_UCS_4 55
|
||||
#define I_8266_BB_UCS_4 56
|
||||
#define I_8266_U0_UCS_4 25
|
||||
#define I_8266_U1_UCS_4 26
|
||||
#define I_8266_DM_UCS_4 27
|
||||
#define I_8266_BB_UCS_4 28
|
||||
//FW1906 GRBCW
|
||||
#define I_8266_U0_FW6_5 66
|
||||
#define I_8266_U1_FW6_5 67
|
||||
#define I_8266_DM_FW6_5 68
|
||||
#define I_8266_BB_FW6_5 69
|
||||
#define I_8266_U0_FW6_5 29
|
||||
#define I_8266_U1_FW6_5 30
|
||||
#define I_8266_DM_FW6_5 31
|
||||
#define I_8266_BB_FW6_5 32
|
||||
//ESP8266 APA106
|
||||
#define I_8266_U0_APA106_3 81
|
||||
#define I_8266_U1_APA106_3 82
|
||||
#define I_8266_DM_APA106_3 83
|
||||
#define I_8266_BB_APA106_3 84
|
||||
#define I_8266_U0_APA106_3 33
|
||||
#define I_8266_U1_APA106_3 34
|
||||
#define I_8266_DM_APA106_3 35
|
||||
#define I_8266_BB_APA106_3 36
|
||||
//WS2805 (RGBCW)
|
||||
#define I_8266_U0_2805_5 89
|
||||
#define I_8266_U1_2805_5 90
|
||||
#define I_8266_DM_2805_5 91
|
||||
#define I_8266_BB_2805_5 92
|
||||
#define I_8266_U0_2805_5 37
|
||||
#define I_8266_U1_2805_5 38
|
||||
#define I_8266_DM_2805_5 39
|
||||
#define I_8266_BB_2805_5 40
|
||||
//TM1914 (RGB)
|
||||
#define I_8266_U0_TM1914_3 99
|
||||
#define I_8266_U1_TM1914_3 100
|
||||
#define I_8266_DM_TM1914_3 101
|
||||
#define I_8266_BB_TM1914_3 102
|
||||
#define I_8266_U0_TM1914_3 41
|
||||
#define I_8266_U1_TM1914_3 42
|
||||
#define I_8266_DM_TM1914_3 43
|
||||
#define I_8266_BB_TM1914_3 44
|
||||
//SM16825 (RGBCW)
|
||||
#define I_8266_U0_SM16825_5 103
|
||||
#define I_8266_U1_SM16825_5 104
|
||||
#define I_8266_DM_SM16825_5 105
|
||||
#define I_8266_BB_SM16825_5 106
|
||||
#define I_8266_U0_SM16825_5 45
|
||||
#define I_8266_U1_SM16825_5 46
|
||||
#define I_8266_DM_SM16825_5 47
|
||||
#define I_8266_BB_SM16825_5 48
|
||||
|
||||
/*** ESP32 Neopixel methods ***/
|
||||
//RGB
|
||||
#define I_32_RN_NEO_3 21
|
||||
#define I_32_I0_NEO_3 22
|
||||
#define I_32_I1_NEO_3 23
|
||||
#define I_32_RN_NEO_3 1
|
||||
#define I_32_I2_NEO_3 2
|
||||
//RGBW
|
||||
#define I_32_RN_NEO_4 25
|
||||
#define I_32_I0_NEO_4 26
|
||||
#define I_32_I1_NEO_4 27
|
||||
#define I_32_RN_NEO_4 5
|
||||
#define I_32_I2_NEO_4 6
|
||||
//400Kbps
|
||||
#define I_32_RN_400_3 29
|
||||
#define I_32_I0_400_3 30
|
||||
#define I_32_I1_400_3 31
|
||||
#define I_32_RN_400_3 9
|
||||
#define I_32_I2_400_3 10
|
||||
//TM1814 (RGBW)
|
||||
#define I_32_RN_TM1_4 33
|
||||
#define I_32_I0_TM1_4 34
|
||||
#define I_32_I1_TM1_4 35
|
||||
#define I_32_RN_TM1_4 13
|
||||
#define I_32_I2_TM1_4 14
|
||||
//TM1829 (RGB)
|
||||
#define I_32_RN_TM2_3 36
|
||||
#define I_32_I0_TM2_3 37
|
||||
#define I_32_I1_TM2_3 38
|
||||
#define I_32_RN_TM2_3 17
|
||||
#define I_32_I2_TM2_3 18
|
||||
//UCS8903 (RGB)
|
||||
#define I_32_RN_UCS_3 57
|
||||
#define I_32_I0_UCS_3 58
|
||||
#define I_32_I1_UCS_3 59
|
||||
#define I_32_RN_UCS_3 21
|
||||
#define I_32_I2_UCS_3 22
|
||||
//UCS8904 (RGBW)
|
||||
#define I_32_RN_UCS_4 60
|
||||
#define I_32_I0_UCS_4 61
|
||||
#define I_32_I1_UCS_4 62
|
||||
#define I_32_RN_UCS_4 25
|
||||
#define I_32_I2_UCS_4 26
|
||||
//FW1906 GRBCW
|
||||
#define I_32_RN_FW6_5 63
|
||||
#define I_32_I0_FW6_5 64
|
||||
#define I_32_I1_FW6_5 65
|
||||
#define I_32_RN_FW6_5 29
|
||||
#define I_32_I2_FW6_5 30
|
||||
//APA106
|
||||
#define I_32_RN_APA106_3 85
|
||||
#define I_32_I0_APA106_3 86
|
||||
#define I_32_I1_APA106_3 87
|
||||
#define I_32_RN_APA106_3 33
|
||||
#define I_32_I2_APA106_3 34
|
||||
//WS2805 (RGBCW)
|
||||
#define I_32_RN_2805_5 93
|
||||
#define I_32_I0_2805_5 94
|
||||
#define I_32_I1_2805_5 95
|
||||
#define I_32_RN_2805_5 37
|
||||
#define I_32_I2_2805_5 38
|
||||
//TM1914 (RGB)
|
||||
#define I_32_RN_TM1914_3 96
|
||||
#define I_32_I0_TM1914_3 97
|
||||
#define I_32_I1_TM1914_3 98
|
||||
#define I_32_RN_TM1914_3 41
|
||||
#define I_32_I2_TM1914_3 42
|
||||
//SM16825 (RGBCW)
|
||||
#define I_32_RN_SM16825_5 107
|
||||
#define I_32_I0_SM16825_5 108
|
||||
#define I_32_I1_SM16825_5 109
|
||||
#define I_32_RN_SM16825_5 45
|
||||
#define I_32_I2_SM16825_5 46
|
||||
|
||||
//APA102
|
||||
#define I_HS_DOT_3 39 //hardware SPI
|
||||
#define I_SS_DOT_3 40 //soft SPI
|
||||
#define I_HS_DOT_3 101 //hardware SPI
|
||||
#define I_SS_DOT_3 102 //soft SPI
|
||||
|
||||
//LPD8806
|
||||
#define I_HS_LPD_3 41
|
||||
#define I_SS_LPD_3 42
|
||||
#define I_HS_LPD_3 103
|
||||
#define I_SS_LPD_3 104
|
||||
|
||||
//WS2801
|
||||
#define I_HS_WS1_3 43
|
||||
#define I_SS_WS1_3 44
|
||||
#define I_HS_WS1_3 105
|
||||
#define I_SS_WS1_3 106
|
||||
|
||||
//P9813
|
||||
#define I_HS_P98_3 45
|
||||
#define I_SS_P98_3 46
|
||||
#define I_HS_P98_3 107
|
||||
#define I_SS_P98_3 108
|
||||
|
||||
//LPD6803
|
||||
#define I_HS_LPO_3 47
|
||||
#define I_SS_LPO_3 48
|
||||
#define I_HS_LPO_3 109
|
||||
#define I_SS_LPO_3 110
|
||||
|
||||
|
||||
// In the following NeoGammaNullMethod can be replaced with NeoGammaWLEDMethod to perform Gamma correction implicitly
|
||||
@ -230,66 +204,95 @@
|
||||
|
||||
/*** ESP32 Neopixel methods ***/
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
// C3: I2S0 and I2S1 methods not supported (has one I2S bus)
|
||||
// S2: I2S0 methods supported (single & parallel), I2S1 methods not supported (has one I2S bus)
|
||||
// S3: I2S0 methods not supported, I2S1 supports LCD parallel methods (has two I2S buses)
|
||||
// https://github.com/Makuna/NeoPixelBus/blob/b32f719e95ef3c35c46da5c99538017ef925c026/src/internal/Esp32_i2s.h#L4
|
||||
// https://github.com/Makuna/NeoPixelBus/blob/b32f719e95ef3c35c46da5c99538017ef925c026/src/internal/NeoEsp32RmtMethod.h#L857
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// S3 will always use LCD parallel output
|
||||
typedef X8Ws2812xMethod X1Ws2812xMethod;
|
||||
typedef X8Sk6812Method X1Sk6812Method;
|
||||
typedef X8400KbpsMethod X1400KbpsMethod;
|
||||
typedef X8800KbpsMethod X1800KbpsMethod;
|
||||
typedef X8Tm1814Method X1Tm1814Method;
|
||||
typedef X8Tm1829Method X1Tm1829Method;
|
||||
typedef X8Apa106Method X1Apa106Method;
|
||||
typedef X8Ws2805Method X1Ws2805Method;
|
||||
typedef X8Tm1914Method X1Tm1914Method;
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
// S2 will use I2S0
|
||||
typedef NeoEsp32I2s0Ws2812xMethod X1Ws2812xMethod;
|
||||
typedef NeoEsp32I2s0Sk6812Method X1Sk6812Method;
|
||||
typedef NeoEsp32I2s0400KbpsMethod X1400KbpsMethod;
|
||||
typedef NeoEsp32I2s0800KbpsMethod X1800KbpsMethod;
|
||||
typedef NeoEsp32I2s0Tm1814Method X1Tm1814Method;
|
||||
typedef NeoEsp32I2s0Tm1829Method X1Tm1829Method;
|
||||
typedef NeoEsp32I2s0Apa106Method X1Apa106Method;
|
||||
typedef NeoEsp32I2s0Ws2805Method X1Ws2805Method;
|
||||
typedef NeoEsp32I2s0Tm1914Method X1Tm1914Method;
|
||||
#elif !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
// regular ESP32 will use I2S1
|
||||
typedef NeoEsp32I2s1Ws2812xMethod X1Ws2812xMethod;
|
||||
typedef NeoEsp32I2s1Sk6812Method X1Sk6812Method;
|
||||
typedef NeoEsp32I2s1400KbpsMethod X1400KbpsMethod;
|
||||
typedef NeoEsp32I2s1800KbpsMethod X1800KbpsMethod;
|
||||
typedef NeoEsp32I2s1Tm1814Method X1Tm1814Method;
|
||||
typedef NeoEsp32I2s1Tm1829Method X1Tm1829Method;
|
||||
typedef NeoEsp32I2s1Apa106Method X1Apa106Method;
|
||||
typedef NeoEsp32I2s1Ws2805Method X1Ws2805Method;
|
||||
typedef NeoEsp32I2s1Tm1914Method X1Tm1914Method;
|
||||
#endif
|
||||
|
||||
//RGB
|
||||
#define B_32_RN_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod>
|
||||
#define B_32_I0_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s0Ws2812xMethod, NeoGammaNullMethod>
|
||||
#define B_32_I1_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1Ws2812xMethod, NeoGammaNullMethod>
|
||||
#define B_32_I1_NEO_3P NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1X8Ws2812xMethod, NeoGammaNullMethod> // parallel I2S
|
||||
#define B_32_RN_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod> // ESP32, S2, S3, C3
|
||||
//#define B_32_IN_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2sNWs2812xMethod, NeoGammaNullMethod> // ESP32 (dynamic I2S selection)
|
||||
#define B_32_I2_NEO_3 NeoPixelBusLg<NeoGrbFeature, X1Ws2812xMethod, NeoGammaNullMethod> // ESP32, S2, S3 (automatic I2S selection, see typedef above)
|
||||
#define B_32_IP_NEO_3 NeoPixelBusLg<NeoGrbFeature, X8Ws2812xMethod, NeoGammaNullMethod> // parallel I2S (ESP32, S2, S3)
|
||||
//RGBW
|
||||
#define B_32_RN_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32RmtNSk6812Method, NeoGammaNullMethod>
|
||||
#define B_32_I0_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32I2s0Sk6812Method, NeoGammaNullMethod>
|
||||
#define B_32_I1_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32I2s1Sk6812Method, NeoGammaNullMethod>
|
||||
#define B_32_I1_NEO_4P NeoPixelBusLg<NeoGrbwFeature, NeoEsp32I2s1X8Sk6812Method, NeoGammaNullMethod> // parallel I2S
|
||||
#define B_32_I2_NEO_4 NeoPixelBusLg<NeoGrbwFeature, X1Sk6812Method, NeoGammaNullMethod>
|
||||
#define B_32_IP_NEO_4 NeoPixelBusLg<NeoGrbwFeature, X8Sk6812Method, NeoGammaNullMethod> // parallel I2S
|
||||
//400Kbps
|
||||
#define B_32_RN_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32RmtN400KbpsMethod, NeoGammaNullMethod>
|
||||
#define B_32_I0_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s0400KbpsMethod, NeoGammaNullMethod>
|
||||
#define B_32_I1_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1400KbpsMethod, NeoGammaNullMethod>
|
||||
#define B_32_I1_400_3P NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1X8400KbpsMethod, NeoGammaNullMethod> // parallel I2S
|
||||
#define B_32_I2_400_3 NeoPixelBusLg<NeoGrbFeature, X1400KbpsMethod, NeoGammaNullMethod>
|
||||
#define B_32_IP_400_3 NeoPixelBusLg<NeoGrbFeature, X8400KbpsMethod, NeoGammaNullMethod> // parallel I2S
|
||||
//TM1814 (RGBW)
|
||||
#define B_32_RN_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp32RmtNTm1814Method, NeoGammaNullMethod>
|
||||
#define B_32_I0_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp32I2s0Tm1814Method, NeoGammaNullMethod>
|
||||
#define B_32_I1_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp32I2s1Tm1814Method, NeoGammaNullMethod>
|
||||
#define B_32_I1_TM1_4P NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp32I2s1X8Tm1814Method, NeoGammaNullMethod> // parallel I2S
|
||||
#define B_32_I2_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, X1Tm1814Method, NeoGammaNullMethod>
|
||||
#define B_32_IP_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, X8Tm1814Method, NeoGammaNullMethod> // parallel I2S
|
||||
//TM1829 (RGB)
|
||||
#define B_32_RN_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp32RmtNTm1829Method, NeoGammaNullMethod>
|
||||
#define B_32_I0_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp32I2s0Tm1829Method, NeoGammaNullMethod>
|
||||
#define B_32_I1_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp32I2s1Tm1829Method, NeoGammaNullMethod>
|
||||
#define B_32_I1_TM2_3P NeoPixelBusLg<NeoBrgFeature, NeoEsp32I2s1X8Tm1829Method, NeoGammaNullMethod> // parallel I2S
|
||||
#define B_32_I2_TM2_3 NeoPixelBusLg<NeoBrgFeature, X1Tm1829Method, NeoGammaNullMethod>
|
||||
#define B_32_IP_TM2_3 NeoPixelBusLg<NeoBrgFeature, X8Tm1829Method, NeoGammaNullMethod> // parallel I2S
|
||||
//UCS8903
|
||||
#define B_32_RN_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod>
|
||||
#define B_32_I0_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp32I2s0800KbpsMethod, NeoGammaNullMethod>
|
||||
#define B_32_I1_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp32I2s1800KbpsMethod, NeoGammaNullMethod>
|
||||
#define B_32_I1_UCS_3P NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp32I2s1X8800KbpsMethod, NeoGammaNullMethod> // parallel I2S
|
||||
#define B_32_I2_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, X1800KbpsMethod, NeoGammaNullMethod>
|
||||
#define B_32_IP_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, X8800KbpsMethod, NeoGammaNullMethod> // parallel I2S
|
||||
//UCS8904
|
||||
#define B_32_RN_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod>
|
||||
#define B_32_I0_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32I2s0800KbpsMethod, NeoGammaNullMethod>
|
||||
#define B_32_I1_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32I2s1800KbpsMethod, NeoGammaNullMethod>
|
||||
#define B_32_I1_UCS_4P NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32I2s1X8800KbpsMethod, NeoGammaNullMethod>// parallel I2S
|
||||
#define B_32_I2_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, X1800KbpsMethod, NeoGammaNullMethod>
|
||||
#define B_32_IP_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, X8800KbpsMethod, NeoGammaNullMethod>// parallel I2S
|
||||
//APA106
|
||||
#define B_32_RN_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32RmtNApa106Method, NeoGammaNullMethod>
|
||||
#define B_32_I0_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s0Apa106Method, NeoGammaNullMethod>
|
||||
#define B_32_I1_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1Apa106Method, NeoGammaNullMethod>
|
||||
#define B_32_I1_APA106_3P NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1X8Apa106Method, NeoGammaNullMethod> // parallel I2S
|
||||
#define B_32_I2_APA106_3 NeoPixelBusLg<NeoGrbFeature, X1Apa106Method, NeoGammaNullMethod>
|
||||
#define B_32_IP_APA106_3 NeoPixelBusLg<NeoGrbFeature, X8Apa106Method, NeoGammaNullMethod> // parallel I2S
|
||||
//FW1906 GRBCW
|
||||
#define B_32_RN_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod>
|
||||
#define B_32_I0_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp32I2s0800KbpsMethod, NeoGammaNullMethod>
|
||||
#define B_32_I1_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp32I2s1800KbpsMethod, NeoGammaNullMethod>
|
||||
#define B_32_I1_FW6_5P NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp32I2s1X8800KbpsMethod, NeoGammaNullMethod> // parallel I2S
|
||||
#define B_32_I2_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, X1800KbpsMethod, NeoGammaNullMethod>
|
||||
#define B_32_IP_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, X8800KbpsMethod, NeoGammaNullMethod> // parallel I2S
|
||||
//WS2805 RGBWC
|
||||
#define B_32_RN_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp32RmtNWs2805Method, NeoGammaNullMethod>
|
||||
#define B_32_I0_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp32I2s0Ws2805Method, NeoGammaNullMethod>
|
||||
#define B_32_I1_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp32I2s1Ws2805Method, NeoGammaNullMethod>
|
||||
#define B_32_I1_2805_5P NeoPixelBusLg<NeoGrbwwFeature, NeoEsp32I2s1X8Ws2805Method, NeoGammaNullMethod> // parallel I2S
|
||||
#define B_32_I2_2805_5 NeoPixelBusLg<NeoGrbwwFeature, X1Ws2805Method, NeoGammaNullMethod>
|
||||
#define B_32_IP_2805_5 NeoPixelBusLg<NeoGrbwwFeature, X8Ws2805Method, NeoGammaNullMethod> // parallel I2S
|
||||
//TM1914 (RGB)
|
||||
#define B_32_RN_TM1914_3 NeoPixelBusLg<NeoGrbTm1914Feature, NeoEsp32RmtNTm1914Method, NeoGammaNullMethod>
|
||||
#define B_32_I0_TM1914_3 NeoPixelBusLg<NeoGrbTm1914Feature, NeoEsp32I2s0Tm1914Method, NeoGammaNullMethod>
|
||||
#define B_32_I1_TM1914_3 NeoPixelBusLg<NeoGrbTm1914Feature, NeoEsp32I2s1Tm1914Method, NeoGammaNullMethod>
|
||||
#define B_32_I1_TM1914_3P NeoPixelBusLg<NeoGrbTm1914Feature, NeoEsp32I2s1X8Tm1914Method, NeoGammaNullMethod> // parallel I2S
|
||||
#define B_32_I2_TM1914_3 NeoPixelBusLg<NeoGrbTm1914Feature, X1Tm1914Method, NeoGammaNullMethod>
|
||||
#define B_32_IP_TM1914_3 NeoPixelBusLg<NeoGrbTm1914Feature, X8Tm1914Method, NeoGammaNullMethod> // parallel I2S
|
||||
//Sm16825 (RGBWC)
|
||||
#define B_32_RN_SM16825_5 NeoPixelBusLg<NeoRgbcwSm16825eFeature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod>
|
||||
#define B_32_I0_SM16825_5 NeoPixelBusLg<NeoRgbcwSm16825eFeature, NeoEsp32I2s0Ws2812xMethod, NeoGammaNullMethod>
|
||||
#define B_32_I1_SM16825_5 NeoPixelBusLg<NeoRgbcwSm16825eFeature, NeoEsp32I2s1Ws2812xMethod, NeoGammaNullMethod>
|
||||
#define B_32_I1_SM16825_5P NeoPixelBusLg<NeoRgbcwSm16825eFeature, NeoEsp32I2s1X8Ws2812xMethod, NeoGammaNullMethod> // parallel I2S
|
||||
#define B_32_I2_SM16825_5 NeoPixelBusLg<NeoRgbcwSm16825eFeature, X1Ws2812xMethod, NeoGammaNullMethod>
|
||||
#define B_32_IP_SM16825_5 NeoPixelBusLg<NeoRgbcwSm16825eFeature, X8Ws2812xMethod, NeoGammaNullMethod> // parallel I2S
|
||||
#endif
|
||||
|
||||
//APA102
|
||||
@ -328,11 +331,11 @@
|
||||
//handles pointer type conversion for all possible bus types
|
||||
class PolyBus {
|
||||
private:
|
||||
static bool useParallelI2S;
|
||||
static bool _useParallelI2S;
|
||||
|
||||
public:
|
||||
static inline void setParallelI2S1Output(bool b = true) { useParallelI2S = b; }
|
||||
static inline bool isParallelI2S1Output(void) { return useParallelI2S; }
|
||||
static inline void setParallelI2S1Output(bool b = true) { _useParallelI2S = b; }
|
||||
static inline bool isParallelI2S1Output(void) { return _useParallelI2S; }
|
||||
|
||||
// initialize SPI bus speed for DotStar methods
|
||||
template <class T>
|
||||
@ -436,34 +439,19 @@ class PolyBus {
|
||||
case I_32_RN_TM1914_3: beginTM1914<B_32_RN_TM1914_3*>(busPtr); break;
|
||||
case I_32_RN_SM16825_5: (static_cast<B_32_RN_SM16825_5*>(busPtr))->Begin(); break;
|
||||
// I2S1 bus or parellel buses
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_3: if (useParallelI2S) (static_cast<B_32_I1_NEO_3P*>(busPtr))->Begin(); else (static_cast<B_32_I1_NEO_3*>(busPtr))->Begin(); break;
|
||||
case I_32_I1_NEO_4: if (useParallelI2S) (static_cast<B_32_I1_NEO_4P*>(busPtr))->Begin(); else (static_cast<B_32_I1_NEO_4*>(busPtr))->Begin(); break;
|
||||
case I_32_I1_400_3: if (useParallelI2S) (static_cast<B_32_I1_400_3P*>(busPtr))->Begin(); else (static_cast<B_32_I1_400_3*>(busPtr))->Begin(); break;
|
||||
case I_32_I1_TM1_4: if (useParallelI2S) beginTM1814<B_32_I1_TM1_4P*>(busPtr); else beginTM1814<B_32_I1_TM1_4*>(busPtr); break;
|
||||
case I_32_I1_TM2_3: if (useParallelI2S) (static_cast<B_32_I1_TM2_3P*>(busPtr))->Begin(); else (static_cast<B_32_I1_TM2_3*>(busPtr))->Begin(); break;
|
||||
case I_32_I1_UCS_3: if (useParallelI2S) (static_cast<B_32_I1_UCS_3P*>(busPtr))->Begin(); else (static_cast<B_32_I1_UCS_3*>(busPtr))->Begin(); break;
|
||||
case I_32_I1_UCS_4: if (useParallelI2S) (static_cast<B_32_I1_UCS_4P*>(busPtr))->Begin(); else (static_cast<B_32_I1_UCS_4*>(busPtr))->Begin(); break;
|
||||
case I_32_I1_FW6_5: if (useParallelI2S) (static_cast<B_32_I1_FW6_5P*>(busPtr))->Begin(); else (static_cast<B_32_I1_FW6_5*>(busPtr))->Begin(); break;
|
||||
case I_32_I1_APA106_3: if (useParallelI2S) (static_cast<B_32_I1_APA106_3P*>(busPtr))->Begin(); else (static_cast<B_32_I1_APA106_3*>(busPtr))->Begin(); break;
|
||||
case I_32_I1_2805_5: if (useParallelI2S) (static_cast<B_32_I1_2805_5P*>(busPtr))->Begin(); else (static_cast<B_32_I1_2805_5*>(busPtr))->Begin(); break;
|
||||
case I_32_I1_TM1914_3: if (useParallelI2S) beginTM1914<B_32_I1_TM1914_3P*>(busPtr); else beginTM1914<B_32_I1_TM1914_3*>(busPtr); break;
|
||||
case I_32_I1_SM16825_5: if (useParallelI2S) (static_cast<B_32_I1_SM16825_5P*>(busPtr))->Begin(); else (static_cast<B_32_I1_SM16825_5*>(busPtr))->Begin(); break;
|
||||
#endif
|
||||
// I2S0 bus
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->Begin(); break;
|
||||
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->Begin(); break;
|
||||
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->Begin(); break;
|
||||
case I_32_I0_TM1_4: beginTM1814<B_32_I0_TM1_4*>(busPtr); break;
|
||||
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->Begin(); break;
|
||||
case I_32_I0_UCS_3: (static_cast<B_32_I0_UCS_3*>(busPtr))->Begin(); break;
|
||||
case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->Begin(); break;
|
||||
case I_32_I0_FW6_5: (static_cast<B_32_I0_FW6_5*>(busPtr))->Begin(); break;
|
||||
case I_32_I0_APA106_3: (static_cast<B_32_I0_APA106_3*>(busPtr))->Begin(); break;
|
||||
case I_32_I0_2805_5: (static_cast<B_32_I0_2805_5*>(busPtr))->Begin(); break;
|
||||
case I_32_I0_TM1914_3: beginTM1914<B_32_I0_TM1914_3*>(busPtr); break;
|
||||
case I_32_I0_SM16825_5: (static_cast<B_32_I0_SM16825_5*>(busPtr))->Begin(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I2_NEO_3: if (_useParallelI2S) (static_cast<B_32_IP_NEO_3*>(busPtr))->Begin(); else (static_cast<B_32_I2_NEO_3*>(busPtr))->Begin(); break;
|
||||
case I_32_I2_NEO_4: if (_useParallelI2S) (static_cast<B_32_IP_NEO_4*>(busPtr))->Begin(); else (static_cast<B_32_I2_NEO_4*>(busPtr))->Begin(); break;
|
||||
case I_32_I2_400_3: if (_useParallelI2S) (static_cast<B_32_IP_400_3*>(busPtr))->Begin(); else (static_cast<B_32_I2_400_3*>(busPtr))->Begin(); break;
|
||||
case I_32_I2_TM1_4: if (_useParallelI2S) beginTM1814<B_32_IP_TM1_4*>(busPtr); else beginTM1814<B_32_I2_TM1_4*>(busPtr); break;
|
||||
case I_32_I2_TM2_3: if (_useParallelI2S) (static_cast<B_32_IP_TM2_3*>(busPtr))->Begin(); else (static_cast<B_32_I2_TM2_3*>(busPtr))->Begin(); break;
|
||||
case I_32_I2_UCS_3: if (_useParallelI2S) (static_cast<B_32_IP_UCS_3*>(busPtr))->Begin(); else (static_cast<B_32_I2_UCS_3*>(busPtr))->Begin(); break;
|
||||
case I_32_I2_UCS_4: if (_useParallelI2S) (static_cast<B_32_IP_UCS_4*>(busPtr))->Begin(); else (static_cast<B_32_I2_UCS_4*>(busPtr))->Begin(); break;
|
||||
case I_32_I2_FW6_5: if (_useParallelI2S) (static_cast<B_32_IP_FW6_5*>(busPtr))->Begin(); else (static_cast<B_32_I2_FW6_5*>(busPtr))->Begin(); break;
|
||||
case I_32_I2_APA106_3: if (_useParallelI2S) (static_cast<B_32_IP_APA106_3*>(busPtr))->Begin(); else (static_cast<B_32_I2_APA106_3*>(busPtr))->Begin(); break;
|
||||
case I_32_I2_2805_5: if (_useParallelI2S) (static_cast<B_32_IP_2805_5*>(busPtr))->Begin(); else (static_cast<B_32_I2_2805_5*>(busPtr))->Begin(); break;
|
||||
case I_32_I2_TM1914_3: if (_useParallelI2S) beginTM1914<B_32_IP_TM1914_3*>(busPtr); else beginTM1914<B_32_I2_TM1914_3*>(busPtr); break;
|
||||
case I_32_I2_SM16825_5: if (_useParallelI2S) (static_cast<B_32_IP_SM16825_5*>(busPtr))->Begin(); else (static_cast<B_32_I2_SM16825_5*>(busPtr))->Begin(); break;
|
||||
#endif
|
||||
// ESP32 can (and should, to avoid inadvertantly driving the chip select signal) specify the pins used for SPI, but only in begin()
|
||||
case I_HS_DOT_3: beginDotStar<B_HS_DOT_3*>(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break;
|
||||
@ -484,8 +472,8 @@ class PolyBus {
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !(defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3))
|
||||
// NOTE: "channel" is only used on ESP32 (and its variants) for RMT channel allocation
|
||||
// since 0.15.0-b3 I2S1 is favoured for classic ESP32 and moved to position 0 (channel 0) so we need to subtract 1 for correct RMT allocation
|
||||
if (useParallelI2S && channel > 7) channel -= 8; // accommodate parallel I2S1 which is used 1st on classic ESP32
|
||||
else if (channel > 0) channel--; // accommodate I2S1 which is used as 1st bus on classic ESP32
|
||||
if (!_useParallelI2S && channel > 0) channel--; // accommodate I2S1 which is used as 1st bus on classic ESP32
|
||||
// if user selected parallel I2S, RMT is used 1st (8 channels) followed by parallel I2S (8 channels)
|
||||
#endif
|
||||
void* busPtr = nullptr;
|
||||
switch (busType) {
|
||||
@ -555,34 +543,19 @@ class PolyBus {
|
||||
case I_32_RN_TM1914_3: busPtr = new B_32_RN_TM1914_3(len, pins[0], (NeoBusChannel)channel); break;
|
||||
case I_32_RN_SM16825_5: busPtr = new B_32_RN_SM16825_5(len, pins[0], (NeoBusChannel)channel); break;
|
||||
// I2S1 bus or paralell buses
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_3: if (useParallelI2S) busPtr = new B_32_I1_NEO_3P(len, pins[0]); else busPtr = new B_32_I1_NEO_3(len, pins[0]); break;
|
||||
case I_32_I1_NEO_4: if (useParallelI2S) busPtr = new B_32_I1_NEO_4P(len, pins[0]); else busPtr = new B_32_I1_NEO_4(len, pins[0]); break;
|
||||
case I_32_I1_400_3: if (useParallelI2S) busPtr = new B_32_I1_400_3P(len, pins[0]); else busPtr = new B_32_I1_400_3(len, pins[0]); break;
|
||||
case I_32_I1_TM1_4: if (useParallelI2S) busPtr = new B_32_I1_TM1_4P(len, pins[0]); else busPtr = new B_32_I1_TM1_4(len, pins[0]); break;
|
||||
case I_32_I1_TM2_3: if (useParallelI2S) busPtr = new B_32_I1_TM2_3P(len, pins[0]); else busPtr = new B_32_I1_TM2_3(len, pins[0]); break;
|
||||
case I_32_I1_UCS_3: if (useParallelI2S) busPtr = new B_32_I1_UCS_3P(len, pins[0]); else busPtr = new B_32_I1_UCS_3(len, pins[0]); break;
|
||||
case I_32_I1_UCS_4: if (useParallelI2S) busPtr = new B_32_I1_UCS_4P(len, pins[0]); else busPtr = new B_32_I1_UCS_4(len, pins[0]); break;
|
||||
case I_32_I1_APA106_3: if (useParallelI2S) busPtr = new B_32_I1_APA106_3P(len, pins[0]); else busPtr = new B_32_I1_APA106_3(len, pins[0]); break;
|
||||
case I_32_I1_FW6_5: if (useParallelI2S) busPtr = new B_32_I1_FW6_5P(len, pins[0]); else busPtr = new B_32_I1_FW6_5(len, pins[0]); break;
|
||||
case I_32_I1_2805_5: if (useParallelI2S) busPtr = new B_32_I1_2805_5P(len, pins[0]); else busPtr = new B_32_I1_2805_5(len, pins[0]); break;
|
||||
case I_32_I1_TM1914_3: if (useParallelI2S) busPtr = new B_32_I1_TM1914_3P(len, pins[0]); else busPtr = new B_32_I1_TM1914_3(len, pins[0]); break;
|
||||
case I_32_I1_SM16825_5: if (useParallelI2S) busPtr = new B_32_I1_SM16825_5P(len, pins[0]); else busPtr = new B_32_I1_SM16825_5(len, pins[0]); break;
|
||||
#endif
|
||||
// I2S0 bus
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_3: busPtr = new B_32_I0_NEO_3(len, pins[0]); break;
|
||||
case I_32_I0_NEO_4: busPtr = new B_32_I0_NEO_4(len, pins[0]); break;
|
||||
case I_32_I0_400_3: busPtr = new B_32_I0_400_3(len, pins[0]); break;
|
||||
case I_32_I0_TM1_4: busPtr = new B_32_I0_TM1_4(len, pins[0]); break;
|
||||
case I_32_I0_TM2_3: busPtr = new B_32_I0_TM2_3(len, pins[0]); break;
|
||||
case I_32_I0_UCS_3: busPtr = new B_32_I0_UCS_3(len, pins[0]); break;
|
||||
case I_32_I0_UCS_4: busPtr = new B_32_I0_UCS_4(len, pins[0]); break;
|
||||
case I_32_I0_APA106_3: busPtr = new B_32_I0_APA106_3(len, pins[0]); break;
|
||||
case I_32_I0_FW6_5: busPtr = new B_32_I0_FW6_5(len, pins[0]); break;
|
||||
case I_32_I0_2805_5: busPtr = new B_32_I0_2805_5(len, pins[0]); break;
|
||||
case I_32_I0_TM1914_3: busPtr = new B_32_I0_TM1914_3(len, pins[0]); break;
|
||||
case I_32_I0_SM16825_5: busPtr = new B_32_I0_SM16825_5(len, pins[0]); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I2_NEO_3: if (_useParallelI2S) busPtr = new B_32_IP_NEO_3(len, pins[0]); else busPtr = new B_32_I2_NEO_3(len, pins[0]); break;
|
||||
case I_32_I2_NEO_4: if (_useParallelI2S) busPtr = new B_32_IP_NEO_4(len, pins[0]); else busPtr = new B_32_I2_NEO_4(len, pins[0]); break;
|
||||
case I_32_I2_400_3: if (_useParallelI2S) busPtr = new B_32_IP_400_3(len, pins[0]); else busPtr = new B_32_I2_400_3(len, pins[0]); break;
|
||||
case I_32_I2_TM1_4: if (_useParallelI2S) busPtr = new B_32_IP_TM1_4(len, pins[0]); else busPtr = new B_32_I2_TM1_4(len, pins[0]); break;
|
||||
case I_32_I2_TM2_3: if (_useParallelI2S) busPtr = new B_32_IP_TM2_3(len, pins[0]); else busPtr = new B_32_I2_TM2_3(len, pins[0]); break;
|
||||
case I_32_I2_UCS_3: if (_useParallelI2S) busPtr = new B_32_IP_UCS_3(len, pins[0]); else busPtr = new B_32_I2_UCS_3(len, pins[0]); break;
|
||||
case I_32_I2_UCS_4: if (_useParallelI2S) busPtr = new B_32_IP_UCS_4(len, pins[0]); else busPtr = new B_32_I2_UCS_4(len, pins[0]); break;
|
||||
case I_32_I2_APA106_3: if (_useParallelI2S) busPtr = new B_32_IP_APA106_3(len, pins[0]); else busPtr = new B_32_I2_APA106_3(len, pins[0]); break;
|
||||
case I_32_I2_FW6_5: if (_useParallelI2S) busPtr = new B_32_IP_FW6_5(len, pins[0]); else busPtr = new B_32_I2_FW6_5(len, pins[0]); break;
|
||||
case I_32_I2_2805_5: if (_useParallelI2S) busPtr = new B_32_IP_2805_5(len, pins[0]); else busPtr = new B_32_I2_2805_5(len, pins[0]); break;
|
||||
case I_32_I2_TM1914_3: if (_useParallelI2S) busPtr = new B_32_IP_TM1914_3(len, pins[0]); else busPtr = new B_32_I2_TM1914_3(len, pins[0]); break;
|
||||
case I_32_I2_SM16825_5: if (_useParallelI2S) busPtr = new B_32_IP_SM16825_5(len, pins[0]); else busPtr = new B_32_I2_SM16825_5(len, pins[0]); break;
|
||||
#endif
|
||||
#endif
|
||||
// for 2-wire: pins[1] is clk, pins[0] is dat. begin expects (len, clk, dat)
|
||||
@ -669,34 +642,19 @@ class PolyBus {
|
||||
case I_32_RN_TM1914_3: (static_cast<B_32_RN_TM1914_3*>(busPtr))->Show(consistent); break;
|
||||
case I_32_RN_SM16825_5: (static_cast<B_32_RN_SM16825_5*>(busPtr))->Show(consistent); break;
|
||||
// I2S1 bus or paralell buses
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_3: if (useParallelI2S) (static_cast<B_32_I1_NEO_3P*>(busPtr))->Show(consistent); else (static_cast<B_32_I1_NEO_3*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I1_NEO_4: if (useParallelI2S) (static_cast<B_32_I1_NEO_4P*>(busPtr))->Show(consistent); else (static_cast<B_32_I1_NEO_4*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I1_400_3: if (useParallelI2S) (static_cast<B_32_I1_400_3P*>(busPtr))->Show(consistent); else (static_cast<B_32_I1_400_3*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I1_TM1_4: if (useParallelI2S) (static_cast<B_32_I1_TM1_4P*>(busPtr))->Show(consistent); else (static_cast<B_32_I1_TM1_4*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I1_TM2_3: if (useParallelI2S) (static_cast<B_32_I1_TM2_3P*>(busPtr))->Show(consistent); else (static_cast<B_32_I1_TM2_3*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I1_UCS_3: if (useParallelI2S) (static_cast<B_32_I1_UCS_3P*>(busPtr))->Show(consistent); else (static_cast<B_32_I1_UCS_3*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I1_UCS_4: if (useParallelI2S) (static_cast<B_32_I1_UCS_4P*>(busPtr))->Show(consistent); else (static_cast<B_32_I1_UCS_4*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I1_APA106_3: if (useParallelI2S) (static_cast<B_32_I1_APA106_3P*>(busPtr))->Show(consistent); else (static_cast<B_32_I1_APA106_3*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I1_FW6_5: if (useParallelI2S) (static_cast<B_32_I1_FW6_5P*>(busPtr))->Show(consistent); else (static_cast<B_32_I1_FW6_5*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I1_2805_5: if (useParallelI2S) (static_cast<B_32_I1_2805_5P*>(busPtr))->Show(consistent); else (static_cast<B_32_I1_2805_5*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I1_TM1914_3: if (useParallelI2S) (static_cast<B_32_I1_TM1914_3P*>(busPtr))->Show(consistent); else (static_cast<B_32_I1_TM1914_3*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I1_SM16825_5: if (useParallelI2S) (static_cast<B_32_I1_SM16825_5P*>(busPtr))->Show(consistent); else (static_cast<B_32_I1_SM16825_5*>(busPtr))->Show(consistent); break;
|
||||
#endif
|
||||
// I2S0 bus
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I0_UCS_3: (static_cast<B_32_I0_UCS_3*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I0_APA106_3: (static_cast<B_32_I0_APA106_3*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I0_FW6_5: (static_cast<B_32_I0_FW6_5*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I0_2805_5: (static_cast<B_32_I0_2805_5*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I0_TM1914_3: (static_cast<B_32_I0_TM1914_3*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I0_SM16825_5: (static_cast<B_32_I0_SM16825_5*>(busPtr))->Show(consistent); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I2_NEO_3: if (_useParallelI2S) (static_cast<B_32_IP_NEO_3*>(busPtr))->Show(consistent); else (static_cast<B_32_I2_NEO_3*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I2_NEO_4: if (_useParallelI2S) (static_cast<B_32_IP_NEO_4*>(busPtr))->Show(consistent); else (static_cast<B_32_I2_NEO_4*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I2_400_3: if (_useParallelI2S) (static_cast<B_32_IP_400_3*>(busPtr))->Show(consistent); else (static_cast<B_32_I2_400_3*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I2_TM1_4: if (_useParallelI2S) (static_cast<B_32_IP_TM1_4*>(busPtr))->Show(consistent); else (static_cast<B_32_I2_TM1_4*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I2_TM2_3: if (_useParallelI2S) (static_cast<B_32_IP_TM2_3*>(busPtr))->Show(consistent); else (static_cast<B_32_I2_TM2_3*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I2_UCS_3: if (_useParallelI2S) (static_cast<B_32_IP_UCS_3*>(busPtr))->Show(consistent); else (static_cast<B_32_I2_UCS_3*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I2_UCS_4: if (_useParallelI2S) (static_cast<B_32_IP_UCS_4*>(busPtr))->Show(consistent); else (static_cast<B_32_I2_UCS_4*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I2_APA106_3: if (_useParallelI2S) (static_cast<B_32_IP_APA106_3*>(busPtr))->Show(consistent); else (static_cast<B_32_I2_APA106_3*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I2_FW6_5: if (_useParallelI2S) (static_cast<B_32_IP_FW6_5*>(busPtr))->Show(consistent); else (static_cast<B_32_I2_FW6_5*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I2_2805_5: if (_useParallelI2S) (static_cast<B_32_IP_2805_5*>(busPtr))->Show(consistent); else (static_cast<B_32_I2_2805_5*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I2_TM1914_3: if (_useParallelI2S) (static_cast<B_32_IP_TM1914_3*>(busPtr))->Show(consistent); else (static_cast<B_32_I2_TM1914_3*>(busPtr))->Show(consistent); break;
|
||||
case I_32_I2_SM16825_5: if (_useParallelI2S) (static_cast<B_32_IP_SM16825_5*>(busPtr))->Show(consistent); else (static_cast<B_32_I2_SM16825_5*>(busPtr))->Show(consistent); break;
|
||||
#endif
|
||||
#endif
|
||||
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->Show(consistent); break;
|
||||
@ -743,6 +701,7 @@ class PolyBus {
|
||||
case I_8266_U0_UCS_4: return (static_cast<B_8266_U0_UCS_4*>(busPtr))->CanShow(); break;
|
||||
case I_8266_U1_UCS_4: return (static_cast<B_8266_U1_UCS_4*>(busPtr))->CanShow(); break;
|
||||
case I_8266_DM_UCS_4: return (static_cast<B_8266_DM_UCS_4*>(busPtr))->CanShow(); break;
|
||||
case I_8266_BB_UCS_4: return (static_cast<B_8266_BB_UCS_4*>(busPtr))->CanShow(); break;
|
||||
case I_8266_U0_APA106_3: return (static_cast<B_8266_U0_APA106_3*>(busPtr))->CanShow(); break;
|
||||
case I_8266_U1_APA106_3: return (static_cast<B_8266_U1_APA106_3*>(busPtr))->CanShow(); break;
|
||||
case I_8266_DM_APA106_3: return (static_cast<B_8266_DM_APA106_3*>(busPtr))->CanShow(); break;
|
||||
@ -779,34 +738,19 @@ class PolyBus {
|
||||
case I_32_RN_TM1914_3: return (static_cast<B_32_RN_TM1914_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_RN_SM16825_5: return (static_cast<B_32_RN_SM16825_5*>(busPtr))->CanShow(); break;
|
||||
// I2S1 bus or paralell buses
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_3: if (useParallelI2S) return (static_cast<B_32_I1_NEO_3P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_NEO_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_I1_NEO_4: if (useParallelI2S) return (static_cast<B_32_I1_NEO_4P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_NEO_4*>(busPtr))->CanShow(); break;
|
||||
case I_32_I1_400_3: if (useParallelI2S) return (static_cast<B_32_I1_400_3P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_400_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_I1_TM1_4: if (useParallelI2S) return (static_cast<B_32_I1_TM1_4P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_TM1_4*>(busPtr))->CanShow(); break;
|
||||
case I_32_I1_TM2_3: if (useParallelI2S) return (static_cast<B_32_I1_TM2_3P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_TM2_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_I1_UCS_3: if (useParallelI2S) return (static_cast<B_32_I1_UCS_3P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_UCS_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_I1_UCS_4: if (useParallelI2S) return (static_cast<B_32_I1_UCS_4P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_UCS_4*>(busPtr))->CanShow(); break;
|
||||
case I_32_I1_APA106_3: if (useParallelI2S) return (static_cast<B_32_I1_APA106_3P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_APA106_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_I1_FW6_5: if (useParallelI2S) return (static_cast<B_32_I1_FW6_5P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_FW6_5*>(busPtr))->CanShow(); break;
|
||||
case I_32_I1_2805_5: if (useParallelI2S) return (static_cast<B_32_I1_2805_5P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_2805_5*>(busPtr))->CanShow(); break;
|
||||
case I_32_I1_TM1914_3: if (useParallelI2S) return (static_cast<B_32_I1_TM1914_3P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_TM1914_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_I1_SM16825_5: if (useParallelI2S) return (static_cast<B_32_I1_SM16825_5P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_SM16825_5*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
// I2S0 bus
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_3: return (static_cast<B_32_I0_NEO_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_I0_NEO_4: return (static_cast<B_32_I0_NEO_4*>(busPtr))->CanShow(); break;
|
||||
case I_32_I0_400_3: return (static_cast<B_32_I0_400_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_I0_TM1_4: return (static_cast<B_32_I0_TM1_4*>(busPtr))->CanShow(); break;
|
||||
case I_32_I0_TM2_3: return (static_cast<B_32_I0_TM2_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_I0_UCS_3: return (static_cast<B_32_I0_UCS_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_I0_UCS_4: return (static_cast<B_32_I0_UCS_4*>(busPtr))->CanShow(); break;
|
||||
case I_32_I0_APA106_3: return (static_cast<B_32_I0_APA106_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_I0_FW6_5: return (static_cast<B_32_I0_FW6_5*>(busPtr))->CanShow(); break;
|
||||
case I_32_I0_2805_5: return (static_cast<B_32_I0_2805_5*>(busPtr))->CanShow(); break;
|
||||
case I_32_I0_TM1914_3: return (static_cast<B_32_I0_TM1914_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_I0_SM16825_5: return (static_cast<B_32_I0_SM16825_5*>(busPtr))->CanShow(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I2_NEO_3: if (_useParallelI2S) return (static_cast<B_32_IP_NEO_3*>(busPtr))->CanShow(); else return (static_cast<B_32_I2_NEO_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_I2_NEO_4: if (_useParallelI2S) return (static_cast<B_32_IP_NEO_4*>(busPtr))->CanShow(); else return (static_cast<B_32_I2_NEO_4*>(busPtr))->CanShow(); break;
|
||||
case I_32_I2_400_3: if (_useParallelI2S) return (static_cast<B_32_IP_400_3*>(busPtr))->CanShow(); else return (static_cast<B_32_I2_400_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_I2_TM1_4: if (_useParallelI2S) return (static_cast<B_32_IP_TM1_4*>(busPtr))->CanShow(); else return (static_cast<B_32_I2_TM1_4*>(busPtr))->CanShow(); break;
|
||||
case I_32_I2_TM2_3: if (_useParallelI2S) return (static_cast<B_32_IP_TM2_3*>(busPtr))->CanShow(); else return (static_cast<B_32_I2_TM2_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_I2_UCS_3: if (_useParallelI2S) return (static_cast<B_32_IP_UCS_3*>(busPtr))->CanShow(); else return (static_cast<B_32_I2_UCS_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_I2_UCS_4: if (_useParallelI2S) return (static_cast<B_32_IP_UCS_4*>(busPtr))->CanShow(); else return (static_cast<B_32_I2_UCS_4*>(busPtr))->CanShow(); break;
|
||||
case I_32_I2_APA106_3: if (_useParallelI2S) return (static_cast<B_32_IP_APA106_3*>(busPtr))->CanShow(); else return (static_cast<B_32_I2_APA106_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_I2_FW6_5: if (_useParallelI2S) return (static_cast<B_32_IP_FW6_5*>(busPtr))->CanShow(); else return (static_cast<B_32_I2_FW6_5*>(busPtr))->CanShow(); break;
|
||||
case I_32_I2_2805_5: if (_useParallelI2S) return (static_cast<B_32_IP_2805_5*>(busPtr))->CanShow(); else return (static_cast<B_32_I2_2805_5*>(busPtr))->CanShow(); break;
|
||||
case I_32_I2_TM1914_3: if (_useParallelI2S) return (static_cast<B_32_IP_TM1914_3*>(busPtr))->CanShow(); else return (static_cast<B_32_I2_TM1914_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_I2_SM16825_5: if (_useParallelI2S) return (static_cast<B_32_IP_SM16825_5*>(busPtr))->CanShow(); else return (static_cast<B_32_I2_SM16825_5*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
#endif
|
||||
case I_HS_DOT_3: return (static_cast<B_HS_DOT_3*>(busPtr))->CanShow(); break;
|
||||
@ -823,7 +767,7 @@ class PolyBus {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void setPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint32_t c, uint8_t co, uint16_t wwcw = 0) {
|
||||
[[gnu::hot]] static void setPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint32_t c, uint8_t co, uint16_t wwcw = 0) {
|
||||
uint8_t r = c >> 16;
|
||||
uint8_t g = c >> 8;
|
||||
uint8_t b = c >> 0;
|
||||
@ -916,34 +860,19 @@ class PolyBus {
|
||||
case I_32_RN_TM1914_3: (static_cast<B_32_RN_TM1914_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_32_RN_SM16825_5: (static_cast<B_32_RN_SM16825_5*>(busPtr))->SetPixelColor(pix, Rgbww80Color(col.R*257, col.G*257, col.B*257, cctWW*257, cctCW*257)); break;
|
||||
// I2S1 bus or paralell buses
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_3: if (useParallelI2S) (static_cast<B_32_I1_NEO_3P*>(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast<B_32_I1_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_32_I1_NEO_4: if (useParallelI2S) (static_cast<B_32_I1_NEO_4P*>(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast<B_32_I1_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
case I_32_I1_400_3: if (useParallelI2S) (static_cast<B_32_I1_400_3P*>(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast<B_32_I1_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_32_I1_TM1_4: if (useParallelI2S) (static_cast<B_32_I1_TM1_4P*>(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast<B_32_I1_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
case I_32_I1_TM2_3: if (useParallelI2S) (static_cast<B_32_I1_TM2_3P*>(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast<B_32_I1_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_32_I1_UCS_3: if (useParallelI2S) (static_cast<B_32_I1_UCS_3P*>(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast<B_32_I1_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break;
|
||||
case I_32_I1_UCS_4: if (useParallelI2S) (static_cast<B_32_I1_UCS_4P*>(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast<B_32_I1_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break;
|
||||
case I_32_I1_APA106_3: if (useParallelI2S) (static_cast<B_32_I1_APA106_3P*>(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast<B_32_I1_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_32_I1_FW6_5: if (useParallelI2S) (static_cast<B_32_I1_FW6_5P*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); else (static_cast<B_32_I1_FW6_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
|
||||
case I_32_I1_2805_5: if (useParallelI2S) (static_cast<B_32_I1_2805_5P*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); else (static_cast<B_32_I1_2805_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
|
||||
case I_32_I1_TM1914_3: if (useParallelI2S) (static_cast<B_32_I1_TM1914_3P*>(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast<B_32_I1_TM1914_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_32_I1_SM16825_5: if (useParallelI2S) (static_cast<B_32_I1_SM16825_5P*>(busPtr))->SetPixelColor(pix, Rgbww80Color(col.R*257, col.G*257, col.B*257, cctWW*257, cctCW*257)); else (static_cast<B_32_I1_SM16825_5*>(busPtr))->SetPixelColor(pix, Rgbww80Color(col.R*257, col.G*257, col.B*257, cctWW*257, cctCW*257)); break;
|
||||
#endif
|
||||
// I2S0 bus
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_32_I0_UCS_3: (static_cast<B_32_I0_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break;
|
||||
case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break;
|
||||
case I_32_I0_APA106_3: (static_cast<B_32_I0_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_32_I0_FW6_5: (static_cast<B_32_I0_FW6_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
|
||||
case I_32_I0_2805_5: (static_cast<B_32_I0_2805_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
|
||||
case I_32_I0_TM1914_3: (static_cast<B_32_I0_TM1914_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_32_I0_SM16825_5: (static_cast<B_32_I0_SM16825_5*>(busPtr))->SetPixelColor(pix, Rgbww80Color(col.R*257, col.G*257, col.B*257, cctWW*257, cctCW*257)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I2_NEO_3: if (_useParallelI2S) (static_cast<B_32_IP_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast<B_32_I2_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_32_I2_NEO_4: if (_useParallelI2S) (static_cast<B_32_IP_NEO_4*>(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast<B_32_I2_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
case I_32_I2_400_3: if (_useParallelI2S) (static_cast<B_32_IP_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast<B_32_I2_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_32_I2_TM1_4: if (_useParallelI2S) (static_cast<B_32_IP_TM1_4*>(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast<B_32_I2_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
case I_32_I2_TM2_3: if (_useParallelI2S) (static_cast<B_32_IP_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast<B_32_I2_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_32_I2_UCS_3: if (_useParallelI2S) (static_cast<B_32_IP_UCS_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast<B_32_I2_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break;
|
||||
case I_32_I2_UCS_4: if (_useParallelI2S) (static_cast<B_32_IP_UCS_4*>(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast<B_32_I2_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break;
|
||||
case I_32_I2_APA106_3: if (_useParallelI2S) (static_cast<B_32_IP_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast<B_32_I2_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_32_I2_FW6_5: if (_useParallelI2S) (static_cast<B_32_IP_FW6_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); else (static_cast<B_32_I2_FW6_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
|
||||
case I_32_I2_2805_5: if (_useParallelI2S) (static_cast<B_32_IP_2805_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); else (static_cast<B_32_I2_2805_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
|
||||
case I_32_I2_TM1914_3: if (_useParallelI2S) (static_cast<B_32_IP_TM1914_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast<B_32_I2_TM1914_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_32_I2_SM16825_5: if (_useParallelI2S) (static_cast<B_32_IP_SM16825_5*>(busPtr))->SetPixelColor(pix, Rgbww80Color(col.R*257, col.G*257, col.B*257, cctWW*257, cctCW*257)); else (static_cast<B_32_I2_SM16825_5*>(busPtr))->SetPixelColor(pix, Rgbww80Color(col.R*257, col.G*257, col.B*257, cctWW*257, cctCW*257)); break;
|
||||
#endif
|
||||
#endif
|
||||
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
@ -1027,34 +956,19 @@ class PolyBus {
|
||||
case I_32_RN_TM1914_3: (static_cast<B_32_RN_TM1914_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_RN_SM16825_5: (static_cast<B_32_RN_SM16825_5*>(busPtr))->SetLuminance(b); break;
|
||||
// I2S1 bus or paralell buses
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_3: if (useParallelI2S) (static_cast<B_32_I1_NEO_3P*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I1_NEO_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I1_NEO_4: if (useParallelI2S) (static_cast<B_32_I1_NEO_4P*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I1_NEO_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I1_400_3: if (useParallelI2S) (static_cast<B_32_I1_400_3P*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I1_400_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I1_TM1_4: if (useParallelI2S) (static_cast<B_32_I1_TM1_4P*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I1_TM1_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I1_TM2_3: if (useParallelI2S) (static_cast<B_32_I1_TM2_3P*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I1_TM2_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I1_UCS_3: if (useParallelI2S) (static_cast<B_32_I1_UCS_3P*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I1_UCS_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I1_UCS_4: if (useParallelI2S) (static_cast<B_32_I1_UCS_4P*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I1_UCS_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I1_APA106_3: if (useParallelI2S) (static_cast<B_32_I1_APA106_3P*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I1_APA106_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I1_FW6_5: if (useParallelI2S) (static_cast<B_32_I1_FW6_5P*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I1_FW6_5*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I1_2805_5: if (useParallelI2S) (static_cast<B_32_I1_2805_5P*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I1_2805_5*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I1_TM1914_3: if (useParallelI2S) (static_cast<B_32_I1_TM1914_3P*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I1_TM1914_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I1_SM16825_5: if (useParallelI2S) (static_cast<B_32_I1_SM16825_5P*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I1_SM16825_5*>(busPtr))->SetLuminance(b); break;
|
||||
#endif
|
||||
// I2S0 bus
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I0_UCS_3: (static_cast<B_32_I0_UCS_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I0_APA106_3: (static_cast<B_32_I0_APA106_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I0_FW6_5: (static_cast<B_32_I0_FW6_5*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I0_2805_5: (static_cast<B_32_I0_2805_5*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I0_TM1914_3: (static_cast<B_32_I0_TM1914_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I0_SM16825_5: (static_cast<B_32_I0_SM16825_5*>(busPtr))->SetLuminance(b); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I2_NEO_3: if (_useParallelI2S) (static_cast<B_32_IP_NEO_3*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_NEO_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I2_NEO_4: if (_useParallelI2S) (static_cast<B_32_IP_NEO_4*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_NEO_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I2_400_3: if (_useParallelI2S) (static_cast<B_32_IP_400_3*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_400_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I2_TM1_4: if (_useParallelI2S) (static_cast<B_32_IP_TM1_4*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_TM1_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I2_TM2_3: if (_useParallelI2S) (static_cast<B_32_IP_TM2_3*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_TM2_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I2_UCS_3: if (_useParallelI2S) (static_cast<B_32_IP_UCS_3*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_UCS_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I2_UCS_4: if (_useParallelI2S) (static_cast<B_32_IP_UCS_4*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_UCS_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I2_APA106_3: if (_useParallelI2S) (static_cast<B_32_IP_APA106_3*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_APA106_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I2_FW6_5: if (_useParallelI2S) (static_cast<B_32_IP_FW6_5*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_FW6_5*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I2_2805_5: if (_useParallelI2S) (static_cast<B_32_IP_2805_5*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_2805_5*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I2_TM1914_3: if (_useParallelI2S) (static_cast<B_32_IP_TM1914_3*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_TM1914_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_I2_SM16825_5: if (_useParallelI2S) (static_cast<B_32_IP_SM16825_5*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_SM16825_5*>(busPtr))->SetLuminance(b); break;
|
||||
#endif
|
||||
#endif
|
||||
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetLuminance(b); break;
|
||||
@ -1070,7 +984,7 @@ class PolyBus {
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t getPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint8_t co) {
|
||||
[[gnu::hot]] static uint32_t getPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint8_t co) {
|
||||
RgbwColor col(0,0,0,0);
|
||||
switch (busType) {
|
||||
case I_NONE: break;
|
||||
@ -1139,34 +1053,19 @@ class PolyBus {
|
||||
case I_32_RN_TM1914_3: col = (static_cast<B_32_RN_TM1914_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_RN_SM16825_5: { Rgbww80Color c = (static_cast<B_32_RN_SM16825_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R/257,c.G/257,c.B/257,max(c.WW,c.CW)/257); } break; // will not return original W
|
||||
// I2S1 bus or paralell buses
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_3: col = (useParallelI2S) ? (static_cast<B_32_I1_NEO_3P*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I1_NEO_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I1_NEO_4: col = (useParallelI2S) ? (static_cast<B_32_I1_NEO_4P*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I1_NEO_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I1_400_3: col = (useParallelI2S) ? (static_cast<B_32_I1_400_3P*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I1_400_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I1_TM1_4: col = (useParallelI2S) ? (static_cast<B_32_I1_TM1_4P*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I1_TM1_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I1_TM2_3: col = (useParallelI2S) ? (static_cast<B_32_I1_TM2_3P*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I1_TM2_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I1_UCS_3: { Rgb48Color c = (useParallelI2S) ? (static_cast<B_32_I1_UCS_3P*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I1_UCS_3*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R/257,c.G/257,c.B/257,0); } break;
|
||||
case I_32_I1_UCS_4: { Rgbw64Color c = (useParallelI2S) ? (static_cast<B_32_I1_UCS_4P*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I1_UCS_4*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R/257,c.G/257,c.B/257,c.W/257); } break;
|
||||
case I_32_I1_APA106_3: col = (useParallelI2S) ? (static_cast<B_32_I1_APA106_3P*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I1_APA106_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I1_FW6_5: { RgbwwColor c = (useParallelI2S) ? (static_cast<B_32_I1_FW6_5P*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I1_FW6_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
|
||||
case I_32_I1_2805_5: { RgbwwColor c = (useParallelI2S) ? (static_cast<B_32_I1_2805_5P*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I1_2805_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
|
||||
case I_32_I1_TM1914_3: col = (useParallelI2S) ? (static_cast<B_32_I1_TM1914_3P*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I1_TM1914_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I1_SM16825_5: { Rgbww80Color c = (useParallelI2S) ? (static_cast<B_32_I1_SM16825_5P*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I1_SM16825_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R/257,c.G/257,c.B/257,max(c.WW,c.CW)/257); } break; // will not return original W
|
||||
#endif
|
||||
// I2S0 bus
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_3: col = (static_cast<B_32_I0_NEO_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I0_NEO_4: col = (static_cast<B_32_I0_NEO_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I0_400_3: col = (static_cast<B_32_I0_400_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I0_TM1_4: col = (static_cast<B_32_I0_TM1_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I0_TM2_3: col = (static_cast<B_32_I0_TM2_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I0_UCS_3: { Rgb48Color c = (static_cast<B_32_I0_UCS_3*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R/257,c.G/257,c.B/257,0); } break;
|
||||
case I_32_I0_UCS_4: { Rgbw64Color c = (static_cast<B_32_I0_UCS_4*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R/257,c.G/257,c.B/257,c.W/257); } break;
|
||||
case I_32_I0_APA106_3: col = (static_cast<B_32_I0_APA106_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I0_FW6_5: { RgbwwColor c = (static_cast<B_32_I0_FW6_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
|
||||
case I_32_I0_2805_5: { RgbwwColor c = (static_cast<B_32_I0_2805_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
|
||||
case I_32_I0_TM1914_3: col = (static_cast<B_32_I0_TM1914_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I0_SM16825_5: { Rgbww80Color c = (static_cast<B_32_I0_SM16825_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R/257,c.G/257,c.B/257,max(c.WW,c.CW)/257); } break; // will not return original W
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I2_NEO_3: col = (_useParallelI2S) ? (static_cast<B_32_IP_NEO_3*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I2_NEO_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I2_NEO_4: col = (_useParallelI2S) ? (static_cast<B_32_IP_NEO_4*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I2_NEO_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I2_400_3: col = (_useParallelI2S) ? (static_cast<B_32_IP_400_3*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I2_400_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I2_TM1_4: col = (_useParallelI2S) ? (static_cast<B_32_IP_TM1_4*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I2_TM1_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I2_TM2_3: col = (_useParallelI2S) ? (static_cast<B_32_IP_TM2_3*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I2_TM2_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I2_UCS_3: { Rgb48Color c = (_useParallelI2S) ? (static_cast<B_32_IP_UCS_3*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I2_UCS_3*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R/257,c.G/257,c.B/257,0); } break;
|
||||
case I_32_I2_UCS_4: { Rgbw64Color c = (_useParallelI2S) ? (static_cast<B_32_IP_UCS_4*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I2_UCS_4*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R/257,c.G/257,c.B/257,c.W/257); } break;
|
||||
case I_32_I2_APA106_3: col = (_useParallelI2S) ? (static_cast<B_32_IP_APA106_3*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I2_APA106_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I2_FW6_5: { RgbwwColor c = (_useParallelI2S) ? (static_cast<B_32_IP_FW6_5*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I2_FW6_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
|
||||
case I_32_I2_2805_5: { RgbwwColor c = (_useParallelI2S) ? (static_cast<B_32_IP_2805_5*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I2_2805_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
|
||||
case I_32_I2_TM1914_3: col = (_useParallelI2S) ? (static_cast<B_32_IP_TM1914_3*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I2_TM1914_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I2_SM16825_5: { Rgbww80Color c = (_useParallelI2S) ? (static_cast<B_32_IP_SM16825_5*>(busPtr))->GetPixelColor(pix) : (static_cast<B_32_I2_SM16825_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R/257,c.G/257,c.B/257,max(c.WW,c.CW)/257); } break; // will not return original W
|
||||
#endif
|
||||
#endif
|
||||
case I_HS_DOT_3: col = (static_cast<B_HS_DOT_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
@ -1269,34 +1168,19 @@ class PolyBus {
|
||||
case I_32_RN_TM1914_3: delete (static_cast<B_32_RN_TM1914_3*>(busPtr)); break;
|
||||
case I_32_RN_SM16825_5: delete (static_cast<B_32_RN_SM16825_5*>(busPtr)); break;
|
||||
// I2S1 bus or paralell buses
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_3: if (useParallelI2S) delete (static_cast<B_32_I1_NEO_3P*>(busPtr)); else delete (static_cast<B_32_I1_NEO_3*>(busPtr)); break;
|
||||
case I_32_I1_NEO_4: if (useParallelI2S) delete (static_cast<B_32_I1_NEO_4P*>(busPtr)); else delete (static_cast<B_32_I1_NEO_4*>(busPtr)); break;
|
||||
case I_32_I1_400_3: if (useParallelI2S) delete (static_cast<B_32_I1_400_3P*>(busPtr)); else delete (static_cast<B_32_I1_400_3*>(busPtr)); break;
|
||||
case I_32_I1_TM1_4: if (useParallelI2S) delete (static_cast<B_32_I1_TM1_4P*>(busPtr)); else delete (static_cast<B_32_I1_TM1_4*>(busPtr)); break;
|
||||
case I_32_I1_TM2_3: if (useParallelI2S) delete (static_cast<B_32_I1_TM2_3P*>(busPtr)); else delete (static_cast<B_32_I1_TM2_3*>(busPtr)); break;
|
||||
case I_32_I1_UCS_3: if (useParallelI2S) delete (static_cast<B_32_I1_UCS_3P*>(busPtr)); else delete (static_cast<B_32_I1_UCS_3*>(busPtr)); break;
|
||||
case I_32_I1_UCS_4: if (useParallelI2S) delete (static_cast<B_32_I1_UCS_4P*>(busPtr)); else delete (static_cast<B_32_I1_UCS_4*>(busPtr)); break;
|
||||
case I_32_I1_APA106_3: if (useParallelI2S) delete (static_cast<B_32_I1_APA106_3P*>(busPtr)); else delete (static_cast<B_32_I1_APA106_3*>(busPtr)); break;
|
||||
case I_32_I1_FW6_5: if (useParallelI2S) delete (static_cast<B_32_I1_FW6_5P*>(busPtr)); else delete (static_cast<B_32_I1_FW6_5*>(busPtr)); break;
|
||||
case I_32_I1_2805_5: if (useParallelI2S) delete (static_cast<B_32_I1_2805_5P*>(busPtr)); else delete (static_cast<B_32_I1_2805_5*>(busPtr)); break;
|
||||
case I_32_I1_TM1914_3: if (useParallelI2S) delete (static_cast<B_32_I1_TM1914_3P*>(busPtr)); else delete (static_cast<B_32_I1_TM1914_3*>(busPtr)); break;
|
||||
case I_32_I1_SM16825_5: if (useParallelI2S) delete (static_cast<B_32_I1_SM16825_5P*>(busPtr)); else delete (static_cast<B_32_I1_SM16825_5*>(busPtr)); break;
|
||||
#endif
|
||||
// I2S0 bus
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_3: delete (static_cast<B_32_I0_NEO_3*>(busPtr)); break;
|
||||
case I_32_I0_NEO_4: delete (static_cast<B_32_I0_NEO_4*>(busPtr)); break;
|
||||
case I_32_I0_400_3: delete (static_cast<B_32_I0_400_3*>(busPtr)); break;
|
||||
case I_32_I0_TM1_4: delete (static_cast<B_32_I0_TM1_4*>(busPtr)); break;
|
||||
case I_32_I0_TM2_3: delete (static_cast<B_32_I0_TM2_3*>(busPtr)); break;
|
||||
case I_32_I0_UCS_3: delete (static_cast<B_32_I0_UCS_3*>(busPtr)); break;
|
||||
case I_32_I0_UCS_4: delete (static_cast<B_32_I0_UCS_4*>(busPtr)); break;
|
||||
case I_32_I0_APA106_3: delete (static_cast<B_32_I0_APA106_3*>(busPtr)); break;
|
||||
case I_32_I0_FW6_5: delete (static_cast<B_32_I0_FW6_5*>(busPtr)); break;
|
||||
case I_32_I0_2805_5: delete (static_cast<B_32_I0_2805_5*>(busPtr)); break;
|
||||
case I_32_I0_TM1914_3: delete (static_cast<B_32_I0_TM1914_3*>(busPtr)); break;
|
||||
case I_32_I0_SM16825_5: delete (static_cast<B_32_I0_SM16825_5*>(busPtr)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I2_NEO_3: if (_useParallelI2S) delete (static_cast<B_32_IP_NEO_3*>(busPtr)); else delete (static_cast<B_32_I2_NEO_3*>(busPtr)); break;
|
||||
case I_32_I2_NEO_4: if (_useParallelI2S) delete (static_cast<B_32_IP_NEO_4*>(busPtr)); else delete (static_cast<B_32_I2_NEO_4*>(busPtr)); break;
|
||||
case I_32_I2_400_3: if (_useParallelI2S) delete (static_cast<B_32_IP_400_3*>(busPtr)); else delete (static_cast<B_32_I2_400_3*>(busPtr)); break;
|
||||
case I_32_I2_TM1_4: if (_useParallelI2S) delete (static_cast<B_32_IP_TM1_4*>(busPtr)); else delete (static_cast<B_32_I2_TM1_4*>(busPtr)); break;
|
||||
case I_32_I2_TM2_3: if (_useParallelI2S) delete (static_cast<B_32_IP_TM2_3*>(busPtr)); else delete (static_cast<B_32_I2_TM2_3*>(busPtr)); break;
|
||||
case I_32_I2_UCS_3: if (_useParallelI2S) delete (static_cast<B_32_IP_UCS_3*>(busPtr)); else delete (static_cast<B_32_I2_UCS_3*>(busPtr)); break;
|
||||
case I_32_I2_UCS_4: if (_useParallelI2S) delete (static_cast<B_32_IP_UCS_4*>(busPtr)); else delete (static_cast<B_32_I2_UCS_4*>(busPtr)); break;
|
||||
case I_32_I2_APA106_3: if (_useParallelI2S) delete (static_cast<B_32_IP_APA106_3*>(busPtr)); else delete (static_cast<B_32_I2_APA106_3*>(busPtr)); break;
|
||||
case I_32_I2_FW6_5: if (_useParallelI2S) delete (static_cast<B_32_IP_FW6_5*>(busPtr)); else delete (static_cast<B_32_I2_FW6_5*>(busPtr)); break;
|
||||
case I_32_I2_2805_5: if (_useParallelI2S) delete (static_cast<B_32_IP_2805_5*>(busPtr)); else delete (static_cast<B_32_I2_2805_5*>(busPtr)); break;
|
||||
case I_32_I2_TM1914_3: if (_useParallelI2S) delete (static_cast<B_32_IP_TM1914_3*>(busPtr)); else delete (static_cast<B_32_I2_TM1914_3*>(busPtr)); break;
|
||||
case I_32_I2_SM16825_5: if (_useParallelI2S) delete (static_cast<B_32_IP_SM16825_5*>(busPtr)); else delete (static_cast<B_32_I2_SM16825_5*>(busPtr)); break;
|
||||
#endif
|
||||
#endif
|
||||
case I_HS_DOT_3: delete (static_cast<B_HS_DOT_3*>(busPtr)); break;
|
||||
@ -1312,8 +1196,178 @@ class PolyBus {
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned getDataSize(void* busPtr, uint8_t busType) {
|
||||
unsigned size = 0;
|
||||
switch (busType) {
|
||||
case I_NONE: break;
|
||||
#ifdef ESP8266
|
||||
case I_8266_U0_NEO_3: size = (static_cast<B_8266_U0_NEO_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U1_NEO_3: size = (static_cast<B_8266_U1_NEO_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_DM_NEO_3: size = (static_cast<B_8266_DM_NEO_3*>(busPtr))->PixelsSize()*5; break;
|
||||
case I_8266_BB_NEO_3: size = (static_cast<B_8266_BB_NEO_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U0_NEO_4: size = (static_cast<B_8266_U0_NEO_4*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U1_NEO_4: size = (static_cast<B_8266_U1_NEO_4*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_DM_NEO_4: size = (static_cast<B_8266_DM_NEO_4*>(busPtr))->PixelsSize()*5; break;
|
||||
case I_8266_BB_NEO_4: size = (static_cast<B_8266_BB_NEO_4*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U0_400_3: size = (static_cast<B_8266_U0_400_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U1_400_3: size = (static_cast<B_8266_U1_400_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_DM_400_3: size = (static_cast<B_8266_DM_400_3*>(busPtr))->PixelsSize()*5; break;
|
||||
case I_8266_BB_400_3: size = (static_cast<B_8266_BB_400_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U0_TM1_4: size = (static_cast<B_8266_U0_TM1_4*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U1_TM1_4: size = (static_cast<B_8266_U1_TM1_4*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_DM_TM1_4: size = (static_cast<B_8266_DM_TM1_4*>(busPtr))->PixelsSize()*5; break;
|
||||
case I_8266_BB_TM1_4: size = (static_cast<B_8266_BB_TM1_4*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U0_TM2_3: size = (static_cast<B_8266_U0_TM2_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U1_TM2_3: size = (static_cast<B_8266_U1_TM2_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_DM_TM2_3: size = (static_cast<B_8266_DM_TM2_3*>(busPtr))->PixelsSize()*5; break;
|
||||
case I_8266_BB_TM2_3: size = (static_cast<B_8266_BB_TM2_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U0_UCS_3: size = (static_cast<B_8266_U0_UCS_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U1_UCS_3: size = (static_cast<B_8266_U1_UCS_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_DM_UCS_3: size = (static_cast<B_8266_DM_UCS_3*>(busPtr))->PixelsSize()*5; break;
|
||||
case I_8266_BB_UCS_3: size = (static_cast<B_8266_BB_UCS_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U0_UCS_4: size = (static_cast<B_8266_U0_UCS_4*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U1_UCS_4: size = (static_cast<B_8266_U1_UCS_4*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_DM_UCS_4: size = (static_cast<B_8266_DM_UCS_4*>(busPtr))->PixelsSize()*5; break;
|
||||
case I_8266_BB_UCS_4: size = (static_cast<B_8266_BB_UCS_4*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U0_APA106_3: size = (static_cast<B_8266_U0_APA106_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U1_APA106_3: size = (static_cast<B_8266_U1_APA106_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_DM_APA106_3: size = (static_cast<B_8266_DM_APA106_3*>(busPtr))->PixelsSize()*5; break;
|
||||
case I_8266_BB_APA106_3: size = (static_cast<B_8266_BB_APA106_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U0_FW6_5: size = (static_cast<B_8266_U0_FW6_5*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U1_FW6_5: size = (static_cast<B_8266_U1_FW6_5*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_DM_FW6_5: size = (static_cast<B_8266_DM_FW6_5*>(busPtr))->PixelsSize()*5; break;
|
||||
case I_8266_BB_FW6_5: size = (static_cast<B_8266_BB_FW6_5*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U0_2805_5: size = (static_cast<B_8266_U0_2805_5*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U1_2805_5: size = (static_cast<B_8266_U1_2805_5*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_DM_2805_5: size = (static_cast<B_8266_DM_2805_5*>(busPtr))->PixelsSize()*5; break;
|
||||
case I_8266_BB_2805_5: size = (static_cast<B_8266_BB_2805_5*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U0_TM1914_3: size = (static_cast<B_8266_U0_TM1914_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U1_TM1914_3: size = (static_cast<B_8266_U1_TM1914_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_DM_TM1914_3: size = (static_cast<B_8266_DM_TM1914_3*>(busPtr))->PixelsSize()*5; break;
|
||||
case I_8266_BB_TM1914_3: size = (static_cast<B_8266_BB_TM1914_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U0_SM16825_5: size = (static_cast<B_8266_U0_SM16825_5*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_U1_SM16825_5: size = (static_cast<B_8266_U1_SM16825_5*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_8266_DM_SM16825_5: size = (static_cast<B_8266_DM_SM16825_5*>(busPtr))->PixelsSize()*5; break;
|
||||
case I_8266_BB_SM16825_5: size = (static_cast<B_8266_BB_SM16825_5*>(busPtr))->PixelsSize()*2; break;
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
// RMT buses (front + back + small system managed RMT)
|
||||
case I_32_RN_NEO_3: size = (static_cast<B_32_RN_NEO_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_32_RN_NEO_4: size = (static_cast<B_32_RN_NEO_4*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_32_RN_400_3: size = (static_cast<B_32_RN_400_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_32_RN_TM1_4: size = (static_cast<B_32_RN_TM1_4*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_32_RN_TM2_3: size = (static_cast<B_32_RN_TM2_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_32_RN_UCS_3: size = (static_cast<B_32_RN_UCS_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_32_RN_UCS_4: size = (static_cast<B_32_RN_UCS_4*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_32_RN_APA106_3: size = (static_cast<B_32_RN_APA106_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_32_RN_FW6_5: size = (static_cast<B_32_RN_FW6_5*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_32_RN_2805_5: size = (static_cast<B_32_RN_2805_5*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_32_RN_TM1914_3: size = (static_cast<B_32_RN_TM1914_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_32_RN_SM16825_5: size = (static_cast<B_32_RN_SM16825_5*>(busPtr))->PixelsSize()*2; break;
|
||||
// I2S1 bus or paralell buses (front + DMA; DMA = front * cadence, aligned to 4 bytes)
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I2_NEO_3: size = (_useParallelI2S) ? (static_cast<B_32_IP_NEO_3*>(busPtr))->PixelsSize()*4 : (static_cast<B_32_I2_NEO_3*>(busPtr))->PixelsSize()*4; break;
|
||||
case I_32_I2_NEO_4: size = (_useParallelI2S) ? (static_cast<B_32_IP_NEO_4*>(busPtr))->PixelsSize()*4 : (static_cast<B_32_I2_NEO_4*>(busPtr))->PixelsSize()*4; break;
|
||||
case I_32_I2_400_3: size = (_useParallelI2S) ? (static_cast<B_32_IP_400_3*>(busPtr))->PixelsSize()*4 : (static_cast<B_32_I2_400_3*>(busPtr))->PixelsSize()*4; break;
|
||||
case I_32_I2_TM1_4: size = (_useParallelI2S) ? (static_cast<B_32_IP_TM1_4*>(busPtr))->PixelsSize()*4 : (static_cast<B_32_I2_TM1_4*>(busPtr))->PixelsSize()*4; break;
|
||||
case I_32_I2_TM2_3: size = (_useParallelI2S) ? (static_cast<B_32_IP_TM2_3*>(busPtr))->PixelsSize()*4 : (static_cast<B_32_I2_TM2_3*>(busPtr))->PixelsSize()*4; break;
|
||||
case I_32_I2_UCS_3: size = (_useParallelI2S) ? (static_cast<B_32_IP_UCS_3*>(busPtr))->PixelsSize()*4 : (static_cast<B_32_I2_UCS_3*>(busPtr))->PixelsSize()*4; break;
|
||||
case I_32_I2_UCS_4: size = (_useParallelI2S) ? (static_cast<B_32_IP_UCS_4*>(busPtr))->PixelsSize()*4 : (static_cast<B_32_I2_UCS_4*>(busPtr))->PixelsSize()*4; break;
|
||||
case I_32_I2_APA106_3: size = (_useParallelI2S) ? (static_cast<B_32_IP_APA106_3*>(busPtr))->PixelsSize()*4 : (static_cast<B_32_I2_APA106_3*>(busPtr))->PixelsSize()*4; break;
|
||||
case I_32_I2_FW6_5: size = (_useParallelI2S) ? (static_cast<B_32_IP_FW6_5*>(busPtr))->PixelsSize()*4 : (static_cast<B_32_I2_FW6_5*>(busPtr))->PixelsSize()*4; break;
|
||||
case I_32_I2_2805_5: size = (_useParallelI2S) ? (static_cast<B_32_IP_2805_5*>(busPtr))->PixelsSize()*4 : (static_cast<B_32_I2_2805_5*>(busPtr))->PixelsSize()*4; break;
|
||||
case I_32_I2_TM1914_3: size = (_useParallelI2S) ? (static_cast<B_32_IP_TM1914_3*>(busPtr))->PixelsSize()*4 : (static_cast<B_32_I2_TM1914_3*>(busPtr))->PixelsSize()*4; break;
|
||||
case I_32_I2_SM16825_5: size = (_useParallelI2S) ? (static_cast<B_32_IP_SM16825_5*>(busPtr))->PixelsSize()*4 : (static_cast<B_32_I2_SM16825_5*>(busPtr))->PixelsSize()*4; break;
|
||||
#endif
|
||||
#endif
|
||||
case I_HS_DOT_3: size = (static_cast<B_HS_DOT_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_SS_DOT_3: size = (static_cast<B_SS_DOT_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_HS_LPD_3: size = (static_cast<B_HS_LPD_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_SS_LPD_3: size = (static_cast<B_SS_LPD_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_HS_LPO_3: size = (static_cast<B_HS_LPO_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_SS_LPO_3: size = (static_cast<B_SS_LPO_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_HS_WS1_3: size = (static_cast<B_HS_WS1_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_SS_WS1_3: size = (static_cast<B_SS_WS1_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_HS_P98_3: size = (static_cast<B_HS_P98_3*>(busPtr))->PixelsSize()*2; break;
|
||||
case I_SS_P98_3: size = (static_cast<B_SS_P98_3*>(busPtr))->PixelsSize()*2; break;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
static unsigned memUsage(unsigned count, unsigned busType) {
|
||||
unsigned size = count*3; // let's assume 3 channels, we will add count or 2*count below for 4 channels or 5 channels
|
||||
switch (busType) {
|
||||
case I_NONE: size = 0; break;
|
||||
#ifdef ESP8266
|
||||
// UART methods have front + back buffers + small UART
|
||||
case I_8266_U0_NEO_4: size = (size + count)*2; break; // 4 channels
|
||||
case I_8266_U1_NEO_4: size = (size + count)*2; break; // 4 channels
|
||||
case I_8266_BB_NEO_4: size = (size + count)*2; break; // 4 channels
|
||||
case I_8266_U0_TM1_4: size = (size + count)*2; break; // 4 channels
|
||||
case I_8266_U1_TM1_4: size = (size + count)*2; break; // 4 channels
|
||||
case I_8266_BB_TM1_4: size = (size + count)*2; break; // 4 channels
|
||||
case I_8266_U0_UCS_3: size *= 4; break; // 16 bit
|
||||
case I_8266_U1_UCS_3: size *= 4; break; // 16 bit
|
||||
case I_8266_BB_UCS_3: size *= 4; break; // 16 bit
|
||||
case I_8266_U0_UCS_4: size = (size + count)*2*2; break; // 16 bit 4 channels
|
||||
case I_8266_U1_UCS_4: size = (size + count)*2*2; break; // 16 bit 4 channels
|
||||
case I_8266_BB_UCS_4: size = (size + count)*2*2; break; // 16 bit 4 channels
|
||||
case I_8266_U0_FW6_5: size = (size + 2*count)*2; break; // 5 channels
|
||||
case I_8266_U1_FW6_5: size = (size + 2*count)*2; break; // 5channels
|
||||
case I_8266_BB_FW6_5: size = (size + 2*count)*2; break; // 5 channels
|
||||
case I_8266_U0_2805_5: size = (size + 2*count)*2; break; // 5 channels
|
||||
case I_8266_U1_2805_5: size = (size + 2*count)*2; break; // 5 channels
|
||||
case I_8266_BB_2805_5: size = (size + 2*count)*2; break; // 5 channels
|
||||
case I_8266_U0_SM16825_5: size = (size + 2*count)*2*2; break; // 16 bit 5 channels
|
||||
case I_8266_U1_SM16825_5: size = (size + 2*count)*2*2; break; // 16 bit 5 channels
|
||||
case I_8266_BB_SM16825_5: size = (size + 2*count)*2*2; break; // 16 bit 5 channels
|
||||
// DMA methods have front + DMA buffer = ((1+(3+1)) * channels)
|
||||
case I_8266_DM_NEO_3: size *= 5; break;
|
||||
case I_8266_DM_NEO_4: size = (size + count)*5; break;
|
||||
case I_8266_DM_400_3: size *= 5; break;
|
||||
case I_8266_DM_TM1_4: size = (size + count)*5; break;
|
||||
case I_8266_DM_TM2_3: size *= 5; break;
|
||||
case I_8266_DM_UCS_3: size *= 2*5; break;
|
||||
case I_8266_DM_UCS_4: size = (size + count)*2*5; break;
|
||||
case I_8266_DM_APA106_3: size *= 5; break;
|
||||
case I_8266_DM_FW6_5: size = (size + 2*count)*5; break;
|
||||
case I_8266_DM_2805_5: size = (size + 2*count)*5; break;
|
||||
case I_8266_DM_TM1914_3: size *= 5; break;
|
||||
case I_8266_DM_SM16825_5: size = (size + 2*count)*2*5; break;
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
// RMT buses (1x front and 1x back buffer)
|
||||
case I_32_RN_NEO_4: size = (size + count)*2; break;
|
||||
case I_32_RN_TM1_4: size = (size + count)*2; break;
|
||||
case I_32_RN_UCS_3: size *= 2*2; break;
|
||||
case I_32_RN_UCS_4: size = (size + count)*2*2; break;
|
||||
case I_32_RN_FW6_5: size = (size + 2*count)*2; break;
|
||||
case I_32_RN_2805_5: size = (size + 2*count)*2; break;
|
||||
case I_32_RN_SM16825_5: size = (size + 2*count)*2*2; break;
|
||||
// I2S1 bus or paralell buses (individual 1x front and 1 DMA (3x or 4x pixel count) or common back DMA buffers)
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I2_NEO_3: size *= 4; break;
|
||||
case I_32_I2_NEO_4: size = (size + count)*4; break;
|
||||
case I_32_I2_400_3: size *= 4; break;
|
||||
case I_32_I2_TM1_4: size = (size + count)*4; break;
|
||||
case I_32_I2_TM2_3: size *= 4; break;
|
||||
case I_32_I2_UCS_3: size *= 2*4; break;
|
||||
case I_32_I2_UCS_4: size = (size + count)*2*4; break;
|
||||
case I_32_I2_APA106_3: size *= 4; break;
|
||||
case I_32_I2_FW6_5: size = (size + 2*count)*4; break;
|
||||
case I_32_I2_2805_5: size = (size + 2*count)*4; break;
|
||||
case I_32_I2_TM1914_3: size *= 4; break;
|
||||
case I_32_I2_SM16825_5: size = (size + 2*count)*2*4; break;
|
||||
#endif
|
||||
#endif
|
||||
// everything else uses 2 buffers
|
||||
default: size *= 2; break;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
//gives back the internal type index (I_XX_XXX_X above) for the input
|
||||
static uint8_t getI(uint8_t busType, uint8_t* pins, uint8_t num = 0) {
|
||||
static uint8_t getI(uint8_t busType, const uint8_t* pins, uint8_t num = 0) {
|
||||
if (!Bus::isDigital(busType)) return I_NONE;
|
||||
if (Bus::is2Pin(busType)) { //SPI LED chips
|
||||
bool isHSPI = false;
|
||||
@ -1372,26 +1426,33 @@ class PolyBus {
|
||||
uint8_t offset = 0; // 0 = RMT (num 1-8), 1 = I2S0 (used by Audioreactive), 2 = I2S1
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
// ESP32-S2 only has 4 RMT channels
|
||||
if (_useParallelI2S) {
|
||||
if (num > 11) return I_NONE;
|
||||
if (num > 3) offset = 1; // use x8 parallel I2S0 channels (use last to allow Audioreactive)
|
||||
} else {
|
||||
if (num > 4) return I_NONE;
|
||||
if (num > 3) offset = 1; // only one I2S (use last to allow Audioreactive)
|
||||
if (num > 3) offset = 1; // only one I2S0 (use last to allow Audioreactive)
|
||||
}
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
// On ESP32-C3 only the first 2 RMT channels are usable for transmitting
|
||||
if (num > 1) return I_NONE;
|
||||
//if (num > 1) offset = 1; // I2S not supported yet (only 1 I2S)
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// On ESP32-S3 only the first 4 RMT channels are usable for transmitting
|
||||
if (num > 3) return I_NONE;
|
||||
//if (num > 3) offset = num -4; // I2S not supported yet
|
||||
if (_useParallelI2S) {
|
||||
if (num > 11) return I_NONE;
|
||||
if (num > 3) offset = 1; // use x8 parallel I2S LCD channels
|
||||
} else {
|
||||
if (num > 3) return I_NONE; // do not use single I2S (as it is not supported)
|
||||
}
|
||||
#else
|
||||
// standard ESP32 has 8 RMT and 2 I2S channels
|
||||
if (useParallelI2S) {
|
||||
if (num > 16) return I_NONE;
|
||||
if (num < 8) offset = 2; // prefer 8 parallel I2S1 channels
|
||||
if (num == 16) offset = 1;
|
||||
// standard ESP32 has 8 RMT and x1/x8 I2S1 channels
|
||||
if (_useParallelI2S) {
|
||||
if (num > 15) return I_NONE;
|
||||
if (num > 7) offset = 1; // 8 RMT followed by 8 I2S
|
||||
} else {
|
||||
if (num > 9) return I_NONE;
|
||||
if (num > 8) offset = 1;
|
||||
if (num == 0) offset = 2; // prefer I2S1 for 1st bus (less flickering but more RAM needed)
|
||||
if (num == 0) offset = 1; // prefer I2S1 for 1st bus (less flickering but more RAM needed)
|
||||
}
|
||||
#endif
|
||||
switch (busType) {
|
||||
|
@ -40,7 +40,7 @@ void longPressAction(uint8_t b)
|
||||
{
|
||||
if (!macroLongPress[b]) {
|
||||
switch (b) {
|
||||
case 0: setRandomColor(col); colorUpdated(CALL_MODE_BUTTON); break;
|
||||
case 0: setRandomColor(colPri); colorUpdated(CALL_MODE_BUTTON); break;
|
||||
case 1:
|
||||
if(buttonBriDirection) {
|
||||
if (bri == 255) break; // avoid unnecessary updates to brightness
|
||||
@ -230,7 +230,7 @@ void handleAnalog(uint8_t b)
|
||||
effectPalette = constrain(effectPalette, 0, strip.getPaletteCount()-1); // map is allowed to "overshoot", so we need to contrain the result
|
||||
} else if (macroDoublePress[b] == 200) {
|
||||
// primary color, hue, full saturation
|
||||
colorHStoRGB(aRead*256,255,col);
|
||||
colorHStoRGB(aRead*256,255,colPri);
|
||||
} else {
|
||||
// otherwise use "double press" for segment selection
|
||||
Segment& seg = strip.getSegment(macroDoublePress[b]);
|
||||
|
116
wled00/cfg.cpp
116
wled00/cfg.cpp
@ -20,11 +20,11 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
|
||||
//long vid = doc[F("vid")]; // 2010020
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
JsonObject ethernet = doc[F("eth")];
|
||||
CJSON(ethernetType, ethernet["type"]);
|
||||
// NOTE: Ethernet configuration takes priority over other use of pins
|
||||
WLED::instance().initEthernet();
|
||||
initEthernet();
|
||||
#endif
|
||||
|
||||
JsonObject id = doc["id"];
|
||||
@ -53,9 +53,11 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
JsonArray sn = wifi["sn"];
|
||||
char ssid[33] = "";
|
||||
char pass[65] = "";
|
||||
char bssid[13] = "";
|
||||
IPAddress nIP = (uint32_t)0U, nGW = (uint32_t)0U, nSN = (uint32_t)0x00FFFFFF; // little endian
|
||||
getStringFromJson(ssid, wifi[F("ssid")], 33);
|
||||
getStringFromJson(pass, wifi["psk"], 65); // password is not normally present but if it is, use it
|
||||
getStringFromJson(bssid, wifi[F("bssid")], 13);
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
CJSON(nIP[i], ip[i]);
|
||||
CJSON(nGW[i], gw[i]);
|
||||
@ -63,6 +65,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
}
|
||||
if (strlen(ssid) > 0) strlcpy(multiWiFi[n].clientSSID, ssid, 33); // this will keep old SSID intact if not present in JSON
|
||||
if (strlen(pass) > 0) strlcpy(multiWiFi[n].clientPass, pass, 65); // this will keep old password intact if not present in JSON
|
||||
if (strlen(bssid) > 0) fillStr2MAC(multiWiFi[n].bssid, bssid);
|
||||
multiWiFi[n].staticIP = nIP;
|
||||
multiWiFi[n].staticGW = nGW;
|
||||
multiWiFi[n].staticSN = nSN;
|
||||
@ -118,6 +121,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
Bus::setCCTBlend(cctBlending);
|
||||
strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS
|
||||
CJSON(useGlobalLedBuffer, hw_led[F("ld")]);
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
CJSON(useParallelI2S, hw_led[F("prl")]);
|
||||
#endif
|
||||
|
||||
#ifndef WLED_DISABLE_2D
|
||||
// 2D Matrix Settings
|
||||
@ -162,37 +168,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
DEBUG_PRINTF_P(PSTR("Heap before buses: %d\n"), ESP.getFreeHeap());
|
||||
int s = 0; // bus iterator
|
||||
if (fromFS) BusManager::removeAll(); // can't safely manipulate busses directly in network callback
|
||||
unsigned mem = 0;
|
||||
|
||||
// determine if it is sensible to use parallel I2S outputs on ESP32 (i.e. more than 5 outputs = 1 I2S + 4 RMT)
|
||||
bool useParallel = false;
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH_ESP32S2) && !defined(ARDUINO_ARCH_ESP32S3) && !defined(ARDUINO_ARCH_ESP32C3)
|
||||
unsigned digitalCount = 0;
|
||||
unsigned maxLedsOnBus = 0;
|
||||
unsigned maxChannels = 0;
|
||||
for (JsonObject elm : ins) {
|
||||
unsigned type = elm["type"] | TYPE_WS2812_RGB;
|
||||
unsigned len = elm["len"] | DEFAULT_LED_COUNT;
|
||||
if (!Bus::isDigital(type)) continue;
|
||||
if (!Bus::is2Pin(type)) {
|
||||
digitalCount++;
|
||||
unsigned channels = Bus::getNumberOfChannels(type);
|
||||
if (len > maxLedsOnBus) maxLedsOnBus = len;
|
||||
if (channels > maxChannels) maxChannels = channels;
|
||||
}
|
||||
}
|
||||
DEBUG_PRINTF_P(PSTR("Maximum LEDs on a bus: %u\nDigital buses: %u\n"), maxLedsOnBus, digitalCount);
|
||||
// we may remove 300 LEDs per bus limit when NeoPixelBus is updated beyond 2.9.0
|
||||
if (maxLedsOnBus <= 300 && digitalCount > 5) {
|
||||
DEBUG_PRINTLN(F("Switching to parallel I2S."));
|
||||
useParallel = true;
|
||||
BusManager::useParallelOutput();
|
||||
mem = BusManager::memUsage(maxChannels, maxLedsOnBus, 8); // use alternate memory calculation
|
||||
}
|
||||
#endif
|
||||
|
||||
for (JsonObject elm : ins) {
|
||||
if (s >= WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES) break;
|
||||
if (s >= WLED_MAX_BUSSES) break;
|
||||
uint8_t pins[5] = {255, 255, 255, 255, 255};
|
||||
JsonArray pinArr = elm["pin"];
|
||||
if (pinArr.size() == 0) continue;
|
||||
@ -220,26 +198,14 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
maMax = 0;
|
||||
}
|
||||
ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh
|
||||
if (fromFS) {
|
||||
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax);
|
||||
if (useParallel && s < 8) {
|
||||
// if for some unexplained reason the above pre-calculation was wrong, update
|
||||
unsigned memT = BusManager::memUsage(bc); // includes x8 memory allocation for parallel I2S
|
||||
if (memT > mem) mem = memT; // if we have unequal LED count use the largest
|
||||
} else
|
||||
mem += BusManager::memUsage(bc); // includes global buffer
|
||||
if (mem <= MAX_LED_MEMORY) if (BusManager::add(bc) == -1) break; // finalization will be done in WLED::beginStrip()
|
||||
} else {
|
||||
if (busConfigs[s] != nullptr) delete busConfigs[s];
|
||||
busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax);
|
||||
|
||||
//busConfigs.push_back(std::move(BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax)));
|
||||
busConfigs.emplace_back(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax);
|
||||
doInitBusses = true; // finalization done in beginStrip()
|
||||
if (!Bus::isVirtual(ledType)) s++; // have as many virtual buses as you want
|
||||
}
|
||||
s++;
|
||||
}
|
||||
DEBUG_PRINTF_P(PSTR("LED buffer size: %uB\n"), mem);
|
||||
DEBUG_PRINTF_P(PSTR("Heap after buses: %d\n"), ESP.getFreeHeap());
|
||||
}
|
||||
if (hw_led["rev"]) BusManager::getBus(0)->setReversed(true); //set 0.11 global reversed setting for first bus
|
||||
if (hw_led["rev"] && BusManager::getNumBusses()) BusManager::getBus(0)->setReversed(true); //set 0.11 global reversed setting for first bus
|
||||
|
||||
// read color order map configuration
|
||||
JsonArray hw_com = hw[F("com")];
|
||||
@ -705,10 +671,10 @@ void deserializeConfigFromFS() {
|
||||
// call readFromConfig() with an empty object so that usermods can initialize to defaults prior to saving
|
||||
JsonObject empty = JsonObject();
|
||||
UsermodManager::readFromConfig(empty);
|
||||
serializeConfig();
|
||||
serializeConfigToFS();
|
||||
// init Ethernet (in case default type is set at compile time)
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
WLED::instance().initEthernet();
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
initEthernet();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
@ -719,10 +685,10 @@ void deserializeConfigFromFS() {
|
||||
bool needsSave = deserializeConfig(root, true);
|
||||
releaseJSONBufferLock();
|
||||
|
||||
if (needsSave) serializeConfig(); // usermods required new parameters
|
||||
if (needsSave) serializeConfigToFS(); // usermods required new parameters
|
||||
}
|
||||
|
||||
void serializeConfig() {
|
||||
void serializeConfigToFS() {
|
||||
serializeConfigSec();
|
||||
|
||||
DEBUG_PRINTLN(F("Writing settings to /cfg.json..."));
|
||||
@ -731,6 +697,17 @@ void serializeConfig() {
|
||||
|
||||
JsonObject root = pDoc->to<JsonObject>();
|
||||
|
||||
serializeConfig(root);
|
||||
|
||||
File f = WLED_FS.open(FPSTR(s_cfg_json), "w");
|
||||
if (f) serializeJson(root, f);
|
||||
f.close();
|
||||
releaseJSONBufferLock();
|
||||
|
||||
configNeedsWrite = false;
|
||||
}
|
||||
|
||||
void serializeConfig(JsonObject root) {
|
||||
JsonArray rev = root.createNestedArray("rev");
|
||||
rev.add(1); //major settings revision
|
||||
rev.add(0); //minor settings revision
|
||||
@ -756,6 +733,9 @@ void serializeConfig() {
|
||||
JsonObject wifi = nw_ins.createNestedObject();
|
||||
wifi[F("ssid")] = multiWiFi[n].clientSSID;
|
||||
wifi[F("pskl")] = strlen(multiWiFi[n].clientPass);
|
||||
char bssid[13];
|
||||
fillMAC2Str(bssid, multiWiFi[n].bssid);
|
||||
wifi[F("bssid")] = bssid;
|
||||
JsonArray wifi_ip = wifi.createNestedArray("ip");
|
||||
JsonArray wifi_gw = wifi.createNestedArray("gw");
|
||||
JsonArray wifi_sn = wifi.createNestedArray("sn");
|
||||
@ -791,7 +771,7 @@ void serializeConfig() {
|
||||
wifi[F("txpwr")] = txPower;
|
||||
#endif
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
JsonObject ethernet = root.createNestedObject("eth");
|
||||
ethernet["type"] = ethernetType;
|
||||
if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) {
|
||||
@ -828,6 +808,9 @@ void serializeConfig() {
|
||||
hw_led["fps"] = strip.getTargetFps();
|
||||
hw_led[F("rgbwm")] = Bus::getGlobalAWMode(); // global auto white mode override
|
||||
hw_led[F("ld")] = useGlobalLedBuffer;
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
hw_led[F("prl")] = BusManager::hasParallelOutput();
|
||||
#endif
|
||||
|
||||
#ifndef WLED_DISABLE_2D
|
||||
// 2D Matrix Settings
|
||||
@ -852,8 +835,19 @@ void serializeConfig() {
|
||||
JsonArray hw_led_ins = hw_led.createNestedArray("ins");
|
||||
|
||||
for (size_t s = 0; s < BusManager::getNumBusses(); s++) {
|
||||
Bus *bus = BusManager::getBus(s);
|
||||
if (!bus || bus->getLength()==0) break;
|
||||
DEBUG_PRINTF_P(PSTR("Cfg: Saving bus #%u\n"), s);
|
||||
const Bus *bus = BusManager::getBus(s);
|
||||
if (!bus || !bus->isOk()) break;
|
||||
DEBUG_PRINTF_P(PSTR(" (%d-%d, type:%d, CO:%d, rev:%d, skip:%d, AW:%d kHz:%d, mA:%d/%d)\n"),
|
||||
(int)bus->getStart(), (int)(bus->getStart()+bus->getLength()),
|
||||
(int)(bus->getType() & 0x7F),
|
||||
(int)bus->getColorOrder(),
|
||||
(int)bus->isReversed(),
|
||||
(int)bus->skippedLeds(),
|
||||
(int)bus->getAutoWhiteMode(),
|
||||
(int)bus->getFrequency(),
|
||||
(int)bus->getLEDCurrent(), (int)bus->getMaxCurrent()
|
||||
);
|
||||
JsonObject ins = hw_led_ins.createNestedObject();
|
||||
ins["start"] = bus->getStart();
|
||||
ins["len"] = bus->getLength();
|
||||
@ -876,8 +870,7 @@ void serializeConfig() {
|
||||
const ColorOrderMap& com = BusManager::getColorOrderMap();
|
||||
for (size_t s = 0; s < com.count(); s++) {
|
||||
const ColorOrderMapEntry *entry = com.get(s);
|
||||
if (!entry) break;
|
||||
|
||||
if (!entry || !entry->len) break;
|
||||
JsonObject co = hw_com.createNestedObject();
|
||||
co["start"] = entry->start;
|
||||
co["len"] = entry->len;
|
||||
@ -1129,13 +1122,6 @@ void serializeConfig() {
|
||||
|
||||
JsonObject usermods_settings = root.createNestedObject("um");
|
||||
UsermodManager::addToConfig(usermods_settings);
|
||||
|
||||
File f = WLED_FS.open(FPSTR(s_cfg_json), "w");
|
||||
if (f) serializeJson(root, f);
|
||||
f.close();
|
||||
releaseJSONBufferLock();
|
||||
|
||||
doSerializeConfig = false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -10,26 +10,28 @@
|
||||
*/
|
||||
uint32_t color_blend(uint32_t color1, uint32_t color2, uint8_t blend) {
|
||||
// min / max blend checking is omitted: calls with 0 or 255 are rare, checking lowers overall performance
|
||||
uint32_t rb1 = color1 & 0x00FF00FF;
|
||||
uint32_t wg1 = (color1>>8) & 0x00FF00FF;
|
||||
uint32_t rb2 = color2 & 0x00FF00FF;
|
||||
uint32_t wg2 = (color2>>8) & 0x00FF00FF;
|
||||
uint32_t rb3 = ((((rb1 << 8) | rb2) + (rb2 * blend) - (rb1 * blend)) >> 8) & 0x00FF00FF;
|
||||
uint32_t wg3 = ((((wg1 << 8) | wg2) + (wg2 * blend) - (wg1 * blend))) & 0xFF00FF00;
|
||||
const uint32_t TWO_CHANNEL_MASK = 0x00FF00FF; // mask for R and B channels or W and G if negated (poorman's SIMD; https://github.com/wled/WLED/pull/4568#discussion_r1986587221)
|
||||
uint32_t rb1 = color1 & TWO_CHANNEL_MASK; // extract R & B channels from color1
|
||||
uint32_t wg1 = (color1 >> 8) & TWO_CHANNEL_MASK; // extract W & G channels from color1 (shifted for multiplication later)
|
||||
uint32_t rb2 = color2 & TWO_CHANNEL_MASK; // extract R & B channels from color2
|
||||
uint32_t wg2 = (color2 >> 8) & TWO_CHANNEL_MASK; // extract W & G channels from color2 (shifted for multiplication later)
|
||||
uint32_t rb3 = ((((rb1 << 8) | rb2) + (rb2 * blend) - (rb1 * blend)) >> 8) & TWO_CHANNEL_MASK; // blend red and blue
|
||||
uint32_t wg3 = ((((wg1 << 8) | wg2) + (wg2 * blend) - (wg1 * blend))) & ~TWO_CHANNEL_MASK; // negated mask for white and green
|
||||
return rb3 | wg3;
|
||||
}
|
||||
|
||||
/*
|
||||
* color add function that preserves ratio
|
||||
* original idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule
|
||||
* original idea: https://github.com/wled-dev/WLED/pull/2465 by https://github.com/Proto-molecule
|
||||
* speed optimisations by @dedehai
|
||||
*/
|
||||
uint32_t color_add(uint32_t c1, uint32_t c2, bool preserveCR)
|
||||
{
|
||||
if (c1 == BLACK) return c2;
|
||||
if (c2 == BLACK) return c1;
|
||||
uint32_t rb = (c1 & 0x00FF00FF) + (c2 & 0x00FF00FF); // mask and add two colors at once
|
||||
uint32_t wg = ((c1>>8) & 0x00FF00FF) + ((c2>>8) & 0x00FF00FF);
|
||||
const uint32_t TWO_CHANNEL_MASK = 0x00FF00FF; // mask for R and B channels or W and G if negated
|
||||
uint32_t rb = ( c1 & TWO_CHANNEL_MASK) + ( c2 & TWO_CHANNEL_MASK); // mask and add two colors at once
|
||||
uint32_t wg = ((c1>>8) & TWO_CHANNEL_MASK) + ((c2>>8) & TWO_CHANNEL_MASK);
|
||||
uint32_t r = rb >> 16; // extract single color values
|
||||
uint32_t b = rb & 0xFFFF;
|
||||
uint32_t w = wg >> 16;
|
||||
@ -44,10 +46,10 @@ uint32_t color_add(uint32_t c1, uint32_t c2, bool preserveCR)
|
||||
//max = b > max ? b : max;
|
||||
//max = w > max ? w : max;
|
||||
if (max > 255) {
|
||||
uint32_t scale = (uint32_t(255)<<8) / max; // division of two 8bit (shifted) values does not work -> use bit shifts and multiplaction instead
|
||||
rb = ((rb * scale) >> 8) & 0x00FF00FF; //
|
||||
wg = (wg * scale) & 0xFF00FF00;
|
||||
} else wg = wg << 8; //shift white and green back to correct position
|
||||
const uint32_t scale = (uint32_t(255)<<8) / max; // division of two 8bit (shifted) values does not work -> use bit shifts and multiplaction instead
|
||||
rb = ((rb * scale) >> 8) & TWO_CHANNEL_MASK;
|
||||
wg = (wg * scale) & ~TWO_CHANNEL_MASK;
|
||||
} else wg <<= 8; //shift white and green back to correct position
|
||||
return rb | wg;
|
||||
} else {
|
||||
r = r > 255 ? 255 : r;
|
||||
@ -77,8 +79,9 @@ uint32_t color_fade(uint32_t c1, uint8_t amount, bool video)
|
||||
addRemains |= B(c1) ? 0x00000001 : 0;
|
||||
addRemains |= W(c1) ? 0x01000000 : 0;
|
||||
}
|
||||
uint32_t rb = (((c1 & 0x00FF00FF) * scale) >> 8) & 0x00FF00FF; // scale red and blue
|
||||
uint32_t wg = (((c1 & 0xFF00FF00) >> 8) * scale) & 0xFF00FF00; // scale white and green
|
||||
const uint32_t TWO_CHANNEL_MASK = 0x00FF00FF;
|
||||
uint32_t rb = (((c1 & TWO_CHANNEL_MASK) * scale) >> 8) & TWO_CHANNEL_MASK; // scale red and blue
|
||||
uint32_t wg = (((c1 >> 8) & TWO_CHANNEL_MASK) * scale) & ~TWO_CHANNEL_MASK; // scale white and green
|
||||
scaledcolor = (rb | wg) + addRemains;
|
||||
return scaledcolor;
|
||||
}
|
||||
@ -87,25 +90,27 @@ uint32_t color_fade(uint32_t c1, uint8_t amount, bool video)
|
||||
uint32_t ColorFromPaletteWLED(const CRGBPalette16& pal, unsigned index, uint8_t brightness, TBlendType blendType)
|
||||
{
|
||||
if (blendType == LINEARBLEND_NOWRAP) {
|
||||
index = (index*240) >> 8; // Blend range is affected by lo4 blend of values, remap to avoid wrapping
|
||||
index = (index * 0xF0) >> 8; // Blend range is affected by lo4 blend of values, remap to avoid wrapping
|
||||
}
|
||||
unsigned hi4 = byte(index) >> 4;
|
||||
const CRGB* entry = (CRGB*)((uint8_t*)(&(pal[0])) + (hi4 * sizeof(CRGB)));
|
||||
unsigned lo4 = (index & 0x0F);
|
||||
const CRGB* entry = (CRGB*)&(pal[0]) + hi4;
|
||||
unsigned red1 = entry->r;
|
||||
unsigned green1 = entry->g;
|
||||
unsigned blue1 = entry->b;
|
||||
if (blendType != NOBLEND) {
|
||||
if (lo4 && blendType != NOBLEND) {
|
||||
if (hi4 == 15) entry = &(pal[0]);
|
||||
else ++entry;
|
||||
unsigned f2 = ((index & 0x0F) << 4) + 1; // +1 so we scale by 256 as a max value, then result can just be shifted by 8
|
||||
unsigned f1 = (257 - f2); // f2 is 1 minimum, so this is 256 max
|
||||
red1 = (red1 * f1 + (unsigned)entry->r * f2) >> 8;
|
||||
unsigned f2 = (lo4 << 4);
|
||||
unsigned f1 = 256 - f2;
|
||||
red1 = (red1 * f1 + (unsigned)entry->r * f2) >> 8; // note: using color_blend() is 20% slower
|
||||
green1 = (green1 * f1 + (unsigned)entry->g * f2) >> 8;
|
||||
blue1 = (blue1 * f1 + (unsigned)entry->b * f2) >> 8;
|
||||
}
|
||||
if (brightness < 255) { // note: zero checking could be done to return black but that is hardly ever used so it is omitted
|
||||
// actually color_fade(c1, brightness)
|
||||
uint32_t scale = brightness + 1; // adjust for rounding (bitshift)
|
||||
red1 = (red1 * scale) >> 8;
|
||||
red1 = (red1 * scale) >> 8; // note: using color_fade() is 30% slower
|
||||
green1 = (green1 * scale) >> 8;
|
||||
blue1 = (blue1 * scale) >> 8;
|
||||
}
|
||||
|
@ -37,7 +37,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef WLED_MAX_USERMODS
|
||||
#ifdef ESP8266
|
||||
#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
#define WLED_MAX_USERMODS 4
|
||||
#else
|
||||
#define WLED_MAX_USERMODS 6
|
||||
@ -49,31 +49,31 @@
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 3
|
||||
#define WLED_MAX_ANALOG_CHANNELS 5
|
||||
#define WLED_MAX_BUSSES 4 // will allow 3 digital & 1 analog RGB
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 2
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 3 // no longer used for bus creation but used to distinguish S2/S3 in UI
|
||||
#else
|
||||
#define WLED_MAX_ANALOG_CHANNELS (LEDC_CHANNEL_MAX*LEDC_SPEED_MODE_MAX)
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, 6 LEDC, only has 1 I2S but NPB does not support it ATM
|
||||
#define WLED_MAX_BUSSES 6 // will allow 2 digital & 2 analog RGB or 6 PWM white
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 2
|
||||
//#define WLED_MAX_ANALOG_CHANNELS 6
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 3
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 4 // no longer used for bus creation but used to distinguish S2/S3 in UI
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2) // 4 RMT, 8 LEDC, only has 1 I2S bus, supported in NPB
|
||||
// the 5th bus (I2S) will prevent Audioreactive usermod from functioning (it is last used though)
|
||||
#define WLED_MAX_BUSSES 7 // will allow 5 digital & 2 analog RGB
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 5
|
||||
//#define WLED_MAX_ANALOG_CHANNELS 8
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 3
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, 8 LEDC, has 2 I2S but NPB does not support them ATM
|
||||
#define WLED_MAX_BUSSES 6 // will allow 4 digital & 2 analog RGB
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 4
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 4 // no longer used for bus creation but used to distinguish S2/S3 in UI
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, 8 LEDC, has 2 I2S but NPB supports parallel x8 LCD on I2S1
|
||||
#define WLED_MAX_BUSSES 14 // will allow 12 digital & 2 analog RGB
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 12 // x4 RMT + x8 I2S-LCD
|
||||
//#define WLED_MAX_ANALOG_CHANNELS 8
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 4
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 6 // no longer used for bus creation but used to distinguish S2/S3 in UI
|
||||
#else
|
||||
// the last digital bus (I2S0) will prevent Audioreactive usermod from functioning
|
||||
#define WLED_MAX_BUSSES 20 // will allow 17 digital & 3 analog RGB
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 17
|
||||
#define WLED_MAX_BUSSES 19 // will allow 16 digital & 3 analog RGB
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 16 // x1/x8 I2S1 + x8 RMT
|
||||
//#define WLED_MAX_ANALOG_CHANNELS 16
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 4
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 6 // no longer used for bus creation but used to distinguish S2/S3 in UI
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
@ -87,7 +87,7 @@
|
||||
#ifndef WLED_MAX_DIGITAL_CHANNELS
|
||||
#error You must also define WLED_MAX_DIGITAL_CHANNELS.
|
||||
#endif
|
||||
#define WLED_MIN_VIRTUAL_BUSSES (5-WLED_MAX_BUSSES)
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 3
|
||||
#else
|
||||
#if WLED_MAX_BUSSES > 20
|
||||
#error Maximum number of buses is 20.
|
||||
@ -98,7 +98,11 @@
|
||||
#ifndef WLED_MAX_DIGITAL_CHANNELS
|
||||
#error You must also define WLED_MAX_DIGITAL_CHANNELS.
|
||||
#endif
|
||||
#define WLED_MIN_VIRTUAL_BUSSES (20-WLED_MAX_BUSSES)
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 4
|
||||
#else
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 6
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -115,7 +119,7 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef ESP8266
|
||||
#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
#define WLED_MAX_COLOR_ORDER_MAPPINGS 5
|
||||
#else
|
||||
#define WLED_MAX_COLOR_ORDER_MAPPINGS 10
|
||||
@ -125,7 +129,7 @@
|
||||
#undef WLED_MAX_LEDMAPS
|
||||
#endif
|
||||
#ifndef WLED_MAX_LEDMAPS
|
||||
#ifdef ESP8266
|
||||
#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
#define WLED_MAX_LEDMAPS 10
|
||||
#else
|
||||
#define WLED_MAX_LEDMAPS 16
|
||||
@ -205,6 +209,7 @@
|
||||
#define USERMOD_ID_PIXELS_DICE_TRAY 54 //Usermod "pixels_dice_tray.h"
|
||||
#define USERMOD_ID_DEEP_SLEEP 55 //Usermod "usermod_deep_sleep.h"
|
||||
#define USERMOD_ID_RF433 56 //Usermod "usermod_v2_RF433.h"
|
||||
#define USERMOD_ID_BRIGHTNESS_FOLLOW_SUN 57 //Usermod "usermod_v2_brightness_follow_sun.h"
|
||||
|
||||
//Access point behavior
|
||||
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
|
||||
@ -476,6 +481,8 @@
|
||||
#ifndef MAX_LEDS
|
||||
#ifdef ESP8266
|
||||
#define MAX_LEDS 1664 //can't rely on memory limit to limit this to 1600 LEDs
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
#define MAX_LEDS 2048 //due to memory constraints
|
||||
#else
|
||||
#define MAX_LEDS 8192
|
||||
#endif
|
||||
@ -485,7 +492,9 @@
|
||||
#ifdef ESP8266
|
||||
#define MAX_LED_MEMORY 4000
|
||||
#else
|
||||
#if defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32C3)
|
||||
#if defined(ARDUINO_ARCH_ESP32S2)
|
||||
#define MAX_LED_MEMORY 16000
|
||||
#elif defined(ARDUINO_ARCH_ESP32C3)
|
||||
#define MAX_LED_MEMORY 32000
|
||||
#else
|
||||
#define MAX_LED_MEMORY 64000
|
||||
|
@ -2,7 +2,7 @@ var d=document;
|
||||
var loc = false, locip, locproto = "http:";
|
||||
|
||||
function H(pg="") { window.open("https://kno.wled.ge/"+pg); }
|
||||
function GH() { window.open("https://github.com/Aircoookie/WLED"); }
|
||||
function GH() { window.open("https://github.com/wled-dev/WLED"); }
|
||||
function gId(c) { return d.getElementById(c); } // getElementById
|
||||
function cE(e) { return d.createElement(e); } // createElement
|
||||
function gEBCN(c) { return d.getElementsByClassName(c); } // getElementsByClassName
|
||||
@ -16,7 +16,7 @@ function isI(n) { return n === +n && n === (n|0); } // isInteger
|
||||
function toggle(el) { gId(el).classList.toggle("hide"); gId('No'+el).classList.toggle("hide"); }
|
||||
function tooltip(cont=null) {
|
||||
d.querySelectorAll((cont?cont+" ":"")+"[title]").forEach((element)=>{
|
||||
element.addEventListener("mouseover", ()=>{
|
||||
element.addEventListener("pointerover", ()=>{
|
||||
// save title
|
||||
element.setAttribute("data-title", element.getAttribute("title"));
|
||||
const tooltip = d.createElement("span");
|
||||
@ -41,7 +41,7 @@ function tooltip(cont=null) {
|
||||
tooltip.classList.add("visible");
|
||||
});
|
||||
|
||||
element.addEventListener("mouseout", ()=>{
|
||||
element.addEventListener("pointerout", ()=>{
|
||||
d.querySelectorAll('.tooltip').forEach((tooltip)=>{
|
||||
tooltip.classList.remove("visible");
|
||||
d.body.removeChild(tooltip);
|
||||
|
@ -6,6 +6,7 @@
|
||||
<meta name="theme-color" content="#222222">
|
||||
<meta content="yes" name="apple-mobile-web-app-capable">
|
||||
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAGACGAAAAFgAAAIlQTkcNChoKAAAADUlIRFIAAAAQAAAAEAgGAAAAH/P/YQAAAE1JREFUOI1j/P//PwOxgNGeAUMxE9G6cQCKDWAhpADZ2f8PMjBS3QW08QK20KaZC2gfC9hCnqouoNgARgY7zMxAyNlUdQHlXiAlO2MDAD63EVqNHAe0AAAAAElFTkSuQmCC"/>
|
||||
<link rel="apple-touch-icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAMAAAAKE/YAAAAANlBMVEUAAAAAKacAPv8APv8APv8APv8APv8AKacAQv8AJZsAKacAKacAPv8APv8AL8MAPv8APv8AQv/mTD7uAAAAEHRSTlMAf1mvpgzzka9/Vy3wnX9ic6GVaQAAAQhJREFUeNrt3EtuwkAQRdGGYAyYT9j/ZqN4GNkv9gSV4ZwVXJU8cVerGwAAAAAAUNTl9LXY6dJq6J/fiz37VsNuTfSu1SB6hmjRgWjRgWjRgWjRgWjRgehf5y1G70r/2Ha3w5T7foWhvVg3Pbxzq6w7lv5MRa8iWnQgWnQgWnQgWnQgWnQgenQtHT3spzxqT7p/+/MX0aJFj0SLDkSLDkSLDkSLDkSL/s/n7OhXRR8fNXb0q6KvrbSZSXetMtEzRIsORIsORIsORIsORIsORIdr/i+PHvYr3A9Tbsui7egn+2pf83//ky7RokWPRIsORIsORIsORIsORG/2gbhNPsUHAAAAAAD89QMg+PZb+jO0tAAAAABJRU5ErkJggg==" sizes="180x180"/>
|
||||
<title>WLED</title>
|
||||
<link rel="stylesheet" href="index.css">
|
||||
</head>
|
||||
@ -362,7 +363,7 @@
|
||||
|
||||
<!--
|
||||
If you want to load iro.js and rangetouch.js as consecutive requests, you can do it like it was done in 0.14.0:
|
||||
https://github.com/Aircoookie/WLED/blob/v0.14.0/wled00/data/index.htm
|
||||
https://github.com/wled-dev/WLED/blob/v0.14.0/wled00/data/index.htm
|
||||
-->
|
||||
<script src="iro.js"></script>
|
||||
<script src="rangetouch.js"></script>
|
||||
|
@ -409,7 +409,9 @@ function pName(i)
|
||||
|
||||
function isPlaylist(i)
|
||||
{
|
||||
return pJson[i].playlist && pJson[i].playlist.ps;
|
||||
if (isNumeric(i)) return pJson[i].playlist && pJson[i].playlist.ps;
|
||||
if (isObj(i)) return i.playlist && i.playlist.ps;
|
||||
return false;
|
||||
}
|
||||
|
||||
function papiVal(i)
|
||||
@ -775,8 +777,8 @@ function populateSegments(s)
|
||||
}
|
||||
|
||||
let segp = `<div id="segp${i}" class="sbs">`+
|
||||
`<i class="icons slider-icon pwr ${inst.on ? "act":""}" id="seg${i}pwr" onclick="setSegPwr(${i})"></i>`+
|
||||
`<div class="sliderwrap il">`+
|
||||
`<i class="icons slider-icon pwr ${inst.on ? "act":""}" id="seg${i}pwr" title="Power" onclick="setSegPwr(${i})"></i>`+
|
||||
`<div class="sliderwrap il" title="Opacity/Brightness">`+
|
||||
`<input id="seg${i}bri" class="noslide" onchange="setSegBri(${i})" oninput="updateTrail(this)" max="255" min="1" type="range" value="${inst.bri}" />`+
|
||||
`<div class="sliderdisplay"></div>`+
|
||||
`</div>`+
|
||||
@ -807,12 +809,14 @@ function populateSegments(s)
|
||||
`<div class="sel-p"><select class="sel-p" id="seg${i}si" onchange="setSi(${i})">`+
|
||||
`<option value="0" ${inst.si==0?' selected':''}>BeatSin</option>`+
|
||||
`<option value="1" ${inst.si==1?' selected':''}>WeWillRockYou</option>`+
|
||||
`<option value="2" ${inst.si==2?' selected':''}>10/13</option>`+
|
||||
`<option value="3" ${inst.si==3?' selected':''}>14/3</option>`+
|
||||
`</select></div>`+
|
||||
`</div>`;
|
||||
cn += `<div class="seg lstI ${i==s.mainseg && !simplifiedUI ? 'selected' : ''} ${exp ? "expanded":""}" id="seg${i}" data-set="${inst.set}">`+
|
||||
`<label class="check schkl ${smpl}">`+
|
||||
`<input type="checkbox" id="seg${i}sel" onchange="selSeg(${i})" ${inst.sel ? "checked":""}>`+
|
||||
`<span class="checkmark"></span>`+
|
||||
`<span class="checkmark" title="Select"></span>`+
|
||||
`</label>`+
|
||||
`<div class="segname ${smpl}" onclick="selSegEx(${i})">`+
|
||||
`<i class="icons e-icon frz" id="seg${i}frz" title="(un)Freeze" onclick="event.preventDefault();tglFreeze(${i});">&#x${inst.frz ? (li.live && li.liveseg==i?'e410':'e0e8') : 'e325'};</i>`+
|
||||
@ -1416,7 +1420,7 @@ function makeWS() {
|
||||
ws = null;
|
||||
}
|
||||
ws.onopen = (e)=>{
|
||||
//ws.send("{'v':true}"); // unnecessary (https://github.com/Aircoookie/WLED/blob/master/wled00/ws.cpp#L18)
|
||||
//ws.send("{'v':true}"); // unnecessary (https://github.com/wled-dev/WLED/blob/main/wled00/ws.cpp#L18)
|
||||
wsRpt = 0;
|
||||
reqsLegal = true;
|
||||
}
|
||||
@ -1664,13 +1668,17 @@ function setEffectParameters(idx)
|
||||
paOnOff[0] = paOnOff[0].substring(0,dPos);
|
||||
}
|
||||
if (paOnOff.length>0 && paOnOff[0] != "!") text = paOnOff[0];
|
||||
gId("adPal").classList.remove("hide");
|
||||
if (lastinfo.cpalcount>0) gId("rmPal").classList.remove("hide");
|
||||
} else {
|
||||
// disable palette list
|
||||
text += ' not used';
|
||||
palw.style.display = "none";
|
||||
gId("adPal").classList.add("hide");
|
||||
gId("rmPal").classList.add("hide");
|
||||
// Close palette dialog if not available
|
||||
if (gId("palw").lastElementChild.tagName == "DIALOG") {
|
||||
gId("palw").lastElementChild.close();
|
||||
if (palw.lastElementChild.tagName == "DIALOG") {
|
||||
palw.lastElementChild.close();
|
||||
}
|
||||
}
|
||||
pall.innerHTML = icon + text;
|
||||
@ -1885,7 +1893,7 @@ function makeSeg()
|
||||
function resetUtil(off=false)
|
||||
{
|
||||
gId('segutil').innerHTML = `<div class="seg btn btn-s${off?' off':''}" style="padding:0;margin-bottom:12px;">`
|
||||
+ '<label class="check schkl"><input type="checkbox" id="selall" onchange="selSegAll(this)"><span class="checkmark"></span></label>'
|
||||
+ '<label class="check schkl"><input type="checkbox" id="selall" onchange="selSegAll(this)"><span class="checkmark" title="Select all"></span></label>'
|
||||
+ `<div class="segname" ${off?'':'onclick="makeSeg()"'}><i class="icons btn-icon"></i>Add segment</div>`
|
||||
+ '<div class="pop hide" onclick="event.stopPropagation();">'
|
||||
+ `<i class="icons g-icon" title="Select group" onclick="this.nextElementSibling.classList.toggle('hide');"></i>`
|
||||
@ -1899,15 +1907,16 @@ function resetUtil(off=false)
|
||||
if (lSeg>2) d.querySelectorAll("#Segments .pop").forEach((e)=>{e.classList.remove("hide");});
|
||||
}
|
||||
|
||||
function makePlSel(el, incPl=false)
|
||||
function makePlSel(p, el)
|
||||
{
|
||||
var plSelContent = "";
|
||||
delete pJson["0"]; // remove filler preset
|
||||
Object.entries(pJson).sort(cmpP).forEach((a)=>{
|
||||
var n = a[1].n ? a[1].n : "Preset " + a[0];
|
||||
if (isPlaylist(a[1])) n += ' ▶'; // mark playlist
|
||||
if (cfg.comp.idsort) n = a[0] + ' ' + n;
|
||||
if (!(!incPl && a[1].playlist && a[1].playlist.ps)) // skip playlists, sub-playlists not yet supported
|
||||
plSelContent += `<option value="${a[0]}" ${a[0]==el?"selected":""}>${n}</option>`;
|
||||
// skip endless playlists and itself
|
||||
if (!isPlaylist(a[1]) || (a[1].playlist.repeat > 0 && a[0]!=p)) plSelContent += `<option value="${a[0]}" ${a[0]==el?"selected":""}>${n}</option>`;
|
||||
});
|
||||
return plSelContent;
|
||||
}
|
||||
@ -1932,21 +1941,23 @@ function refreshPlE(p)
|
||||
});
|
||||
}
|
||||
|
||||
// p: preset ID, i: ps index
|
||||
// p: preset ID, i: playlist item index
|
||||
function addPl(p,i)
|
||||
{
|
||||
plJson[p].ps.splice(i+1,0,0);
|
||||
plJson[p].dur.splice(i+1,0,plJson[p].dur[i]);
|
||||
plJson[p].transition.splice(i+1,0,plJson[p].transition[i]);
|
||||
const pl = plJson[p];
|
||||
pl.ps.splice(i+1,0,1);
|
||||
pl.dur.splice(i+1,0,pl.dur[i]);
|
||||
pl.transition.splice(i+1,0,pl.transition[i]);
|
||||
refreshPlE(p);
|
||||
}
|
||||
|
||||
function delPl(p,i)
|
||||
{
|
||||
if (plJson[p].ps.length < 2) return;
|
||||
plJson[p].ps.splice(i,1);
|
||||
plJson[p].dur.splice(i,1);
|
||||
plJson[p].transition.splice(i,1);
|
||||
const pl = plJson[p];
|
||||
if (pl.ps.length < 2) return;
|
||||
pl.ps.splice(i,1);
|
||||
pl.dur.splice(i,1);
|
||||
pl.transition.splice(i,1);
|
||||
refreshPlE(p);
|
||||
}
|
||||
|
||||
@ -1963,6 +1974,13 @@ function pleDur(p,i,field)
|
||||
|
||||
function pleTr(p,i,field)
|
||||
{
|
||||
const du = gId(`pl${p}du${i}`);
|
||||
const dv = parseFloat(du.value);
|
||||
if (dv > 0) {
|
||||
field.max = dv;
|
||||
if (parseFloat(field.value) > dv)
|
||||
field.value = du.value;
|
||||
}
|
||||
if (field.validity.valid)
|
||||
plJson[p].transition[i] = Math.floor(field.value*10);
|
||||
}
|
||||
@ -1982,6 +2000,17 @@ function plR(p)
|
||||
}
|
||||
}
|
||||
|
||||
function plM(p)
|
||||
{
|
||||
const man = gId(`pl${p}manual`).checked;
|
||||
plJson[p].dur.forEach((e,i)=>{
|
||||
const d = gId(`pl${p}du${i}`);
|
||||
plJson[p].dur[i] = e = man ? 0 : 100;
|
||||
d.value = e/10; // 10s default
|
||||
d.readOnly = man;
|
||||
});
|
||||
}
|
||||
|
||||
function makeP(i,pl)
|
||||
{
|
||||
var content = "";
|
||||
@ -1995,12 +2024,17 @@ function makeP(i,pl)
|
||||
r: false,
|
||||
end: 0
|
||||
};
|
||||
var rep = plJson[i].repeat ? plJson[i].repeat : 0;
|
||||
const rep = plJson[i].repeat ? plJson[i].repeat : 0;
|
||||
const man = plJson[i].dur == 0;
|
||||
content =
|
||||
`<div id="ple${i}" style="margin-top:10px;"></div><label class="check revchkl">Shuffle
|
||||
<input type="checkbox" id="pl${i}rtgl" onchange="plR(${i})" ${plJson[i].r||rep<0?"checked":""}>
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label class="check revchkl">Manual advance
|
||||
<input type="checkbox" id="pl${i}manual" onchange="plM(${i})" ${man?"checked":""}>
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label class="check revchkl">Repeat indefinitely
|
||||
<input type="checkbox" id="pl${i}rptgl" onchange="plR(${i})" ${rep>0?"":"checked"}>
|
||||
<span class="checkmark"></span>
|
||||
@ -2011,7 +2045,7 @@ function makeP(i,pl)
|
||||
<div class="sel-p"><select class="sel-ple" id="pl${i}selEnd" onchange="plR(${i})" data-val=${plJson[i].end?plJson[i].end:0}>
|
||||
<option value="0">None</option>
|
||||
<option value="255" ${plJson[i].end && plJson[i].end==255?"selected":""}>Restore preset</option>
|
||||
${makePlSel(plJson[i].end?plJson[i].end:0, true)}
|
||||
${makePlSel(i, plJson[i].end?plJson[i].end:0)}
|
||||
</select></div></div>
|
||||
</div>
|
||||
<div class="c"><button class="btn btn-p" onclick="testPl(${i}, this)"><i class='icons btn-icon'></i>Test</button></div>`;
|
||||
@ -2084,25 +2118,26 @@ function makePUtil()
|
||||
|
||||
function makePlEntry(p,i)
|
||||
{
|
||||
const man = gId(`pl${p}manual`).checked;
|
||||
return `<div class="plentry">
|
||||
<div class="hrz"></div>
|
||||
<table>
|
||||
<tr>
|
||||
<td width="80%" colspan=2>
|
||||
<div class="sel-p"><select class="sel-pl" onchange="plePs(${p},${i},this)" data-val="${plJson[p].ps[i]}" data-index="${i}">
|
||||
${makePlSel(plJson[p].ps[i])}
|
||||
${makePlSel(p, plJson[p].ps[i])}
|
||||
</select></div>
|
||||
</td>
|
||||
<td class="c"><button class="btn btn-pl-add" onclick="addPl(${p},${i})"><i class="icons btn-icon"></i></button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="c">Duration</td>
|
||||
<td class="c">Duration <i style="font-size:70%;">(0=inf.)</i></td>
|
||||
<td class="c">Transition</td>
|
||||
<td class="c">#${i+1}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="c" width="40%"><input class="segn" type="number" placeholder="Duration" max=6553.0 min=0.2 step=0.1 oninput="pleDur(${p},${i},this)" value="${plJson[p].dur[i]/10.0}">s</td>
|
||||
<td class="c" width="40%"><input class="segn" type="number" placeholder="Transition" max=65.0 min=0.0 step=0.1 oninput="pleTr(${p},${i},this)" value="${plJson[p].transition[i]/10.0}">s</td>
|
||||
<td class="c" width="40%"><input class="segn" type="number" placeholder="Duration" max=6553.0 min=0.0 step=0.1 oninput="pleDur(${p},${i},this)" value="${plJson[p].dur[i]/10.0}" id="pl${p}du${i}" ${man?"readonly":""}>s</td>
|
||||
<td class="c" width="40%"><input class="segn" type="number" placeholder="Transition" max=65.0 min=0.0 step=0.1 oninput="pleTr(${p},${i},this)" onfocus="pleTr(${p},${i},this)" value="${plJson[p].transition[i]/10.0}">s</td>
|
||||
<td class="c"><button class="btn btn-pl-del" onclick="delPl(${p},${i})"><i class="icons btn-icon"></i></button></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
@ -2655,28 +2690,28 @@ function fromRgb()
|
||||
var g = gId('sliderG').value;
|
||||
var b = gId('sliderB').value;
|
||||
setPicker(`rgb(${r},${g},${b})`);
|
||||
let cd = gId('csl').children; // color slots
|
||||
cd[csel].dataset.r = r;
|
||||
cd[csel].dataset.g = g;
|
||||
cd[csel].dataset.b = b;
|
||||
setCSL(cd[csel]);
|
||||
let cd = gId('csl').children[csel]; // color slots
|
||||
cd.dataset.r = r;
|
||||
cd.dataset.g = g;
|
||||
cd.dataset.b = b;
|
||||
setCSL(cd);
|
||||
}
|
||||
|
||||
function fromW()
|
||||
{
|
||||
let w = gId('sliderW');
|
||||
let cd = gId('csl').children; // color slots
|
||||
cd[csel].dataset.w = w.value;
|
||||
setCSL(cd[csel]);
|
||||
let cd = gId('csl').children[csel]; // color slots
|
||||
cd.dataset.w = w.value;
|
||||
setCSL(cd);
|
||||
updateTrail(w);
|
||||
}
|
||||
|
||||
// sr 0: from RGB sliders, 1: from picker, 2: from hex
|
||||
function setColor(sr)
|
||||
{
|
||||
var cd = gId('csl').children; // color slots
|
||||
let cdd = cd[csel].dataset;
|
||||
let w = 0, r,g,b;
|
||||
var cd = gId('csl').children[csel]; // color slots
|
||||
let cdd = cd.dataset;
|
||||
let w = parseInt(cdd.w), r = parseInt(cdd.r), g = parseInt(cdd.g), b = parseInt(cdd.b);
|
||||
if (sr == 1 && isRgbBlack(cdd)) cpick.color.setChannel('hsv', 'v', 100);
|
||||
if (sr != 2 && hasWhite) w = parseInt(gId('sliderW').value);
|
||||
var col = cpick.color.rgb;
|
||||
@ -2684,7 +2719,7 @@ function setColor(sr)
|
||||
cdd.g = g = hasRGB ? col.g : w;
|
||||
cdd.b = b = hasRGB ? col.b : w;
|
||||
cdd.w = w;
|
||||
setCSL(cd[csel]);
|
||||
setCSL(cd);
|
||||
var obj = {"seg": {"col": [[],[],[]]}};
|
||||
obj.seg.col[csel] = [r, g, b, w];
|
||||
requestJson(obj);
|
||||
@ -2727,7 +2762,7 @@ setInterval(()=>{
|
||||
gId('heart').style.color = `hsl(${hc}, 100%, 50%)`;
|
||||
}, 910);
|
||||
|
||||
function openGH() { window.open("https://github.com/Aircoookie/WLED/wiki"); }
|
||||
function openGH() { window.open("https://github.com/wled-dev/WLED/wiki"); }
|
||||
|
||||
var cnfr = false;
|
||||
function cnfReset()
|
||||
@ -3120,10 +3155,9 @@ function mergeDeep(target, ...sources)
|
||||
return mergeDeep(target, ...sources);
|
||||
}
|
||||
|
||||
function tooltip(cont=null)
|
||||
{
|
||||
function tooltip(cont=null) {
|
||||
d.querySelectorAll((cont?cont+" ":"")+"[title]").forEach((element)=>{
|
||||
element.addEventListener("mouseover", ()=>{
|
||||
element.addEventListener("pointerover", ()=>{
|
||||
// save title
|
||||
element.setAttribute("data-title", element.getAttribute("title"));
|
||||
const tooltip = d.createElement("span");
|
||||
@ -3148,7 +3182,7 @@ function tooltip(cont=null)
|
||||
tooltip.classList.add("visible");
|
||||
});
|
||||
|
||||
element.addEventListener("mouseout", ()=>{
|
||||
element.addEventListener("pointerout", ()=>{
|
||||
d.querySelectorAll('.tooltip').forEach((tooltip)=>{
|
||||
tooltip.classList.remove("visible");
|
||||
d.body.removeChild(tooltip);
|
||||
|
@ -6,7 +6,7 @@
|
||||
<title>DMX Settings</title>
|
||||
<script src="common.js" async type="text/javascript"></script>
|
||||
<script>
|
||||
function HW(){window.open("https://github.com/Aircoookie/WLED/wiki/DMX");}
|
||||
function HW(){window.open("https://kno.wled.ge/interfaces/dmx-output/");}
|
||||
function GCH(num) {
|
||||
gId('dmxchannels').innerHTML += "";
|
||||
for (i=0;i<num;i++) {
|
||||
|
@ -24,6 +24,7 @@
|
||||
function is16b(t) { return !!(gT(t).c & 0x10); } // is digital 16 bit type
|
||||
function mustR(t) { return !!(gT(t).c & 0x20); } // Off refresh is mandatory
|
||||
function numPins(t){ return Math.max(gT(t).t.length, 1); } // type length determines number of GPIO pins
|
||||
function chrID(x) { return String.fromCharCode((x<10?48:55)+x); }
|
||||
function S() {
|
||||
getLoc();
|
||||
loadJS(getURL('/settings/s.js?p=2'), false, ()=>{
|
||||
@ -42,10 +43,10 @@
|
||||
if (loc) d.Sf.action = getURL('/settings/leds');
|
||||
}
|
||||
function bLimits(b,v,p,m,l,o=5,d=2,a=6) {
|
||||
oMaxB = maxB = b; // maxB - max buses (can be changed if using ESP32 parallel I2S)
|
||||
maxD = d; // maxD - max digital channels (can be changed if using ESP32 parallel I2S)
|
||||
maxA = a; // maxA - max analog channels
|
||||
maxV = v; // maxV - min virtual buses
|
||||
oMaxB = maxB = b; // maxB - max buses (can be changed if using ESP32 parallel I2S): 20 - ESP32, 14 - S3/S2, 6 - C3, 4 - 8266
|
||||
maxD = d; // maxD - max digital channels (can be changed if using ESP32 parallel I2S): 17 - ESP32, 12 - S3/S2, 2 - C3, 3 - 8266
|
||||
maxA = a; // maxA - max analog channels: 16 - ESP32, 8 - S3/S2, 6 - C3, 5 - 8266
|
||||
maxV = v; // maxV - min virtual buses: 6 - ESP32/S3, 4 - S2/C3, 3 - ESP8266 (only used to distinguish S2/S3)
|
||||
maxPB = p; // maxPB - max LEDs per bus
|
||||
maxM = m; // maxM - max LED memory
|
||||
maxL = l; // maxL - max LEDs (will serve to determine ESP >1664 == ESP32)
|
||||
@ -138,7 +139,7 @@
|
||||
gId("ppldis").style.display = ppl ? 'inline' : 'none';
|
||||
// set PPL minimum value and clear actual PPL limit if ABL is disabled
|
||||
d.Sf.querySelectorAll("#mLC input[name^=MA]").forEach((i,x)=>{
|
||||
var n = String.fromCharCode((x<10?48:55)+x);
|
||||
var n = chrID(x);
|
||||
gId("PSU"+n).style.display = ppl ? "inline" : "none";
|
||||
const t = parseInt(d.Sf["LT"+n].value); // LED type SELECT
|
||||
const c = parseInt(d.Sf["LC"+n].value); //get LED count
|
||||
@ -169,7 +170,7 @@
|
||||
// select appropriate LED current
|
||||
d.Sf.querySelectorAll("#mLC select[name^=LAsel]").forEach((sel,x)=>{
|
||||
sel.value = 0; // set custom
|
||||
var n = String.fromCharCode((x<10?48:55)+x);
|
||||
var n = chrID(x);
|
||||
if (en)
|
||||
switch (parseInt(d.Sf["LA"+n].value)) {
|
||||
case 0: break; // disable ABL
|
||||
@ -250,6 +251,7 @@
|
||||
}
|
||||
|
||||
// enable/disable LED fields
|
||||
let dC = 0; // count of digital buses (for parallel I2S)
|
||||
let LTs = d.Sf.querySelectorAll("#mLC select[name^=LT]");
|
||||
LTs.forEach((s,i)=>{
|
||||
if (i < LTs.length-1) s.disabled = true; // prevent changing type (as we can't update options)
|
||||
@ -257,6 +259,7 @@
|
||||
var n = s.name.substring(2);
|
||||
var t = parseInt(s.value);
|
||||
memu += getMem(t, n); // calc memory
|
||||
dC += (isDig(t) && !isD2P(t));
|
||||
setPinConfig(n,t);
|
||||
gId("abl"+n).style.display = (!abl || !isDig(t)) ? "none" : "inline"; // show/hide individual ABL settings
|
||||
if (change) { // did we change LED type?
|
||||
@ -295,8 +298,7 @@
|
||||
// do we have a led count field
|
||||
if (nm=="LC") {
|
||||
let c = parseInt(LC.value,10); //get LED count
|
||||
if (c > 300 && i < 8) maxB = oMaxB - Math.max(maxD-7,0); //TODO: hard limit for buses when using ESP32 parallel I2S
|
||||
if (!customStarts || !startsDirty[n]) gId("ls"+n).value=sLC; //update start value
|
||||
if (!customStarts || !startsDirty[n]) gId("ls"+n).value = sLC; //update start value
|
||||
gId("ls"+n).disabled = !customStarts; //enable/disable field editing
|
||||
if (c) {
|
||||
let s = parseInt(gId("ls"+n).value); //start value
|
||||
@ -350,6 +352,18 @@
|
||||
else LC.style.color = d.ro_gpio.some((e)=>e==parseInt(LC.value)) ? "orange" : "#fff";
|
||||
}
|
||||
});
|
||||
const S2 = (oMaxB == 14) && (maxV == 4);
|
||||
const S3 = (oMaxB == 14) && (maxV == 6);
|
||||
if (oMaxB == 19 || S2 || S3) { // TODO: crude ESP32 & S2/S3 detection
|
||||
if (maxLC > 300 || dC <= 2) {
|
||||
d.Sf["PR"].checked = false;
|
||||
gId("prl").classList.add("hide");
|
||||
} else
|
||||
gId("prl").classList.remove("hide");
|
||||
// S2 supports mono I2S as well as parallel so we need to take that into account; S3 only supports parallel
|
||||
maxD = (S2 || S3 ? 4 : 8) + (d.Sf["PR"].checked ? 8 : S2); // TODO: use bLimits() : 4/8RMT + (x1/x8 parallel) I2S1
|
||||
maxB = oMaxB - (d.Sf["PR"].checked ? 0 : 7 + S3); // S2 (maxV==4) does support mono I2S
|
||||
}
|
||||
// distribute ABL current if not using PPL
|
||||
enPPL(sDI);
|
||||
|
||||
@ -387,7 +401,7 @@
|
||||
}
|
||||
function lastEnd(i) {
|
||||
if (i-- < 1) return 0;
|
||||
var s = String.fromCharCode((i<10?48:55)+i);
|
||||
var s = chrID(i);
|
||||
v = parseInt(d.getElementsByName("LS"+s)[0].value) + parseInt(d.getElementsByName("LC"+s)[0].value);
|
||||
var t = parseInt(d.getElementsByName("LT"+s)[0].value);
|
||||
if (isPWM(t)) v = 1; //PWM busses
|
||||
@ -409,8 +423,8 @@
|
||||
if (isVir(t)) virtB++;
|
||||
});
|
||||
|
||||
if ((n==1 && i>=maxB+maxV) || (n==-1 && i==0)) return;
|
||||
var s = String.fromCharCode((i<10?48:55)+i);
|
||||
if ((n==1 && i>=36) || (n==-1 && i==0)) return; // used to be i>=maxB+maxV when virtual buses were limited (now :"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
var s = chrID(i);
|
||||
|
||||
if (n==1) {
|
||||
// npm run build has trouble minimizing spaces inside string
|
||||
@ -470,21 +484,20 @@ mA/LED: <select name="LAsel${s}" onchange="enLA(this,'${s}');UI();">
|
||||
}
|
||||
}
|
||||
});
|
||||
enLA(d.Sf["LAsel"+s],s); // update LED mA
|
||||
// disable inappropriate LED types
|
||||
let sel = d.getElementsByName("LT"+s)[0]
|
||||
if (i >= maxB || digitalB >= maxD) disable(sel,'option[data-type="D"]'); // NOTE: see isDig()
|
||||
if (i >= maxB || twopinB >= 1) disable(sel,'option[data-type="2P"]'); // NOTE: see isD2P()
|
||||
disable(sel,`option[data-type^="${'A'.repeat(maxA-analogB+1)}"]`); // NOTE: see isPWM()
|
||||
sel.selectedIndex = sel.querySelector('option:not(:disabled)').index;
|
||||
// initialize current limiter
|
||||
enLA(d.Sf["LAsel"+s],s);
|
||||
}
|
||||
if (n==-1) {
|
||||
o[--i].remove();--i;
|
||||
o[i].querySelector("[name^=LT]").disabled = false;
|
||||
}
|
||||
|
||||
gId("+").style.display = (i<maxB+maxV-1) ? "inline":"none";
|
||||
gId("+").style.display = (i<35) ? "inline":"none"; // was maxB+maxV-1 when virtual buses were limited (now :"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
gId("-").style.display = (i>0) ? "inline":"none";
|
||||
|
||||
if (!init) {
|
||||
@ -495,7 +508,7 @@ mA/LED: <select name="LAsel${s}" onchange="enLA(this,'${s}');UI();">
|
||||
function addCOM(start=0,len=1,co=0) {
|
||||
var i = gEBCN("com_entry").length;
|
||||
if (i >= maxCO) return;
|
||||
var s = String.fromCharCode((i<10?48:55)+i);
|
||||
var s = chrID(i);
|
||||
var b = `<div class="com_entry">
|
||||
<hr class="sml">
|
||||
${i+1}: Start: <input type="number" name="XS${s}" id="xs${s}" class="l starts" min="0" max="65535" value="${start}" oninput="UI();" required="">
|
||||
@ -549,7 +562,7 @@ Swap: <select id="xw${s}" name="XW${s}">
|
||||
|
||||
function addBtn(i,p,t) {
|
||||
var c = gId("btns").innerHTML;
|
||||
var s = String.fromCharCode((i<10?48:55)+i);
|
||||
var s = chrID(i);
|
||||
c += `Button ${i} GPIO: <input type="number" name="BT${s}" onchange="UI()" class="xs" value="${p}">`;
|
||||
c += ` <select name="BE${s}">`
|
||||
c += `<option value="0" ${t==0?"selected":""}>Disabled</option>`;
|
||||
@ -573,8 +586,10 @@ Swap: <select id="xw${s}" name="XW${s}">
|
||||
function checkSi() { //on load, checks whether there are custom start fields
|
||||
var cs = false;
|
||||
for (var i=1; i < gEBCN("iST").length; i++) {
|
||||
var v = parseInt(gId("ls"+(i-1)).value) + parseInt(gN("LC"+(i-1)).value);
|
||||
if (v != parseInt(gId("ls"+i).value)) {cs = true; startsDirty[i] = true;}
|
||||
var s = chrID(i);
|
||||
var p = chrID(i-1); // cover edge case 'A' previous char being '9'
|
||||
var v = parseInt(gId("ls"+p).value) + parseInt(gN("LC"+p).value);
|
||||
if (v != parseInt(gId("ls"+s).value)) {cs = true; startsDirty[i] = true;}
|
||||
}
|
||||
if (gId("ls0") && parseInt(gId("ls0").value) != 0) {cs = true; startsDirty[0] = true;}
|
||||
gId("si").checked = cs;
|
||||
@ -603,22 +618,32 @@ Swap: <select id="xw${s}" name="XW${s}">
|
||||
|
||||
function receivedText(e) {
|
||||
let lines = e.target.result;
|
||||
var c = JSON.parse(lines);
|
||||
let c = JSON.parse(lines);
|
||||
if (c.hw) {
|
||||
if (c.hw.led) {
|
||||
for (var i=0; i<10; i++) addLEDs(-1);
|
||||
var l = c.hw.led;
|
||||
// remove all existing outputs
|
||||
for (const i=0; i<36; i++) addLEDs(-1); // was i<maxb+maxV when number of virtual buses was limited (now :"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
let l = c.hw.led;
|
||||
l.ins.forEach((v,i,a)=>{
|
||||
addLEDs(1);
|
||||
for (var j=0; j<v.pin.length; j++) d.getElementsByName(`L${j}${i}`)[0].value = v.pin[j];
|
||||
d.getElementsByName("LT"+i)[0].value = v.type;
|
||||
d.getElementsByName("LS"+i)[0].value = v.start;
|
||||
d.getElementsByName("LC"+i)[0].value = v.len;
|
||||
d.getElementsByName("CO"+i)[0].value = v.order;
|
||||
d.getElementsByName("CO"+i)[0].value = v.order & 0x0F;
|
||||
d.getElementsByName("SL"+i)[0].value = v.skip;
|
||||
d.getElementsByName("RF"+i)[0].checked = v.ref;
|
||||
d.getElementsByName("CV"+i)[0].checked = v.rev;
|
||||
d.getElementsByName("AW"+i)[0].value = v.rgbwm;
|
||||
d.getElementsByName("WO"+i)[0].value = (v.order>>4) & 0x0F;
|
||||
d.getElementsByName("SP"+i)[0].value = v.freq;
|
||||
d.getElementsByName("LA"+i)[0].value = v.ledma;
|
||||
d.getElementsByName("MA"+i)[0].value = v.maxpwr;
|
||||
});
|
||||
d.getElementsByName("PR")[0].checked = l.prl | 0;
|
||||
d.getElementsByName("LD")[0].checked = l.ld;
|
||||
d.getElementsByName("MA")[0].value = l.maxpwr;
|
||||
d.getElementsByName("ABL")[0].checked = l.maxpwr > 0;
|
||||
}
|
||||
if(c.hw.com) {
|
||||
resetCOM();
|
||||
@ -626,22 +651,28 @@ Swap: <select id="xw${s}" name="XW${s}">
|
||||
addCOM(e.start, e.len, e.order);
|
||||
});
|
||||
}
|
||||
if (c.hw.btn) {
|
||||
var b = c.hw.btn;
|
||||
let b = c.hw.btn;
|
||||
if (b) {
|
||||
if (Array.isArray(b.ins)) gId("btns").innerHTML = "";
|
||||
b.ins.forEach((v,i,a)=>{
|
||||
addBtn(i,v.pin[0],v.type);
|
||||
});
|
||||
d.getElementsByName("TT")[0].value = b.tt;
|
||||
}
|
||||
if (c.hw.ir) {
|
||||
d.getElementsByName("IR")[0].value = c.hw.ir.pin;
|
||||
d.getElementsByName("IT")[0].value = c.hw.ir.type;
|
||||
let ir = c.hw.ir;
|
||||
if (ir) {
|
||||
d.getElementsByName("IR")[0].value = ir.pin;
|
||||
d.getElementsByName("IT")[0].value = ir.type;
|
||||
}
|
||||
if (c.hw.relay) {
|
||||
d.getElementsByName("RL")[0].value = c.hw.relay.pin;
|
||||
d.getElementsByName("RM")[0].checked = c.hw.relay.rev;
|
||||
d.getElementsByName("RO")[0].checked = c.hw.relay.odrain;
|
||||
let rl = c.hw.relay;
|
||||
if (rl) {
|
||||
d.getElementsByName("RL")[0].value = rl.pin;
|
||||
d.getElementsByName("RM")[0].checked = rl.rev;
|
||||
d.getElementsByName("RO")[0].checked = rl.odrain;
|
||||
}
|
||||
let li = c.light;
|
||||
if (li) {
|
||||
d.getElementsByName("MS")[0].checked = li.aseg;
|
||||
}
|
||||
UI();
|
||||
}
|
||||
@ -789,6 +820,7 @@ Swap: <select id="xw${s}" name="XW${s}">
|
||||
Use less than <span id="wreason">800 LEDs per output</span> for the best experience!<br>
|
||||
</div>
|
||||
<hr class="sml">
|
||||
<div id="prl" class="hide">Use parallel I2S: <input type="checkbox" name="PR"><br></div>
|
||||
Make a segment for each output: <input type="checkbox" name="MS"><br>
|
||||
Custom bus start indices: <input type="checkbox" onchange="tglSi(this.checked)" id="si"><br>
|
||||
Use global LED buffer: <input type="checkbox" name="LD" onchange="UI()"><br>
|
||||
|
@ -68,11 +68,11 @@
|
||||
<div>Restore configuration<br><input type="file" name="data2" accept=".json"> <button type="button" onclick="uploadFile(d.Sf.data2,'/cfg.json');">Upload</button><br></div>
|
||||
<hr>
|
||||
<h3>About</h3>
|
||||
<a href="https://github.com/Aircoookie/WLED/" target="_blank">WLED</a> version ##VERSION##<!-- Autoreplaced from package.json --><br><br>
|
||||
<a href="https://github.com/Aircoookie/WLED/wiki/Contributors-and-credits" target="_blank">Contributors, dependencies and special thanks</a><br>
|
||||
<a href="https://github.com/wled-dev/WLED/" target="_blank">WLED</a> version ##VERSION##<!-- Autoreplaced from package.json --><br><br>
|
||||
<a href="https://kno.wled.ge/about/contributors/" target="_blank">Contributors, dependencies and special thanks</a><br>
|
||||
A huge thank you to everyone who helped me create WLED!<br><br>
|
||||
(c) 2016-2024 Christian Schwinne <br>
|
||||
<i>Licensed under the <a href="https://github.com/Aircoookie/WLED/blob/master/LICENSE" target="_blank">EUPL v1.2 license</a></i><br><br>
|
||||
<i>Licensed under the <a href="https://github.com/wled-dev/WLED/blob/main/LICENSE" target="_blank">EUPL v1.2 license</a></i><br><br>
|
||||
Server message: <span class="sip"> Response error! </span><hr>
|
||||
<div id="toast"></div>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
|
||||
|
@ -168,8 +168,8 @@
|
||||
<h3>Clock</h3>
|
||||
Analog Clock overlay: <input type="checkbox" name="OL" onchange="Cs()"><br>
|
||||
<div id="cac">
|
||||
First LED: <input name="O1" type="number" min="0" max="255" required> Last LED: <input name="O2" type="number" min="0" max="255" required><br>
|
||||
12h LED: <input name="OM" type="number" min="0" max="255" required><br>
|
||||
First LED: <input name="O1" type="number" min="0" max="1024" required> Last LED: <input name="O2" type="number" min="0" max="1024" required><br>
|
||||
12h LED: <input name="OM" type="number" min="0" max="1024" required><br>
|
||||
Show 5min marks: <input type="checkbox" name="O5"><br>
|
||||
Seconds (as trail): <input type="checkbox" name="OS"><br>
|
||||
Show clock overlay only if all LEDs are solid black: <input type="checkbox" name="OB"><br>
|
||||
|
@ -13,7 +13,7 @@
|
||||
function S() {
|
||||
getLoc();
|
||||
// load settings and insert values into DOM
|
||||
fetch(getURL('/cfg.json'), {
|
||||
fetch(getURL('/json/cfg'), {
|
||||
method: 'get'
|
||||
})
|
||||
.then(res => {
|
||||
|
@ -47,7 +47,7 @@
|
||||
scanLoops = 0;
|
||||
|
||||
if (networks.length > 0) {
|
||||
let cs = d.querySelectorAll("#wifi_entries input[type=text]");
|
||||
let cs = d.querySelectorAll("#wifi_entries input[type=text][name^=CS]");
|
||||
for (let input of (cs||[])) {
|
||||
let found = false;
|
||||
let select = cE("select");
|
||||
@ -64,7 +64,7 @@
|
||||
const option = cE("option");
|
||||
|
||||
option.setAttribute("value", networks[i].ssid);
|
||||
option.textContent = `${networks[i].ssid} (${networks[i].rssi} dBm)`;
|
||||
option.textContent = `${networks[i].ssid} (${networks[i].rssi} dBm)`; // [${networks[i].bssid.replaceAll(':','')}]
|
||||
|
||||
if (networks[i].ssid === input.value) {
|
||||
option.setAttribute("selected", "selected");
|
||||
@ -109,12 +109,13 @@
|
||||
gId("wifi_add").style.display = (i<maxNetworks) ? "inline":"none";
|
||||
gId("wifi_rem").style.display = (i>1) ? "inline":"none";
|
||||
}
|
||||
function addWiFi(ssid="",pass="",ip=0,gw=0,sn=0x00ffffff) { // little endian
|
||||
function addWiFi(ssid="",pass="",bssid="",ip=0,gw=0,sn=0x00ffffff) { // little endian
|
||||
var i = gId("wifi_entries").childNodes.length;
|
||||
if (i >= maxNetworks) return;
|
||||
var b = `<div id="net${i}"><hr class="sml">
|
||||
Network name (SSID${i==0?", empty to not connect":""}):<br><input type="text" id="CS${i}" name="CS${i}" maxlength="32" value="${ssid}" ${i>0?"required":""}><br>
|
||||
Network password:<br><input type="password" name="PW${i}" maxlength="64" value="${pass}"><br>
|
||||
BSSID (optional):<br><input type="text" id="BS${i}" name="BS${i}" maxlength="12" value="${bssid}"><br>
|
||||
Static IP (leave at 0.0.0.0 for DHCP)${i==0?"<br>Also used by Ethernet":""}:<br>
|
||||
<input name="IP${i}0" type="number" class="s" min="0" max="255" value="${ip&0xFF}" required>.<input name="IP${i}1" type="number" class="s" min="0" max="255" value="${(ip>>8)&0xFF}" required>.<input name="IP${i}2" type="number" class="s" min="0" max="255" value="${(ip>>16)&0xFF}" required>.<input name="IP${i}3" type="number" class="s" min="0" max="255" value="${(ip>>24)&0xFF}" required><br>
|
||||
Static gateway:<br>
|
||||
|
@ -17,9 +17,9 @@
|
||||
<h2>WLED Software Update</h2>
|
||||
<form method='POST' action='./update' id='uf' enctype='multipart/form-data' onsubmit="U()">
|
||||
Installed version: <span class="sip">##VERSION##</span><br>
|
||||
Download the latest binary: <a href="https://github.com/Aircoookie/WLED/releases" target="_blank"
|
||||
Download the latest binary: <a href="https://github.com/wled-dev/WLED/releases" target="_blank"
|
||||
style="vertical-align: text-bottom; display: inline-flex;">
|
||||
<img src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square"></a><br>
|
||||
<img src="https://img.shields.io/github/release/wled-dev/WLED.svg?style=flat-square"></a><br>
|
||||
<input type='file' name='update' required><br> <!--should have accept='.bin', but it prevents file upload from android app-->
|
||||
<button type="submit">Update!</button><br>
|
||||
<button type="button" onclick="B()">Back</button>
|
||||
|
@ -22,7 +22,7 @@ void rdmPersonalityChangedCb(dmx_port_t dmxPort, const rdm_header_t *header,
|
||||
if (header->cc == RDM_CC_SET_COMMAND_RESPONSE) {
|
||||
const uint8_t personality = dmx_get_current_personality(dmx->inputPortNum);
|
||||
DMXMode = std::min(DMX_MODE_PRESET, std::max(DMX_MODE_SINGLE_RGB, int(personality)));
|
||||
doSerializeConfig = true;
|
||||
configNeedsWrite = true;
|
||||
DEBUG_PRINTF("DMX personality changed to to: %d\n", DMXMode);
|
||||
}
|
||||
}
|
||||
@ -40,7 +40,7 @@ void rdmAddressChangedCb(dmx_port_t dmxPort, const rdm_header_t *header,
|
||||
if (header->cc == RDM_CC_SET_COMMAND_RESPONSE) {
|
||||
const uint16_t addr = dmx_get_start_address(dmx->inputPortNum);
|
||||
DMXAddress = std::min(512, int(addr));
|
||||
doSerializeConfig = true;
|
||||
configNeedsWrite = true;
|
||||
DEBUG_PRINTF("DMX start addr changed to: %d\n", DMXAddress);
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,9 @@ public:
|
||||
void disable();
|
||||
void enable();
|
||||
|
||||
/// True if dmx is currently connected
|
||||
bool isConnected() const { return connected; }
|
||||
|
||||
private:
|
||||
/// @return true if rdm identify is active
|
||||
bool isIdentifyOn() const;
|
||||
|
@ -1,3 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef WLED_FCN_DECLARE_H
|
||||
#define WLED_FCN_DECLARE_H
|
||||
|
||||
@ -26,7 +27,8 @@ void IRAM_ATTR touchButtonISR();
|
||||
bool deserializeConfig(JsonObject doc, bool fromFS = false);
|
||||
void deserializeConfigFromFS();
|
||||
bool deserializeConfigSec();
|
||||
void serializeConfig();
|
||||
void serializeConfig(JsonObject doc);
|
||||
void serializeConfigToFS();
|
||||
void serializeConfigSec();
|
||||
|
||||
template<typename DestType>
|
||||
@ -52,6 +54,7 @@ bool getJsonValue(const JsonVariant& element, DestType& destination, const Defau
|
||||
typedef struct WiFiConfig {
|
||||
char clientSSID[33];
|
||||
char clientPass[65];
|
||||
uint8_t bssid[6];
|
||||
IPAddress staticIP;
|
||||
IPAddress staticGW;
|
||||
IPAddress staticSN;
|
||||
@ -62,6 +65,7 @@ typedef struct WiFiConfig {
|
||||
{
|
||||
strncpy(clientSSID, ssid, 32); clientSSID[32] = 0;
|
||||
strncpy(clientPass, pass, 64); clientPass[64] = 0;
|
||||
memset(bssid, 0, sizeof(bssid));
|
||||
}
|
||||
} wifi_config;
|
||||
|
||||
@ -202,14 +206,14 @@ void sendArtnetPollReply(ArtPollReply* reply, IPAddress ipAddress, uint16_t port
|
||||
bool handleFileRead(AsyncWebServerRequest*, String path);
|
||||
bool writeObjectToFileUsingId(const char* file, uint16_t id, const JsonDocument* content);
|
||||
bool writeObjectToFile(const char* file, const char* key, const JsonDocument* content);
|
||||
bool readObjectFromFileUsingId(const char* file, uint16_t id, JsonDocument* dest);
|
||||
bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest);
|
||||
bool readObjectFromFileUsingId(const char* file, uint16_t id, JsonDocument* dest, const JsonDocument* filter = nullptr);
|
||||
bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest, const JsonDocument* filter = nullptr);
|
||||
void updateFSInfo();
|
||||
void closeFile();
|
||||
inline bool writeObjectToFileUsingId(const String &file, uint16_t id, const JsonDocument* content) { return writeObjectToFileUsingId(file.c_str(), id, content); };
|
||||
inline bool writeObjectToFile(const String &file, const char* key, const JsonDocument* content) { return writeObjectToFile(file.c_str(), key, content); };
|
||||
inline bool readObjectFromFileUsingId(const String &file, uint16_t id, JsonDocument* dest) { return readObjectFromFileUsingId(file.c_str(), id, dest); };
|
||||
inline bool readObjectFromFile(const String &file, const char* key, JsonDocument* dest) { return readObjectFromFile(file.c_str(), key, dest); };
|
||||
inline bool readObjectFromFileUsingId(const String &file, uint16_t id, JsonDocument* dest, const JsonDocument* filter = nullptr) { return readObjectFromFileUsingId(file.c_str(), id, dest); };
|
||||
inline bool readObjectFromFile(const String &file, const char* key, JsonDocument* dest, const JsonDocument* filter = nullptr) { return readObjectFromFile(file.c_str(), key, dest); };
|
||||
|
||||
//hue.cpp
|
||||
void handleHue();
|
||||
@ -324,6 +328,7 @@ void serializePlaylist(JsonObject obj);
|
||||
|
||||
//presets.cpp
|
||||
const char *getPresetsFileName(bool persistent = true);
|
||||
bool presetNeedsSaving();
|
||||
void initPresetsFile();
|
||||
void handlePresets();
|
||||
bool applyPreset(byte index, byte callMode = CALL_MODE_DIRECT_CHANGE);
|
||||
@ -359,7 +364,12 @@ void espNowReceiveCB(uint8_t* address, uint8_t* data, uint8_t len, signed int rs
|
||||
#endif
|
||||
|
||||
//network.cpp
|
||||
bool initEthernet(); // result is informational
|
||||
int getSignalQuality(int rssi);
|
||||
void fillMAC2Str(char *str, const uint8_t *mac);
|
||||
void fillStr2MAC(uint8_t *mac, const char *str);
|
||||
int findWiFi(bool doScan = false);
|
||||
bool isWiFiConfigured();
|
||||
void WiFiEvent(WiFiEvent_t event);
|
||||
|
||||
//um_manager.cpp
|
||||
@ -477,6 +487,9 @@ void userLoop();
|
||||
#include "soc/wdev_reg.h"
|
||||
#define HW_RND_REGISTER REG_READ(WDEV_RND_REG)
|
||||
#endif
|
||||
#define inoise8 perlin8 // fastled legacy alias
|
||||
#define inoise16 perlin16 // fastled legacy alias
|
||||
#define hex2int(a) (((a)>='0' && (a)<='9') ? (a)-'0' : ((a)>='A' && (a)<='F') ? (a)-'A'+10 : ((a)>='a' && (a)<='f') ? (a)-'a'+10 : 0)
|
||||
[[gnu::pure]] int getNumVal(const String* req, uint16_t pos);
|
||||
void parseNumber(const char* str, byte* val, byte minv=0, byte maxv=255);
|
||||
bool getVal(JsonVariant elem, byte* val, byte vmin=0, byte vmax=255); // getVal supports inc/decrementing and random ("X~Y(r|[w]~[-][Z])" form)
|
||||
@ -504,6 +517,15 @@ void enumerateLedmaps();
|
||||
[[gnu::hot]] uint8_t get_random_wheel_index(uint8_t pos);
|
||||
[[gnu::hot, gnu::pure]] float mapf(float x, float in_min, float in_max, float out_min, float out_max);
|
||||
uint32_t hashInt(uint32_t s);
|
||||
int32_t perlin1D_raw(uint32_t x, bool is16bit = false);
|
||||
int32_t perlin2D_raw(uint32_t x, uint32_t y, bool is16bit = false);
|
||||
int32_t perlin3D_raw(uint32_t x, uint32_t y, uint32_t z, bool is16bit = false);
|
||||
uint16_t perlin16(uint32_t x);
|
||||
uint16_t perlin16(uint32_t x, uint32_t y);
|
||||
uint16_t perlin16(uint32_t x, uint32_t y, uint32_t z);
|
||||
uint8_t perlin8(uint16_t x);
|
||||
uint8_t perlin8(uint16_t x, uint16_t y);
|
||||
uint8_t perlin8(uint16_t x, uint16_t y, uint16_t z);
|
||||
|
||||
// fast (true) random numbers using hardware RNG, all functions return values in the range lowerlimit to upperlimit-1
|
||||
// note: for true random numbers with high entropy, do not call faster than every 200ns (5MHz)
|
||||
|
@ -325,15 +325,15 @@ bool writeObjectToFile(const char* file, const char* key, const JsonDocument* co
|
||||
return true;
|
||||
}
|
||||
|
||||
bool readObjectFromFileUsingId(const char* file, uint16_t id, JsonDocument* dest)
|
||||
bool readObjectFromFileUsingId(const char* file, uint16_t id, JsonDocument* dest, const JsonDocument* filter)
|
||||
{
|
||||
char objKey[10];
|
||||
sprintf(objKey, "\"%d\":", id);
|
||||
return readObjectFromFile(file, objKey, dest);
|
||||
return readObjectFromFile(file, objKey, dest, filter);
|
||||
}
|
||||
|
||||
//if the key is a nullptr, deserialize entire object
|
||||
bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest)
|
||||
bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest, const JsonDocument* filter)
|
||||
{
|
||||
if (doCloseFile) closeFile();
|
||||
#ifdef WLED_DEBUG_FS
|
||||
@ -352,7 +352,8 @@ bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest)
|
||||
return false;
|
||||
}
|
||||
|
||||
deserializeJson(*dest, f);
|
||||
if (filter) deserializeJson(*dest, f, DeserializationOption::Filter(*filter));
|
||||
else deserializeJson(*dest, f);
|
||||
|
||||
f.close();
|
||||
DEBUGFS_PRINTF("Read, took %d ms\n", millis() - s);
|
||||
|
@ -195,9 +195,9 @@ void onHueData(void* arg, AsyncClient* client, void *data, size_t len)
|
||||
{
|
||||
switch(hueColormode)
|
||||
{
|
||||
case 1: if (hueX != hueXLast || hueY != hueYLast) colorXYtoRGB(hueX,hueY,col); hueXLast = hueX; hueYLast = hueY; break;
|
||||
case 2: if (hueHue != hueHueLast || hueSat != hueSatLast) colorHStoRGB(hueHue,hueSat,col); hueHueLast = hueHue; hueSatLast = hueSat; break;
|
||||
case 3: if (hueCt != hueCtLast) colorCTtoRGB(hueCt,col); hueCtLast = hueCt; break;
|
||||
case 1: if (hueX != hueXLast || hueY != hueYLast) colorXYtoRGB(hueX,hueY,colPri); hueXLast = hueX; hueYLast = hueY; break;
|
||||
case 2: if (hueHue != hueHueLast || hueSat != hueSatLast) colorHStoRGB(hueHue,hueSat,colPri); hueHueLast = hueHue; hueSatLast = hueSat; break;
|
||||
case 3: if (hueCt != hueCtLast) colorCTtoRGB(hueCt,colPri); hueCtLast = hueCt; break;
|
||||
}
|
||||
}
|
||||
hueReceived = true;
|
||||
|
@ -272,5 +272,5 @@ void parseWiFiCommand(char* rpcData) {
|
||||
improvActive = 2;
|
||||
|
||||
forceReconnect = true;
|
||||
serializeConfig();
|
||||
serializeConfigToFS();
|
||||
}
|
@ -530,7 +530,7 @@ static void decodeIR9(uint32_t code)
|
||||
|
||||
/*
|
||||
This allows users to customize IR actions without the need to edit C code and compile.
|
||||
From the https://github.com/Aircoookie/WLED/wiki/Infrared-Control page, download the starter
|
||||
From the https://github.com/wled-dev/WLED/wiki/Infrared-Control page, download the starter
|
||||
ir.json file that corresponds to the number of buttons on your remote.
|
||||
Many of the remotes with the same number of buttons emit the same codes, but will have
|
||||
different labels or colors. Once you edit the ir.json file, upload it to your controller
|
||||
@ -611,9 +611,15 @@ static void decodeIRJson(uint32_t code)
|
||||
handleSet(nullptr, cmdStr, false); // no stateUpdated() call here
|
||||
}
|
||||
} else {
|
||||
// command is JSON object (TODO: currently will not handle irApplyToAllSelected correctly)
|
||||
if (jsonCmdObj[F("psave")].isNull()) deserializeState(jsonCmdObj, CALL_MODE_BUTTON_PRESET);
|
||||
else {
|
||||
// command is JSON object
|
||||
if (jsonCmdObj[F("psave")].isNull()) {
|
||||
if (irApplyToAllSelected && jsonCmdObj["seg"].is<JsonArray>()) {
|
||||
JsonObject seg = jsonCmdObj["seg"][0]; // take 1st segment from array and use it to apply to all selected segments
|
||||
seg.remove("id"); // remove segment ID if it exists
|
||||
jsonCmdObj["seg"] = seg; // replace array with object
|
||||
}
|
||||
deserializeState(jsonCmdObj, CALL_MODE_BUTTON_PRESET); // **will call stateUpdated() with correct CALL_MODE**
|
||||
} else {
|
||||
uint8_t psave = jsonCmdObj[F("psave")].as<int>();
|
||||
char pname[33];
|
||||
sprintf_P(pname, PSTR("IR Preset %d"), psave);
|
||||
@ -628,6 +634,7 @@ static void applyRepeatActions()
|
||||
{
|
||||
if (irEnabled == 8) {
|
||||
decodeIRJson(lastValidCode);
|
||||
stateUpdated(CALL_MODE_BUTTON_PRESET);
|
||||
return;
|
||||
} else switch (lastRepeatableAction) {
|
||||
case ACTION_BRIGHT_UP : incBrightness(); stateUpdated(CALL_MODE_BUTTON); return;
|
||||
@ -664,7 +671,7 @@ static void decodeIR(uint32_t code)
|
||||
|
||||
if (irEnabled == 8) { // any remote configurable with ir.json file
|
||||
decodeIRJson(code);
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
stateUpdated(CALL_MODE_BUTTON_PRESET);
|
||||
return;
|
||||
}
|
||||
if (code > 0xFFFFFF) return; //invalid code
|
||||
|
109
wled00/json.cpp
109
wled00/json.cpp
@ -2,15 +2,6 @@
|
||||
|
||||
#include "palettes.h"
|
||||
|
||||
#define JSON_PATH_STATE 1
|
||||
#define JSON_PATH_INFO 2
|
||||
#define JSON_PATH_STATE_INFO 3
|
||||
#define JSON_PATH_NODES 4
|
||||
#define JSON_PATH_PALETTES 5
|
||||
#define JSON_PATH_FXDATA 6
|
||||
#define JSON_PATH_NETWORKS 7
|
||||
#define JSON_PATH_EFFECTS 8
|
||||
|
||||
/*
|
||||
* JSON API (De)serialization
|
||||
*/
|
||||
@ -97,8 +88,14 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
uint8_t soundSim = elem["si"] | seg.soundSim;
|
||||
uint8_t map1D2D = elem["m12"] | seg.map1D2D;
|
||||
uint8_t set = elem[F("set")] | seg.set;
|
||||
seg.set = constrain(set, 0, 3);
|
||||
seg.soundSim = constrain(soundSim, 0, 3);
|
||||
bool selected = getBoolVal(elem["sel"], seg.selected);
|
||||
bool reverse = getBoolVal(elem["rev"], seg.reverse);
|
||||
bool mirror = getBoolVal(elem["mi"] , seg.mirror);
|
||||
#ifndef WLED_DISABLE_2D
|
||||
bool reverse_y = getBoolVal(elem["rY"] , seg.reverse_y);
|
||||
bool mirror_y = getBoolVal(elem["mY"] , seg.mirror_y);
|
||||
bool transpose = getBoolVal(elem[F("tp")], seg.transpose);
|
||||
#endif
|
||||
|
||||
int len = (stop > start) ? stop - start : 1;
|
||||
int offset = elem[F("of")] | INT32_MAX;
|
||||
@ -200,20 +197,16 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
}
|
||||
#endif
|
||||
|
||||
//seg.map1D2D = constrain(map1D2D, 0, 7); // done in setGeometry()
|
||||
seg.set = constrain(set, 0, 3);
|
||||
seg.soundSim = constrain(soundSim, 0, 3);
|
||||
seg.selected = selected;
|
||||
seg.reverse = reverse;
|
||||
seg.mirror = mirror;
|
||||
#ifndef WLED_DISABLE_2D
|
||||
bool reverse = seg.reverse;
|
||||
bool mirror = seg.mirror;
|
||||
#endif
|
||||
seg.selected = getBoolVal(elem["sel"], seg.selected);
|
||||
seg.reverse = getBoolVal(elem["rev"], seg.reverse);
|
||||
seg.mirror = getBoolVal(elem["mi"] , seg.mirror);
|
||||
#ifndef WLED_DISABLE_2D
|
||||
bool reverse_y = seg.reverse_y;
|
||||
bool mirror_y = seg.mirror_y;
|
||||
seg.reverse_y = getBoolVal(elem["rY"] , seg.reverse_y);
|
||||
seg.mirror_y = getBoolVal(elem["mY"] , seg.mirror_y);
|
||||
seg.transpose = getBoolVal(elem[F("tp")], seg.transpose);
|
||||
if (seg.is2D() && seg.map1D2D == M12_pArc && (reverse != seg.reverse || reverse_y != seg.reverse_y || mirror != seg.mirror || mirror_y != seg.mirror_y)) seg.fill(BLACK); // clear entire segment (in case of Arc 1D to 2D expansion)
|
||||
seg.reverse_y = reverse_y;
|
||||
seg.mirror_y = mirror_y;
|
||||
seg.transpose = transpose;
|
||||
#endif
|
||||
|
||||
byte fx = seg.mode;
|
||||
@ -338,7 +331,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
blendingStyle = root[F("bs")] | blendingStyle;
|
||||
blendingStyle = constrain(blendingStyle, 0, BLEND_STYLE_COUNT-1);
|
||||
blendingStyle &= 0x1F;
|
||||
#endif
|
||||
|
||||
// temporary transition (applies only once)
|
||||
@ -392,23 +385,25 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
|
||||
int it = 0;
|
||||
JsonVariant segVar = root["seg"];
|
||||
if (!segVar.isNull()) strip.suspend();
|
||||
if (segVar.is<JsonObject>())
|
||||
{
|
||||
if (!segVar.isNull()) {
|
||||
// we may be called during strip.service() so we must not modify segments while effects are executing
|
||||
strip.suspend();
|
||||
const unsigned long start = millis();
|
||||
while (strip.isServicing() && millis() - start < strip.getFrameTime()) yield(); // wait until frame is over
|
||||
#ifdef WLED_DEBUG
|
||||
if (millis() - start > 0) DEBUG_PRINTLN(F("JSON: Waited for strip to finish servicing."));
|
||||
#endif
|
||||
if (segVar.is<JsonObject>()) {
|
||||
int id = segVar["id"] | -1;
|
||||
//if "seg" is not an array and ID not specified, apply to all selected/checked segments
|
||||
if (id < 0) {
|
||||
//apply all selected segments
|
||||
//bool didSet = false;
|
||||
for (size_t s = 0; s < strip.getSegmentsNum(); s++) {
|
||||
Segment &sg = strip.getSegment(s);
|
||||
if (sg.isActive() && sg.isSelected()) {
|
||||
deserializeSegment(segVar, s, presetId);
|
||||
//didSet = true;
|
||||
}
|
||||
}
|
||||
//TODO: not sure if it is good idea to change first active but unselected segment
|
||||
//if (!didSet) deserializeSegment(segVar, strip.getMainSegmentId(), presetId);
|
||||
} else {
|
||||
deserializeSegment(segVar, id, presetId); //apply only the segment with the specified ID
|
||||
}
|
||||
@ -421,6 +416,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
if (strip.getSegmentsNum() > 3 && deleted >= strip.getSegmentsNum()/2U) strip.purgeSegments(); // batch deleting more than half segments
|
||||
}
|
||||
strip.resume();
|
||||
}
|
||||
|
||||
UsermodManager::readFromJsonState(root);
|
||||
|
||||
@ -1031,16 +1027,21 @@ class LockedJsonResponse: public AsyncJsonResponse {
|
||||
|
||||
void serveJson(AsyncWebServerRequest* request)
|
||||
{
|
||||
byte subJson = 0;
|
||||
enum class json_target {
|
||||
all, state, info, state_info, nodes, effects, palettes, fxdata, networks, config
|
||||
};
|
||||
json_target subJson = json_target::all;
|
||||
|
||||
const String& url = request->url();
|
||||
if (url.indexOf("state") > 0) subJson = JSON_PATH_STATE;
|
||||
else if (url.indexOf("info") > 0) subJson = JSON_PATH_INFO;
|
||||
else if (url.indexOf("si") > 0) subJson = JSON_PATH_STATE_INFO;
|
||||
else if (url.indexOf(F("nodes")) > 0) subJson = JSON_PATH_NODES;
|
||||
else if (url.indexOf(F("eff")) > 0) subJson = JSON_PATH_EFFECTS;
|
||||
else if (url.indexOf(F("palx")) > 0) subJson = JSON_PATH_PALETTES;
|
||||
else if (url.indexOf(F("fxda")) > 0) subJson = JSON_PATH_FXDATA;
|
||||
else if (url.indexOf(F("net")) > 0) subJson = JSON_PATH_NETWORKS;
|
||||
if (url.indexOf("state") > 0) subJson = json_target::state;
|
||||
else if (url.indexOf("info") > 0) subJson = json_target::info;
|
||||
else if (url.indexOf("si") > 0) subJson = json_target::state_info;
|
||||
else if (url.indexOf(F("nodes")) > 0) subJson = json_target::nodes;
|
||||
else if (url.indexOf(F("eff")) > 0) subJson = json_target::effects;
|
||||
else if (url.indexOf(F("palx")) > 0) subJson = json_target::palettes;
|
||||
else if (url.indexOf(F("fxda")) > 0) subJson = json_target::fxdata;
|
||||
else if (url.indexOf(F("net")) > 0) subJson = json_target::networks;
|
||||
else if (url.indexOf(F("cfg")) > 0) subJson = json_target::config;
|
||||
#ifdef WLED_ENABLE_JSONLIVE
|
||||
else if (url.indexOf("live") > 0) {
|
||||
serveLiveLeds(request);
|
||||
@ -1051,9 +1052,6 @@ void serveJson(AsyncWebServerRequest* request)
|
||||
request->send_P(200, FPSTR(CONTENT_TYPE_JSON), JSON_palette_names);
|
||||
return;
|
||||
}
|
||||
else if (url.indexOf(F("cfg")) > 0 && handleFileRead(request, F("/cfg.json"))) {
|
||||
return;
|
||||
}
|
||||
else if (url.length() > 6) { //not just /json
|
||||
serveJsonError(request, 501, ERR_NOT_IMPL);
|
||||
return;
|
||||
@ -1065,32 +1063,35 @@ void serveJson(AsyncWebServerRequest* request)
|
||||
}
|
||||
// releaseJSONBufferLock() will be called when "response" is destroyed (from AsyncWebServer)
|
||||
// make sure you delete "response" if no "request->send(response);" is made
|
||||
LockedJsonResponse *response = new LockedJsonResponse(pDoc, subJson==JSON_PATH_FXDATA || subJson==JSON_PATH_EFFECTS); // will clear and convert JsonDocument into JsonArray if necessary
|
||||
LockedJsonResponse *response = new LockedJsonResponse(pDoc, subJson==json_target::fxdata || subJson==json_target::effects); // will clear and convert JsonDocument into JsonArray if necessary
|
||||
|
||||
JsonVariant lDoc = response->getRoot();
|
||||
|
||||
switch (subJson)
|
||||
{
|
||||
case JSON_PATH_STATE:
|
||||
case json_target::state:
|
||||
serializeState(lDoc); break;
|
||||
case JSON_PATH_INFO:
|
||||
case json_target::info:
|
||||
serializeInfo(lDoc); break;
|
||||
case JSON_PATH_NODES:
|
||||
case json_target::nodes:
|
||||
serializeNodes(lDoc); break;
|
||||
case JSON_PATH_PALETTES:
|
||||
case json_target::palettes:
|
||||
serializePalettes(lDoc, request->hasParam(F("page")) ? request->getParam(F("page"))->value().toInt() : 0); break;
|
||||
case JSON_PATH_EFFECTS:
|
||||
case json_target::effects:
|
||||
serializeModeNames(lDoc); break;
|
||||
case JSON_PATH_FXDATA:
|
||||
case json_target::fxdata:
|
||||
serializeModeData(lDoc); break;
|
||||
case JSON_PATH_NETWORKS:
|
||||
case json_target::networks:
|
||||
serializeNetworks(lDoc); break;
|
||||
default: //all
|
||||
case json_target::config:
|
||||
serializeConfig(lDoc); break;
|
||||
case json_target::state_info:
|
||||
case json_target::all:
|
||||
JsonObject state = lDoc.createNestedObject("state");
|
||||
serializeState(state);
|
||||
JsonObject info = lDoc.createNestedObject("info");
|
||||
serializeInfo(info);
|
||||
if (subJson != JSON_PATH_STATE_INFO)
|
||||
if (subJson == json_target::all)
|
||||
{
|
||||
JsonArray effects = lDoc.createNestedArray(F("effects"));
|
||||
serializeModeNames(effects); // remove WLED-SR extensions from effect names
|
||||
|
@ -9,10 +9,10 @@ void setValuesFromFirstSelectedSeg() { setValuesFromSegment(strip.getFirstSelect
|
||||
void setValuesFromSegment(uint8_t s)
|
||||
{
|
||||
Segment& seg = strip.getSegment(s);
|
||||
col[0] = R(seg.colors[0]);
|
||||
col[1] = G(seg.colors[0]);
|
||||
col[2] = B(seg.colors[0]);
|
||||
col[3] = W(seg.colors[0]);
|
||||
colPri[0] = R(seg.colors[0]);
|
||||
colPri[1] = G(seg.colors[0]);
|
||||
colPri[2] = B(seg.colors[0]);
|
||||
colPri[3] = W(seg.colors[0]);
|
||||
colSec[0] = R(seg.colors[1]);
|
||||
colSec[1] = G(seg.colors[1]);
|
||||
colSec[2] = B(seg.colors[1]);
|
||||
@ -39,7 +39,7 @@ void applyValuesToSelectedSegs()
|
||||
if (effectIntensity != selsegPrev.intensity) {seg.intensity = effectIntensity; stateChanged = true;}
|
||||
if (effectPalette != selsegPrev.palette) {seg.setPalette(effectPalette);}
|
||||
if (effectCurrent != selsegPrev.mode) {seg.setMode(effectCurrent);}
|
||||
uint32_t col0 = RGBW32( col[0], col[1], col[2], col[3]);
|
||||
uint32_t col0 = RGBW32(colPri[0], colPri[1], colPri[2], colPri[3]);
|
||||
uint32_t col1 = RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]);
|
||||
if (col0 != selsegPrev.colors[0]) {seg.setColor(0, col0);}
|
||||
if (col1 != selsegPrev.colors[1]) {seg.setColor(1, col1);}
|
||||
@ -112,10 +112,11 @@ void stateUpdated(byte callMode) {
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long now = millis();
|
||||
if (callMode != CALL_MODE_NO_NOTIFY && nightlightActive && (nightlightMode == NL_MODE_FADE || nightlightMode == NL_MODE_COLORFADE)) {
|
||||
briNlT = bri;
|
||||
nightlightDelayMs -= (millis() - nightlightStartTime);
|
||||
nightlightStartTime = millis();
|
||||
nightlightDelayMs -= (now - nightlightStartTime);
|
||||
nightlightStartTime = now;
|
||||
}
|
||||
if (briT == 0) {
|
||||
if (callMode != CALL_MODE_NOTIFICATION) strip.resetTimebase(); //effect start from beginning
|
||||
@ -141,7 +142,7 @@ void stateUpdated(byte callMode) {
|
||||
} else
|
||||
strip.setTransitionMode(true); // force all segments to transition mode
|
||||
transitionActive = true;
|
||||
transitionStartTime = millis();
|
||||
transitionStartTime = now;
|
||||
}
|
||||
|
||||
|
||||
@ -150,14 +151,14 @@ void updateInterfaces(uint8_t callMode) {
|
||||
|
||||
sendDataWs();
|
||||
lastInterfaceUpdate = millis();
|
||||
interfaceUpdateCallMode = 0; //disable further updates
|
||||
interfaceUpdateCallMode = CALL_MODE_INIT; //disable further updates
|
||||
|
||||
if (callMode == CALL_MODE_WS_SEND) return;
|
||||
|
||||
#ifndef WLED_DISABLE_ALEXA
|
||||
if (espalexaDevice != nullptr && callMode != CALL_MODE_ALEXA) {
|
||||
espalexaDevice->setValue(bri);
|
||||
espalexaDevice->setColor(col[0], col[1], col[2]);
|
||||
espalexaDevice->setColor(colPri[0], colPri[1], colPri[2]);
|
||||
}
|
||||
#endif
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
@ -211,7 +212,7 @@ void handleNightlight() {
|
||||
nightlightDelayMs = (unsigned)(nightlightDelayMins*60000);
|
||||
nightlightActiveOld = true;
|
||||
briNlT = bri;
|
||||
for (unsigned i=0; i<4; i++) colNlT[i] = col[i]; // remember starting color
|
||||
for (unsigned i=0; i<4; i++) colNlT[i] = colPri[i]; // remember starting color
|
||||
if (nightlightMode == NL_MODE_SUN)
|
||||
{
|
||||
//save current
|
||||
@ -236,7 +237,7 @@ void handleNightlight() {
|
||||
bri = briNlT + ((nightlightTargetBri - briNlT)*nper);
|
||||
if (nightlightMode == NL_MODE_COLORFADE) // color fading only is enabled with "NF=2"
|
||||
{
|
||||
for (unsigned i=0; i<4; i++) col[i] = colNlT[i]+ ((colSec[i] - colNlT[i])*nper); // fading from actual color to secondary color
|
||||
for (unsigned i=0; i<4; i++) colPri[i] = colNlT[i]+ ((colSec[i] - colNlT[i])*nper); // fading from actual color to secondary color
|
||||
}
|
||||
colorUpdated(CALL_MODE_NO_NOTIFY);
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ static void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProp
|
||||
//Prefix is stripped from the topic at this point
|
||||
|
||||
if (strcmp_P(topic, PSTR("/col")) == 0) {
|
||||
colorFromDecOrHexString(col, payloadStr);
|
||||
colorFromDecOrHexString(colPri, payloadStr);
|
||||
colorUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
} else if (strcmp_P(topic, PSTR("/api")) == 0) {
|
||||
if (requestJSONBufferLock(15)) {
|
||||
@ -169,7 +169,7 @@ void publishMqtt()
|
||||
strcat_P(subuf, PSTR("/g"));
|
||||
mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2263)
|
||||
|
||||
sprintf_P(s, PSTR("#%06X"), (col[3] << 24) | (col[0] << 16) | (col[1] << 8) | (col[2]));
|
||||
sprintf_P(s, PSTR("#%06X"), (colPri[3] << 24) | (colPri[0] << 16) | (colPri[1] << 8) | (colPri[2]));
|
||||
strlcpy(subuf, mqttDeviceTopic, MQTT_MAX_TOPIC_LEN + 1);
|
||||
strcat_P(subuf, PSTR("/c"));
|
||||
mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2263)
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include "wled_ethernet.h"
|
||||
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
// The following six pins are neither configurable nor
|
||||
// can they be re-assigned through IOMUX / GPIO matrix.
|
||||
// See https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-ethernet-kit-v1.1.html#ip101gri-phy-interface
|
||||
@ -146,6 +146,101 @@ const ethernet_settings ethernetBoards[] = {
|
||||
ETH_CLOCK_GPIO0_OUT // eth_clk_mode
|
||||
}
|
||||
};
|
||||
|
||||
bool initEthernet()
|
||||
{
|
||||
static bool successfullyConfiguredEthernet = false;
|
||||
|
||||
if (successfullyConfiguredEthernet) {
|
||||
// DEBUG_PRINTLN(F("initE: ETH already successfully configured, ignoring"));
|
||||
return false;
|
||||
}
|
||||
if (ethernetType == WLED_ETH_NONE) {
|
||||
return false;
|
||||
}
|
||||
if (ethernetType >= WLED_NUM_ETH_TYPES) {
|
||||
DEBUG_PRINTF_P(PSTR("initE: Ignoring attempt for invalid ethernetType (%d)\n"), ethernetType);
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_PRINTF_P(PSTR("initE: Attempting ETH config: %d\n"), ethernetType);
|
||||
|
||||
// Ethernet initialization should only succeed once -- else reboot required
|
||||
ethernet_settings es = ethernetBoards[ethernetType];
|
||||
managed_pin_type pinsToAllocate[10] = {
|
||||
// first six pins are non-configurable
|
||||
esp32_nonconfigurable_ethernet_pins[0],
|
||||
esp32_nonconfigurable_ethernet_pins[1],
|
||||
esp32_nonconfigurable_ethernet_pins[2],
|
||||
esp32_nonconfigurable_ethernet_pins[3],
|
||||
esp32_nonconfigurable_ethernet_pins[4],
|
||||
esp32_nonconfigurable_ethernet_pins[5],
|
||||
{ (int8_t)es.eth_mdc, true }, // [6] = MDC is output and mandatory
|
||||
{ (int8_t)es.eth_mdio, true }, // [7] = MDIO is bidirectional and mandatory
|
||||
{ (int8_t)es.eth_power, true }, // [8] = optional pin, not all boards use
|
||||
{ ((int8_t)0xFE), false }, // [9] = replaced with eth_clk_mode, mandatory
|
||||
};
|
||||
// update the clock pin....
|
||||
if (es.eth_clk_mode == ETH_CLOCK_GPIO0_IN) {
|
||||
pinsToAllocate[9].pin = 0;
|
||||
pinsToAllocate[9].isOutput = false;
|
||||
} else if (es.eth_clk_mode == ETH_CLOCK_GPIO0_OUT) {
|
||||
pinsToAllocate[9].pin = 0;
|
||||
pinsToAllocate[9].isOutput = true;
|
||||
} else if (es.eth_clk_mode == ETH_CLOCK_GPIO16_OUT) {
|
||||
pinsToAllocate[9].pin = 16;
|
||||
pinsToAllocate[9].isOutput = true;
|
||||
} else if (es.eth_clk_mode == ETH_CLOCK_GPIO17_OUT) {
|
||||
pinsToAllocate[9].pin = 17;
|
||||
pinsToAllocate[9].isOutput = true;
|
||||
} else {
|
||||
DEBUG_PRINTF_P(PSTR("initE: Failing due to invalid eth_clk_mode (%d)\n"), es.eth_clk_mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!PinManager::allocateMultiplePins(pinsToAllocate, 10, PinOwner::Ethernet)) {
|
||||
DEBUG_PRINTLN(F("initE: Failed to allocate ethernet pins"));
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
For LAN8720 the most correct way is to perform clean reset each time before init
|
||||
applying LOW to power or nRST pin for at least 100 us (please refer to datasheet, page 59)
|
||||
ESP_IDF > V4 implements it (150 us, lan87xx_reset_hw(esp_eth_phy_t *phy) function in
|
||||
/components/esp_eth/src/esp_eth_phy_lan87xx.c, line 280)
|
||||
but ESP_IDF < V4 does not. Lets do it:
|
||||
[not always needed, might be relevant in some EMI situations at startup and for hot resets]
|
||||
*/
|
||||
#if ESP_IDF_VERSION_MAJOR==3
|
||||
if(es.eth_power>0 && es.eth_type==ETH_PHY_LAN8720) {
|
||||
pinMode(es.eth_power, OUTPUT);
|
||||
digitalWrite(es.eth_power, 0);
|
||||
delayMicroseconds(150);
|
||||
digitalWrite(es.eth_power, 1);
|
||||
delayMicroseconds(10);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!ETH.begin(
|
||||
(uint8_t) es.eth_address,
|
||||
(int) es.eth_power,
|
||||
(int) es.eth_mdc,
|
||||
(int) es.eth_mdio,
|
||||
(eth_phy_type_t) es.eth_type,
|
||||
(eth_clock_mode_t) es.eth_clk_mode
|
||||
)) {
|
||||
DEBUG_PRINTLN(F("initC: ETH.begin() failed"));
|
||||
// de-allocate the allocated pins
|
||||
for (managed_pin_type mpt : pinsToAllocate) {
|
||||
PinManager::deallocatePin(mpt.pin, PinOwner::Ethernet);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
successfullyConfiguredEthernet = true;
|
||||
DEBUG_PRINTLN(F("initC: *** Ethernet successfully configured! ***"));
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -170,19 +265,136 @@ int getSignalQuality(int rssi)
|
||||
}
|
||||
|
||||
|
||||
void fillMAC2Str(char *str, const uint8_t *mac) {
|
||||
sprintf_P(str, PSTR("%02x%02x%02x%02x%02x%02x"), MAC2STR(mac));
|
||||
byte nul = 0;
|
||||
for (int i = 0; i < 6; i++) nul |= *mac++; // do we have 0
|
||||
if (!nul) str[0] = '\0'; // empty string
|
||||
}
|
||||
|
||||
void fillStr2MAC(uint8_t *mac, const char *str) {
|
||||
for (int i = 0; i < 6; i++) *mac++ = 0; // clear
|
||||
if (!str) return; // null string
|
||||
uint64_t MAC = strtoull(str, nullptr, 16);
|
||||
for (int i = 0; i < 6; i++) { *--mac = MAC & 0xFF; MAC >>= 8; }
|
||||
}
|
||||
|
||||
|
||||
// performs asynchronous scan for available networks (which may take couple of seconds to finish)
|
||||
// returns configured WiFi ID with the strongest signal (or default if no configured networks available)
|
||||
int findWiFi(bool doScan) {
|
||||
if (multiWiFi.size() <= 1) {
|
||||
DEBUG_PRINTF_P(PSTR("WiFi: Defaulf SSID (%s) used.\n"), multiWiFi[0].clientSSID);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int status = WiFi.scanComplete(); // complete scan may take as much as several seconds (usually <6s with not very crowded air)
|
||||
|
||||
if (doScan || status == WIFI_SCAN_FAILED) {
|
||||
DEBUG_PRINTF_P(PSTR("WiFi: Scan started. @ %lus\n"), millis()/1000);
|
||||
WiFi.scanNetworks(true); // start scanning in asynchronous mode (will delete old scan)
|
||||
} else if (status >= 0) { // status contains number of found networks (including duplicate SSIDs with different BSSID)
|
||||
DEBUG_PRINTF_P(PSTR("WiFi: Found %d SSIDs. @ %lus\n"), status, millis()/1000);
|
||||
int rssi = -9999;
|
||||
int selected = selectedWiFi;
|
||||
for (int o = 0; o < status; o++) {
|
||||
DEBUG_PRINTF_P(PSTR(" SSID: %s (BSSID: %s) RSSI: %ddB\n"), WiFi.SSID(o).c_str(), WiFi.BSSIDstr(o).c_str(), WiFi.RSSI(o));
|
||||
for (unsigned n = 0; n < multiWiFi.size(); n++)
|
||||
if (!strcmp(WiFi.SSID(o).c_str(), multiWiFi[n].clientSSID)) {
|
||||
bool foundBSSID = memcmp(multiWiFi[n].bssid, WiFi.BSSID(o), 6) == 0;
|
||||
// find the WiFi with the strongest signal (but keep priority of entry if signal difference is not big)
|
||||
if (foundBSSID || (n < selected && WiFi.RSSI(o) > rssi-10) || WiFi.RSSI(o) > rssi) {
|
||||
rssi = foundBSSID ? 0 : WiFi.RSSI(o); // RSSI is only ever negative
|
||||
selected = n;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
DEBUG_PRINTF_P(PSTR("WiFi: Selected SSID: %s RSSI: %ddB\n"), multiWiFi[selected].clientSSID, rssi);
|
||||
return selected;
|
||||
}
|
||||
//DEBUG_PRINT(F("WiFi scan running."));
|
||||
return status; // scan is still running or there was an error
|
||||
}
|
||||
|
||||
|
||||
bool isWiFiConfigured() {
|
||||
return multiWiFi.size() > 1 || (strlen(multiWiFi[0].clientSSID) >= 1 && strcmp_P(multiWiFi[0].clientSSID, PSTR(DEFAULT_CLIENT_SSID)) != 0);
|
||||
}
|
||||
|
||||
#if defined(ESP8266)
|
||||
#define ARDUINO_EVENT_WIFI_AP_STADISCONNECTED WIFI_EVENT_SOFTAPMODE_STADISCONNECTED
|
||||
#define ARDUINO_EVENT_WIFI_AP_STACONNECTED WIFI_EVENT_SOFTAPMODE_STACONNECTED
|
||||
#define ARDUINO_EVENT_WIFI_STA_GOT_IP WIFI_EVENT_STAMODE_GOT_IP
|
||||
#define ARDUINO_EVENT_WIFI_STA_CONNECTED WIFI_EVENT_STAMODE_CONNECTED
|
||||
#define ARDUINO_EVENT_WIFI_STA_DISCONNECTED WIFI_EVENT_STAMODE_DISCONNECTED
|
||||
#elif defined(ARDUINO_ARCH_ESP32) && !defined(ESP_ARDUINO_VERSION_MAJOR) //ESP_IDF_VERSION_MAJOR==3
|
||||
// not strictly IDF v3 but Arduino core related
|
||||
#define ARDUINO_EVENT_WIFI_AP_STADISCONNECTED SYSTEM_EVENT_AP_STADISCONNECTED
|
||||
#define ARDUINO_EVENT_WIFI_AP_STACONNECTED SYSTEM_EVENT_AP_STACONNECTED
|
||||
#define ARDUINO_EVENT_WIFI_STA_GOT_IP SYSTEM_EVENT_STA_GOT_IP
|
||||
#define ARDUINO_EVENT_WIFI_STA_CONNECTED SYSTEM_EVENT_STA_CONNECTED
|
||||
#define ARDUINO_EVENT_WIFI_STA_DISCONNECTED SYSTEM_EVENT_STA_DISCONNECTED
|
||||
#define ARDUINO_EVENT_WIFI_AP_START SYSTEM_EVENT_AP_START
|
||||
#define ARDUINO_EVENT_WIFI_AP_STOP SYSTEM_EVENT_AP_STOP
|
||||
#define ARDUINO_EVENT_WIFI_SCAN_DONE SYSTEM_EVENT_SCAN_DONE
|
||||
#define ARDUINO_EVENT_ETH_START SYSTEM_EVENT_ETH_START
|
||||
#define ARDUINO_EVENT_ETH_CONNECTED SYSTEM_EVENT_ETH_CONNECTED
|
||||
#define ARDUINO_EVENT_ETH_DISCONNECTED SYSTEM_EVENT_ETH_DISCONNECTED
|
||||
#endif
|
||||
|
||||
//handle Ethernet connection event
|
||||
void WiFiEvent(WiFiEvent_t event)
|
||||
{
|
||||
switch (event) {
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
case SYSTEM_EVENT_ETH_START:
|
||||
DEBUG_PRINTLN(F("ETH Started"));
|
||||
case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED:
|
||||
// AP client disconnected
|
||||
if (--apClients == 0 && isWiFiConfigured()) forceReconnect = true; // no clients reconnect WiFi if awailable
|
||||
DEBUG_PRINTF_P(PSTR("WiFi-E: AP Client Disconnected (%d) @ %lus.\n"), (int)apClients, millis()/1000);
|
||||
break;
|
||||
case SYSTEM_EVENT_ETH_CONNECTED:
|
||||
case ARDUINO_EVENT_WIFI_AP_STACONNECTED:
|
||||
// AP client connected
|
||||
apClients++;
|
||||
DEBUG_PRINTF_P(PSTR("WiFi-E: AP Client Connected (%d) @ %lus.\n"), (int)apClients, millis()/1000);
|
||||
break;
|
||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
||||
DEBUG_PRINT(F("WiFi-E: IP address: ")); DEBUG_PRINTLN(Network.localIP());
|
||||
break;
|
||||
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
|
||||
// followed by IDLE and SCAN_DONE
|
||||
DEBUG_PRINTF_P(PSTR("WiFi-E: Connected! @ %lus\n"), millis()/1000);
|
||||
wasConnected = true;
|
||||
break;
|
||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
||||
if (wasConnected && interfacesInited) {
|
||||
DEBUG_PRINTF_P(PSTR("WiFi-E: Disconnected! @ %lus\n"), millis()/1000);
|
||||
if (interfacesInited && multiWiFi.size() > 1 && WiFi.scanComplete() >= 0) {
|
||||
findWiFi(true); // reinit WiFi scan
|
||||
forceReconnect = true;
|
||||
}
|
||||
interfacesInited = false;
|
||||
}
|
||||
break;
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
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_AP_START:
|
||||
DEBUG_PRINTLN(F("WiFi-E: AP Started"));
|
||||
break;
|
||||
case ARDUINO_EVENT_WIFI_AP_STOP:
|
||||
DEBUG_PRINTLN(F("WiFi-E: AP Stopped"));
|
||||
break;
|
||||
#if defined(WLED_USE_ETHERNET)
|
||||
case ARDUINO_EVENT_ETH_START:
|
||||
DEBUG_PRINTLN(F("ETH-E: Started"));
|
||||
break;
|
||||
case ARDUINO_EVENT_ETH_CONNECTED:
|
||||
{
|
||||
DEBUG_PRINTLN(F("ETH Connected"));
|
||||
DEBUG_PRINTLN(F("ETH-E: Connected"));
|
||||
if (!apActive) {
|
||||
WiFi.disconnect(true);
|
||||
WiFi.disconnect(true); // disable WiFi entirely
|
||||
}
|
||||
if (multiWiFi[0].staticIP != (uint32_t)0x00000000 && multiWiFi[0].staticGW != (uint32_t)0x00000000) {
|
||||
ETH.config(multiWiFi[0].staticIP, multiWiFi[0].staticGW, multiWiFi[0].staticSN, dnsAddress);
|
||||
@ -196,18 +408,20 @@ void WiFiEvent(WiFiEvent_t event)
|
||||
showWelcomePage = false;
|
||||
break;
|
||||
}
|
||||
case SYSTEM_EVENT_ETH_DISCONNECTED:
|
||||
DEBUG_PRINTLN(F("ETH Disconnected"));
|
||||
case ARDUINO_EVENT_ETH_DISCONNECTED:
|
||||
DEBUG_PRINTLN(F("ETH-E: Disconnected"));
|
||||
// This doesn't really affect ethernet per se,
|
||||
// as it's only configured once. Rather, it
|
||||
// may be necessary to reconnect the WiFi when
|
||||
// ethernet disconnects, as a way to provide
|
||||
// alternative access to the device.
|
||||
if (interfacesInited && WiFi.scanComplete() >= 0) findWiFi(true); // reinit WiFi scan
|
||||
forceReconnect = true;
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
default:
|
||||
DEBUG_PRINTF_P(PSTR("Network event: %d\n"), (int)event);
|
||||
DEBUG_PRINTF_P(PSTR("WiFi-E: Event %d\n"), (int)event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -10,19 +10,19 @@ typedef struct PlaylistEntry {
|
||||
uint16_t tr; //Duration of the transition TO this entry (in tenths of seconds)
|
||||
} ple;
|
||||
|
||||
byte playlistRepeat = 1; //how many times to repeat the playlist (0 = infinitely)
|
||||
byte playlistEndPreset = 0; //what preset to apply after playlist end (0 = stay on last preset)
|
||||
byte playlistOptions = 0; //bit 0: shuffle playlist after each iteration. bits 1-7 TBD
|
||||
static byte playlistRepeat = 1; //how many times to repeat the playlist (0 = infinitely)
|
||||
static byte playlistEndPreset = 0; //what preset to apply after playlist end (0 = stay on last preset)
|
||||
static byte playlistOptions = 0; //bit 0: shuffle playlist after each iteration. bits 1-7 TBD
|
||||
|
||||
PlaylistEntry *playlistEntries = nullptr;
|
||||
byte playlistLen; //number of playlist entries
|
||||
int8_t playlistIndex = -1;
|
||||
uint16_t playlistEntryDur = 0; //duration of the current entry in tenths of seconds
|
||||
static PlaylistEntry *playlistEntries = nullptr;
|
||||
static byte playlistLen; //number of playlist entries
|
||||
static int8_t playlistIndex = -1;
|
||||
static uint16_t playlistEntryDur = 0; //duration of the current entry in tenths of seconds
|
||||
|
||||
//values we need to keep about the parent playlist while inside sub-playlist
|
||||
//int8_t parentPlaylistIndex = -1;
|
||||
//byte parentPlaylistRepeat = 0;
|
||||
//byte parentPlaylistPresetId = 0; //for re-loading
|
||||
static int16_t parentPlaylistIndex = -1;
|
||||
static byte parentPlaylistRepeat = 0;
|
||||
static byte parentPlaylistPresetId = 0; //for re-loading
|
||||
|
||||
|
||||
void shufflePlaylist() {
|
||||
@ -54,6 +54,12 @@ void unloadPlaylist() {
|
||||
|
||||
|
||||
int16_t loadPlaylist(JsonObject playlistObj, byte presetId) {
|
||||
if (currentPlaylist > 0 && parentPlaylistPresetId > 0) return -1; // we are already in nested playlist, do nothing
|
||||
if (currentPlaylist > 0) {
|
||||
parentPlaylistIndex = playlistIndex;
|
||||
parentPlaylistRepeat = playlistRepeat;
|
||||
parentPlaylistPresetId = currentPlaylist;
|
||||
}
|
||||
unloadPlaylist();
|
||||
|
||||
JsonArray presets = playlistObj["ps"];
|
||||
@ -79,7 +85,7 @@ int16_t loadPlaylist(JsonObject playlistObj, byte presetId) {
|
||||
} else {
|
||||
for (int dur : durations) {
|
||||
if (it >= playlistLen) break;
|
||||
playlistEntries[it].dur = (dur > 1) ? dur : 100;
|
||||
playlistEntries[it].dur = constrain(dur, 0, 65530);
|
||||
it++;
|
||||
}
|
||||
}
|
||||
@ -117,6 +123,19 @@ int16_t loadPlaylist(JsonObject playlistObj, byte presetId) {
|
||||
shuffle = shuffle || playlistObj["r"];
|
||||
if (shuffle) playlistOptions |= PL_OPTION_SHUFFLE;
|
||||
|
||||
if (parentPlaylistPresetId == 0 && parentPlaylistIndex > -1) {
|
||||
// we are re-loading playlist when returning from nested playlist
|
||||
playlistIndex = parentPlaylistIndex;
|
||||
playlistRepeat = parentPlaylistRepeat;
|
||||
parentPlaylistIndex = -1;
|
||||
parentPlaylistRepeat = 0;
|
||||
} else if (rep == 0) {
|
||||
// endless playlist will never return to parent so erase parent information if it was called from it
|
||||
parentPlaylistPresetId = 0;
|
||||
parentPlaylistIndex = -1;
|
||||
parentPlaylistRepeat = 0;
|
||||
}
|
||||
|
||||
currentPlaylist = presetId;
|
||||
DEBUG_PRINTLN(F("Playlist loaded."));
|
||||
return currentPlaylist;
|
||||
@ -127,7 +146,7 @@ void handlePlaylist() {
|
||||
static unsigned long presetCycledTime = 0;
|
||||
if (currentPlaylist < 0 || playlistEntries == nullptr) return;
|
||||
|
||||
if (millis() - presetCycledTime > (100 * playlistEntryDur) || doAdvancePlaylist) {
|
||||
if ((playlistEntryDur < UINT16_MAX && millis() - presetCycledTime > 100 * playlistEntryDur) || doAdvancePlaylist) {
|
||||
presetCycledTime = millis();
|
||||
if (bri == 0 || nightlightActive) return;
|
||||
|
||||
@ -137,7 +156,10 @@ if (millis() - presetCycledTime > (100 * playlistEntryDur) || doAdvancePlaylist)
|
||||
if (!playlistIndex) {
|
||||
if (playlistRepeat == 1) { //stop if all repetitions are done
|
||||
unloadPlaylist();
|
||||
if (playlistEndPreset) applyPresetFromPlaylist(playlistEndPreset);
|
||||
if (parentPlaylistPresetId > 0) {
|
||||
applyPresetFromPlaylist(parentPlaylistPresetId); // reload previous playlist (unfortunately asynchronous)
|
||||
parentPlaylistPresetId = 0; // reset previous playlist but do not reset Index or Repeat (they will be loaded & reset in loadPlaylist())
|
||||
} else if (playlistEndPreset) applyPresetFromPlaylist(playlistEndPreset);
|
||||
return;
|
||||
}
|
||||
if (playlistRepeat > 1) playlistRepeat--; // decrease repeat count on each index reset if not an endless playlist
|
||||
@ -147,7 +169,7 @@ if (millis() - presetCycledTime > (100 * playlistEntryDur) || doAdvancePlaylist)
|
||||
|
||||
jsonTransitionOnce = true;
|
||||
strip.setTransition(playlistEntries[playlistIndex].tr * 100);
|
||||
playlistEntryDur = playlistEntries[playlistIndex].dur;
|
||||
playlistEntryDur = playlistEntries[playlistIndex].dur > 0 ? playlistEntries[playlistIndex].dur : UINT16_MAX;
|
||||
applyPresetFromPlaylist(playlistEntries[playlistIndex].preset);
|
||||
doAdvancePlaylist = false;
|
||||
}
|
||||
|
@ -22,6 +22,10 @@ const char *getPresetsFileName(bool persistent) {
|
||||
return persistent ? presets_json : tmp_json;
|
||||
}
|
||||
|
||||
bool presetNeedsSaving() {
|
||||
return presetToSave;
|
||||
}
|
||||
|
||||
static void doSaveState() {
|
||||
bool persist = (presetToSave < 251);
|
||||
|
||||
@ -238,7 +242,7 @@ void savePreset(byte index, const char* pname, JsonObject sObj)
|
||||
if (!sObj[FPSTR(bootPS)].isNull()) {
|
||||
bootPreset = sObj[FPSTR(bootPS)] | bootPreset;
|
||||
sObj.remove(FPSTR(bootPS));
|
||||
doSerializeConfig = true;
|
||||
configNeedsWrite = true;
|
||||
}
|
||||
|
||||
if (sObj.size()==0 || sObj["o"].isNull()) { // no "o" means not a playlist or custom API call, saving of state is async (not immediately)
|
||||
@ -269,7 +273,7 @@ void savePreset(byte index, const char* pname, JsonObject sObj)
|
||||
quickLoad = nullptr;
|
||||
} else {
|
||||
// store playlist
|
||||
// WARNING: playlist will be loaded in json.cpp after this call and will have repeat counter increased by 1
|
||||
// WARNING: playlist will be loaded in json.cpp after this call and will have repeat counter increased by 1 it will also be randomised if selected
|
||||
includeBri = true; // !sObj["on"].isNull();
|
||||
playlistSave = true;
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
for (size_t n = 0; n < WLED_MAX_WIFI_COUNT; n++) {
|
||||
char cs[4] = "CS"; cs[2] = 48+n; cs[3] = 0; //client SSID
|
||||
char pw[4] = "PW"; pw[2] = 48+n; pw[3] = 0; //client password
|
||||
char bs[4] = "BS"; bs[2] = 48+n; bs[3] = 0; //BSSID
|
||||
char ip[5] = "IP"; ip[2] = 48+n; ip[4] = 0; //IP address
|
||||
char gw[5] = "GW"; gw[2] = 48+n; gw[4] = 0; //GW address
|
||||
char sn[5] = "SN"; sn[2] = 48+n; sn[4] = 0; //subnet mask
|
||||
@ -39,6 +40,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
strlcpy(multiWiFi[n].clientPass, request->arg(pw).c_str(), 65);
|
||||
forceReconnect = true;
|
||||
}
|
||||
fillStr2MAC(multiWiFi[n].bssid, request->arg(bs).c_str());
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
ip[3] = 48+i;
|
||||
gw[3] = 48+i;
|
||||
@ -93,9 +95,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
strlwr(linked_remote); //Normalize MAC format to lowercase
|
||||
#endif
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
ethernetType = request->arg(F("ETH")).toInt();
|
||||
WLED::instance().initEthernet();
|
||||
initEthernet();
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -134,15 +136,17 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
strip.correctWB = request->hasArg(F("CCT"));
|
||||
strip.cctFromRgb = request->hasArg(F("CR"));
|
||||
cctICused = request->hasArg(F("IC"));
|
||||
uint8_t cctBlending = request->arg(F("CB")).toInt();
|
||||
Bus::setCCTBlend(cctBlending);
|
||||
Bus::setCCTBlend(request->arg(F("CB")).toInt());
|
||||
Bus::setGlobalAWMode(request->arg(F("AW")).toInt());
|
||||
strip.setTargetFps(request->arg(F("FR")).toInt());
|
||||
useGlobalLedBuffer = request->hasArg(F("LD"));
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
useParallelI2S = request->hasArg(F("PR"));
|
||||
#endif
|
||||
|
||||
bool busesChanged = false;
|
||||
for (int s = 0; s < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; s++) {
|
||||
int offset = s < 10 ? 48 : 55;
|
||||
for (int s = 0; s < 36; s++) { // theoretical limit is 36 : "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
int offset = s < 10 ? '0' : 'A' - 10;
|
||||
char lp[4] = "L0"; lp[2] = offset+s; lp[3] = 0; //ascii 0-9 //strip data pin
|
||||
char lc[4] = "LC"; lc[2] = offset+s; lc[3] = 0; //strip length
|
||||
char co[4] = "CO"; co[2] = offset+s; co[3] = 0; //strip color order
|
||||
@ -157,11 +161,11 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
char la[4] = "LA"; la[2] = offset+s; la[3] = 0; //LED mA
|
||||
char ma[4] = "MA"; ma[2] = offset+s; ma[3] = 0; //max mA
|
||||
if (!request->hasArg(lp)) {
|
||||
DEBUG_PRINTF_P(PSTR("No data for %d\n"), s);
|
||||
DEBUG_PRINTF_P(PSTR("# of buses: %d\n"), s+1);
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < 5; i++) {
|
||||
lp[1] = offset+i;
|
||||
lp[1] = '0'+i;
|
||||
if (!request->hasArg(lp)) break;
|
||||
pins[i] = (request->arg(lp).length() > 0) ? request->arg(lp).toInt() : 255;
|
||||
}
|
||||
@ -208,8 +212,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
type |= request->hasArg(rf) << 7; // off refresh override
|
||||
// actual finalization is done in WLED::loop() (removing old busses and adding new)
|
||||
// this may happen even before this loop is finished so we do "doInitBusses" after the loop
|
||||
if (busConfigs[s] != nullptr) delete busConfigs[s];
|
||||
busConfigs[s] = new(std::nothrow) BusConfig(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freq, useGlobalLedBuffer, maPerLed, maMax);
|
||||
busConfigs.emplace_back(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freq, useGlobalLedBuffer, maPerLed, maMax);
|
||||
busesChanged = true;
|
||||
}
|
||||
//doInitBusses = busesChanged; // we will do that below to ensure all input data is processed
|
||||
@ -217,7 +220,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
// we will not bother with pre-allocating ColorOrderMappings vector
|
||||
BusManager::getColorOrderMap().reset();
|
||||
for (int s = 0; s < WLED_MAX_COLOR_ORDER_MAPPINGS; s++) {
|
||||
int offset = s < 10 ? 48 : 55;
|
||||
int offset = s < 10 ? '0' : 'A' - 10;
|
||||
char xs[4] = "XS"; xs[2] = offset+s; xs[3] = 0; //start LED
|
||||
char xc[4] = "XC"; xc[2] = offset+s; xc[3] = 0; //strip length
|
||||
char xo[4] = "XO"; xo[2] = offset+s; xo[3] = 0; //color order
|
||||
@ -256,7 +259,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
disablePullUp = (bool)request->hasArg(F("IP"));
|
||||
touchThreshold = request->arg(F("TT")).toInt();
|
||||
for (int i = 0; i < WLED_MAX_BUTTONS; i++) {
|
||||
int offset = i < 10 ? 48 : 55;
|
||||
int offset = i < 10 ? '0' : 'A' - 10;
|
||||
char bt[4] = "BT"; bt[2] = offset+i; bt[3] = 0; // button pin (use A,B,C,... if WLED_MAX_BUTTONS>10)
|
||||
char be[4] = "BE"; be[2] = offset+i; be[3] = 0; // button type (use A,B,C,... if WLED_MAX_BUTTONS>10)
|
||||
int hw_btn_pin = request->arg(bt).toInt();
|
||||
@ -802,8 +805,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
|
||||
lastEditTime = millis();
|
||||
// do not save if factory reset or LED settings (which are saved after LED re-init)
|
||||
doSerializeConfig = subPage != SUBPAGE_LEDS && !(subPage == SUBPAGE_SEC && doReboot);
|
||||
if (subPage == SUBPAGE_UM) doReboot = request->hasArg(F("RBT")); // prevent race condition on dual core system (set reboot here, after doSerializeConfig has been set)
|
||||
configNeedsWrite = subPage != SUBPAGE_LEDS && !(subPage == SUBPAGE_SEC && doReboot);
|
||||
if (subPage == SUBPAGE_UM) doReboot = request->hasArg(F("RBT")); // prevent race condition on dual core system (set reboot here, after configNeedsWrite has been set)
|
||||
#ifndef WLED_DISABLE_ALEXA
|
||||
if (subPage == SUBPAGE_SYNC) alexaInit();
|
||||
#endif
|
||||
@ -1190,7 +1193,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
}
|
||||
// you can add more if you need
|
||||
|
||||
// global col[], effectCurrent, ... are updated in stateChanged()
|
||||
// global colPri[], effectCurrent, ... are updated in stateChanged()
|
||||
if (!apply) return true; // when called by JSON API, do not call colorUpdated() here
|
||||
|
||||
pos = req.indexOf(F("&NN")); //do not send UDP notifications this time
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "wled.h"
|
||||
/*
|
||||
* This v1 usermod file allows you to add own functionality to WLED more easily
|
||||
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
|
||||
* See: https://github.com/wled-dev/WLED/wiki/Add-own-functionality
|
||||
* EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in const.h)
|
||||
* If you just need 8 bytes, use 2551-2559 (you do not need to increase EEPSIZE)
|
||||
*
|
||||
|
185
wled00/util.cpp
185
wled00/util.cpp
@ -150,7 +150,7 @@ bool isAsterisksOnly(const char* str, byte maxLen)
|
||||
}
|
||||
|
||||
|
||||
//threading/network callback details: https://github.com/Aircoookie/WLED/pull/2336#discussion_r762276994
|
||||
//threading/network callback details: https://github.com/wled-dev/WLED/pull/2336#discussion_r762276994
|
||||
bool requestJSONBufferLock(uint8_t moduleID)
|
||||
{
|
||||
if (pDoc == nullptr) {
|
||||
@ -506,12 +506,12 @@ um_data_t* simulateSound(uint8_t simulationId)
|
||||
break;
|
||||
case UMS_10_13:
|
||||
for (int i = 0; i<16; i++)
|
||||
fftResult[i] = inoise8(beatsin8_t(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3);
|
||||
fftResult[i] = perlin8(beatsin8_t(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3);
|
||||
volumeSmth = fftResult[8];
|
||||
break;
|
||||
case UMS_14_3:
|
||||
for (int i = 0; i<16; i++)
|
||||
fftResult[i] = inoise8(beatsin8_t(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3);
|
||||
fftResult[i] = perlin8(beatsin8_t(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3);
|
||||
volumeSmth = fftResult[8];
|
||||
break;
|
||||
}
|
||||
@ -530,6 +530,8 @@ um_data_t* simulateSound(uint8_t simulationId)
|
||||
static const char s_ledmap_tmpl[] PROGMEM = "ledmap%d.json";
|
||||
// enumerate all ledmapX.json files on FS and extract ledmap names if existing
|
||||
void enumerateLedmaps() {
|
||||
StaticJsonDocument<64> filter;
|
||||
filter["n"] = true;
|
||||
ledMaps = 1;
|
||||
for (size_t i=1; i<WLED_MAX_LEDMAPS; i++) {
|
||||
char fileName[33] = "/";
|
||||
@ -548,7 +550,7 @@ void enumerateLedmaps() {
|
||||
|
||||
#ifndef ESP8266
|
||||
if (requestJSONBufferLock(21)) {
|
||||
if (readObjectFromFile(fileName, nullptr, pDoc)) {
|
||||
if (readObjectFromFile(fileName, nullptr, pDoc, &filter)) {
|
||||
size_t len = 0;
|
||||
JsonObject root = pDoc->as<JsonObject>();
|
||||
if (!root["n"].isNull()) {
|
||||
@ -616,3 +618,178 @@ int32_t hw_random(int32_t lowerlimit, int32_t upperlimit) {
|
||||
uint32_t diff = upperlimit - lowerlimit;
|
||||
return hw_random(diff) + lowerlimit;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fixed point integer based Perlin noise functions by @dedehai
|
||||
* Note: optimized for speed and to mimic fastled inoise functions, not for accuracy or best randomness
|
||||
*/
|
||||
#define PERLIN_SHIFT 1
|
||||
|
||||
// calculate gradient for corner from hash value
|
||||
static inline __attribute__((always_inline)) int32_t hashToGradient(uint32_t h) {
|
||||
// using more steps yields more "detailed" perlin noise but looks less like the original fastled version (adjust PERLIN_SHIFT to compensate, also changes range and needs proper adustment)
|
||||
// return (h & 0xFF) - 128; // use PERLIN_SHIFT 7
|
||||
// return (h & 0x0F) - 8; // use PERLIN_SHIFT 3
|
||||
// return (h & 0x07) - 4; // use PERLIN_SHIFT 2
|
||||
return (h & 0x03) - 2; // use PERLIN_SHIFT 1 -> closest to original fastled version
|
||||
}
|
||||
|
||||
// Gradient functions for 1D, 2D and 3D Perlin noise note: forcing inline produces smaller code and makes it 3x faster!
|
||||
static inline __attribute__((always_inline)) int32_t gradient1D(uint32_t x0, int32_t dx) {
|
||||
uint32_t h = x0 * 0x27D4EB2D;
|
||||
h ^= h >> 15;
|
||||
h *= 0x92C3412B;
|
||||
h ^= h >> 13;
|
||||
h ^= h >> 7;
|
||||
return (hashToGradient(h) * dx) >> PERLIN_SHIFT;
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline)) int32_t gradient2D(uint32_t x0, int32_t dx, uint32_t y0, int32_t dy) {
|
||||
uint32_t h = (x0 * 0x27D4EB2D) ^ (y0 * 0xB5297A4D);
|
||||
h ^= h >> 15;
|
||||
h *= 0x92C3412B;
|
||||
h ^= h >> 13;
|
||||
return (hashToGradient(h) * dx + hashToGradient(h>>PERLIN_SHIFT) * dy) >> (1 + PERLIN_SHIFT);
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline)) int32_t gradient3D(uint32_t x0, int32_t dx, uint32_t y0, int32_t dy, uint32_t z0, int32_t dz) {
|
||||
// fast and good entropy hash from corner coordinates
|
||||
uint32_t h = (x0 * 0x27D4EB2D) ^ (y0 * 0xB5297A4D) ^ (z0 * 0x1B56C4E9);
|
||||
h ^= h >> 15;
|
||||
h *= 0x92C3412B;
|
||||
h ^= h >> 13;
|
||||
return ((hashToGradient(h) * dx + hashToGradient(h>>(1+PERLIN_SHIFT)) * dy + hashToGradient(h>>(1 + 2*PERLIN_SHIFT)) * dz) * 85) >> (8 + PERLIN_SHIFT); // scale to 16bit, x*85 >> 8 = x/3
|
||||
}
|
||||
|
||||
// fast cubic smoothstep: t*(3 - 2t²), optimized for fixed point, scaled to avoid overflows
|
||||
static uint32_t smoothstep(const uint32_t t) {
|
||||
uint32_t t_squared = (t * t) >> 16;
|
||||
uint32_t factor = (3 << 16) - ((t << 1));
|
||||
return (t_squared * factor) >> 18; // scale to avoid overflows and give best resolution
|
||||
}
|
||||
|
||||
// simple linear interpolation for fixed-point values, scaled for perlin noise use
|
||||
static inline int32_t lerpPerlin(int32_t a, int32_t b, int32_t t) {
|
||||
return a + (((b - a) * t) >> 14); // match scaling with smoothstep to yield 16.16bit values
|
||||
}
|
||||
|
||||
// 1D Perlin noise function that returns a value in range of -24691 to 24689
|
||||
int32_t perlin1D_raw(uint32_t x, bool is16bit) {
|
||||
// integer and fractional part coordinates
|
||||
int32_t x0 = x >> 16;
|
||||
int32_t x1 = x0 + 1;
|
||||
if(is16bit) x1 = x1 & 0xFF; // wrap back to zero at 0xFF instead of 0xFFFF
|
||||
|
||||
int32_t dx0 = x & 0xFFFF;
|
||||
int32_t dx1 = dx0 - 0x10000;
|
||||
// gradient values for the two corners
|
||||
int32_t g0 = gradient1D(x0, dx0);
|
||||
int32_t g1 = gradient1D(x1, dx1);
|
||||
// interpolate and smooth function
|
||||
int32_t tx = smoothstep(dx0);
|
||||
int32_t noise = lerpPerlin(g0, g1, tx);
|
||||
return noise;
|
||||
}
|
||||
|
||||
// 2D Perlin noise function that returns a value in range of -20633 to 20629
|
||||
int32_t perlin2D_raw(uint32_t x, uint32_t y, bool is16bit) {
|
||||
int32_t x0 = x >> 16;
|
||||
int32_t y0 = y >> 16;
|
||||
int32_t x1 = x0 + 1;
|
||||
int32_t y1 = y0 + 1;
|
||||
|
||||
if(is16bit) {
|
||||
x1 = x1 & 0xFF; // wrap back to zero at 0xFF instead of 0xFFFF
|
||||
y1 = y1 & 0xFF;
|
||||
}
|
||||
|
||||
int32_t dx0 = x & 0xFFFF;
|
||||
int32_t dy0 = y & 0xFFFF;
|
||||
int32_t dx1 = dx0 - 0x10000;
|
||||
int32_t dy1 = dy0 - 0x10000;
|
||||
|
||||
int32_t g00 = gradient2D(x0, dx0, y0, dy0);
|
||||
int32_t g10 = gradient2D(x1, dx1, y0, dy0);
|
||||
int32_t g01 = gradient2D(x0, dx0, y1, dy1);
|
||||
int32_t g11 = gradient2D(x1, dx1, y1, dy1);
|
||||
|
||||
uint32_t tx = smoothstep(dx0);
|
||||
uint32_t ty = smoothstep(dy0);
|
||||
|
||||
int32_t nx0 = lerpPerlin(g00, g10, tx);
|
||||
int32_t nx1 = lerpPerlin(g01, g11, tx);
|
||||
|
||||
int32_t noise = lerpPerlin(nx0, nx1, ty);
|
||||
return noise;
|
||||
}
|
||||
|
||||
// 3D Perlin noise function that returns a value in range of -16788 to 16381
|
||||
int32_t perlin3D_raw(uint32_t x, uint32_t y, uint32_t z, bool is16bit) {
|
||||
int32_t x0 = x >> 16;
|
||||
int32_t y0 = y >> 16;
|
||||
int32_t z0 = z >> 16;
|
||||
int32_t x1 = x0 + 1;
|
||||
int32_t y1 = y0 + 1;
|
||||
int32_t z1 = z0 + 1;
|
||||
|
||||
if(is16bit) {
|
||||
x1 = x1 & 0xFF; // wrap back to zero at 0xFF instead of 0xFFFF
|
||||
y1 = y1 & 0xFF;
|
||||
z1 = z1 & 0xFF;
|
||||
}
|
||||
|
||||
int32_t dx0 = x & 0xFFFF;
|
||||
int32_t dy0 = y & 0xFFFF;
|
||||
int32_t dz0 = z & 0xFFFF;
|
||||
int32_t dx1 = dx0 - 0x10000;
|
||||
int32_t dy1 = dy0 - 0x10000;
|
||||
int32_t dz1 = dz0 - 0x10000;
|
||||
|
||||
int32_t g000 = gradient3D(x0, dx0, y0, dy0, z0, dz0);
|
||||
int32_t g001 = gradient3D(x0, dx0, y0, dy0, z1, dz1);
|
||||
int32_t g010 = gradient3D(x0, dx0, y1, dy1, z0, dz0);
|
||||
int32_t g011 = gradient3D(x0, dx0, y1, dy1, z1, dz1);
|
||||
int32_t g100 = gradient3D(x1, dx1, y0, dy0, z0, dz0);
|
||||
int32_t g101 = gradient3D(x1, dx1, y0, dy0, z1, dz1);
|
||||
int32_t g110 = gradient3D(x1, dx1, y1, dy1, z0, dz0);
|
||||
int32_t g111 = gradient3D(x1, dx1, y1, dy1, z1, dz1);
|
||||
|
||||
uint32_t tx = smoothstep(dx0);
|
||||
uint32_t ty = smoothstep(dy0);
|
||||
uint32_t tz = smoothstep(dz0);
|
||||
|
||||
int32_t nx0 = lerpPerlin(g000, g100, tx);
|
||||
int32_t nx1 = lerpPerlin(g010, g110, tx);
|
||||
int32_t nx2 = lerpPerlin(g001, g101, tx);
|
||||
int32_t nx3 = lerpPerlin(g011, g111, tx);
|
||||
int32_t ny0 = lerpPerlin(nx0, nx1, ty);
|
||||
int32_t ny1 = lerpPerlin(nx2, nx3, ty);
|
||||
|
||||
int32_t noise = lerpPerlin(ny0, ny1, tz);
|
||||
return noise;
|
||||
}
|
||||
|
||||
// scaling functions for fastled replacement
|
||||
uint16_t perlin16(uint32_t x) {
|
||||
return ((perlin1D_raw(x) * 1159) >> 10) + 32803; //scale to 16bit and offset (fastled range: about 4838 to 60766)
|
||||
}
|
||||
|
||||
uint16_t perlin16(uint32_t x, uint32_t y) {
|
||||
return ((perlin2D_raw(x, y) * 1537) >> 10) + 32725; //scale to 16bit and offset (fastled range: about 1748 to 63697)
|
||||
}
|
||||
|
||||
uint16_t perlin16(uint32_t x, uint32_t y, uint32_t z) {
|
||||
return ((perlin3D_raw(x, y, z) * 1731) >> 10) + 33147; //scale to 16bit and offset (fastled range: about 4766 to 60840)
|
||||
}
|
||||
|
||||
uint8_t perlin8(uint16_t x) {
|
||||
return (((perlin1D_raw((uint32_t)x << 8, true) * 1353) >> 10) + 32769) >> 8; //scale to 16 bit, offset, then scale to 8bit
|
||||
}
|
||||
|
||||
uint8_t perlin8(uint16_t x, uint16_t y) {
|
||||
return (((perlin2D_raw((uint32_t)x << 8, (uint32_t)y << 8, true) * 1620) >> 10) + 32771) >> 8; //scale to 16 bit, offset, then scale to 8bit
|
||||
}
|
||||
|
||||
uint8_t perlin8(uint16_t x, uint16_t y, uint16_t z) {
|
||||
return (((perlin3D_raw((uint32_t)x << 8, (uint32_t)y << 8, (uint32_t)z << 8, true) * 2015) >> 10) + 33168) >> 8; //scale to 16 bit, offset, then scale to 8bit
|
||||
}
|
201
wled00/wled.cpp
201
wled00/wled.cpp
@ -109,7 +109,6 @@ void WLED::loop()
|
||||
if (WLED_CONNECTED && aOtaEnabled && !otaLock && correctPIN) ArduinoOTA.handle();
|
||||
#endif
|
||||
handleNightlight();
|
||||
handlePlaylist();
|
||||
yield();
|
||||
|
||||
#ifndef WLED_DISABLE_HUESYNC
|
||||
@ -117,6 +116,10 @@ void WLED::loop()
|
||||
yield();
|
||||
#endif
|
||||
|
||||
if (!presetNeedsSaving()) {
|
||||
handlePlaylist();
|
||||
yield();
|
||||
}
|
||||
handlePresets();
|
||||
yield();
|
||||
|
||||
@ -185,58 +188,19 @@ void WLED::loop()
|
||||
DEBUG_PRINTLN(F("Re-init busses."));
|
||||
bool aligned = strip.checkSegmentAlignment(); //see if old segments match old bus(ses)
|
||||
BusManager::removeAll();
|
||||
unsigned mem = 0;
|
||||
// determine if it is sensible to use parallel I2S outputs on ESP32 (i.e. more than 5 outputs = 1 I2S + 4 RMT)
|
||||
bool useParallel = false;
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH_ESP32S2) && !defined(ARDUINO_ARCH_ESP32S3) && !defined(ARDUINO_ARCH_ESP32C3)
|
||||
unsigned digitalCount = 0;
|
||||
unsigned maxLedsOnBus = 0;
|
||||
unsigned maxChannels = 0;
|
||||
for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
|
||||
if (busConfigs[i] == nullptr) break;
|
||||
if (!Bus::isDigital(busConfigs[i]->type)) continue;
|
||||
if (!Bus::is2Pin(busConfigs[i]->type)) {
|
||||
digitalCount++;
|
||||
unsigned channels = Bus::getNumberOfChannels(busConfigs[i]->type);
|
||||
if (busConfigs[i]->count > maxLedsOnBus) maxLedsOnBus = busConfigs[i]->count;
|
||||
if (channels > maxChannels) maxChannels = channels;
|
||||
}
|
||||
}
|
||||
DEBUG_PRINTF_P(PSTR("Maximum LEDs on a bus: %u\nDigital buses: %u\n"), maxLedsOnBus, digitalCount);
|
||||
// we may remove 300 LEDs per bus limit when NeoPixelBus is updated beyond 2.9.0
|
||||
if (maxLedsOnBus <= 300 && digitalCount > 5) {
|
||||
DEBUG_PRINTF_P(PSTR("Switching to parallel I2S."));
|
||||
useParallel = true;
|
||||
BusManager::useParallelOutput();
|
||||
mem = BusManager::memUsage(maxChannels, maxLedsOnBus, 8); // use alternate memory calculation (hse to be used *after* useParallelOutput())
|
||||
}
|
||||
#endif
|
||||
// create buses/outputs
|
||||
for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
|
||||
if (busConfigs[i] == nullptr || (!useParallel && i > 10)) break;
|
||||
if (useParallel && i < 8) {
|
||||
// if for some unexplained reason the above pre-calculation was wrong, update
|
||||
unsigned memT = BusManager::memUsage(*busConfigs[i]); // includes x8 memory allocation for parallel I2S
|
||||
if (memT > mem) mem = memT; // if we have unequal LED count use the largest
|
||||
} else
|
||||
mem += BusManager::memUsage(*busConfigs[i]); // includes global buffer
|
||||
if (mem <= MAX_LED_MEMORY) BusManager::add(*busConfigs[i]);
|
||||
delete busConfigs[i];
|
||||
busConfigs[i] = nullptr;
|
||||
}
|
||||
strip.finalizeInit(); // also loads default ledmap if present
|
||||
strip.finalizeInit(); // will create buses and also load default ledmap if present
|
||||
BusManager::setBrightness(bri); // fix re-initialised bus' brightness #4005
|
||||
if (aligned) strip.makeAutoSegments();
|
||||
else strip.fixInvalidSegments();
|
||||
BusManager::setBrightness(bri); // fix re-initialised bus' brightness
|
||||
doSerializeConfig = true;
|
||||
configNeedsWrite = true;
|
||||
}
|
||||
if (loadLedmap >= 0) {
|
||||
strip.deserializeMap(loadLedmap);
|
||||
loadLedmap = -1;
|
||||
}
|
||||
yield();
|
||||
if (doSerializeConfig) serializeConfig();
|
||||
if (configNeedsWrite) serializeConfigToFS();
|
||||
|
||||
yield();
|
||||
handleWs();
|
||||
@ -259,7 +223,7 @@ void WLED::loop()
|
||||
}
|
||||
#endif
|
||||
|
||||
if (doReboot && (!doInitBusses || !doSerializeConfig)) // if busses have to be inited & saved, wait until next iteration
|
||||
if (doReboot && (!doInitBusses || !configNeedsWrite)) // if busses have to be inited & saved, wait until next iteration
|
||||
reset();
|
||||
|
||||
// DEBUG serial logging (every 30s)
|
||||
@ -378,7 +342,6 @@ void WLED::setup()
|
||||
#else
|
||||
DEBUG_PRINTLN(F("arduino-esp32 v1.0.x\n")); // we can't say in more detail.
|
||||
#endif
|
||||
|
||||
DEBUG_PRINTF_P(PSTR("CPU: %s rev.%d, %d core(s), %d MHz.\n"), ESP.getChipModel(), (int)ESP.getChipRevision(), ESP.getChipCores(), ESP.getCpuFreqMHz());
|
||||
DEBUG_PRINTF_P(PSTR("FLASH: %d MB, Mode %d "), (ESP.getFlashChipSize()/1024)/1024, (int)ESP.getFlashChipMode());
|
||||
#ifdef WLED_DEBUG
|
||||
@ -500,7 +463,7 @@ void WLED::setup()
|
||||
#endif
|
||||
|
||||
// fill in unique mdns default
|
||||
if (strcmp(cmDNS, "x") == 0) sprintf_P(cmDNS, PSTR("wled-%*s"), 6, escapedMac.c_str() + 6);
|
||||
if (strcmp(cmDNS, DEFAULT_MDNS_NAME) == 0) sprintf_P(cmDNS, PSTR("wled-%*s"), 6, escapedMac.c_str() + 6);
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
if (mqttDeviceTopic[0] == 0) sprintf_P(mqttDeviceTopic, PSTR("wled/%*s"), 6, escapedMac.c_str() + 6);
|
||||
if (mqttClientID[0] == 0) sprintf_P(mqttClientID, PSTR("WLED-%*s"), 6, escapedMac.c_str() + 6);
|
||||
@ -570,6 +533,7 @@ void WLED::beginStrip()
|
||||
strip.makeAutoSegments();
|
||||
strip.setBrightness(0);
|
||||
strip.setShowCallback(handleOverlayDraw);
|
||||
doInitBusses = false;
|
||||
|
||||
if (turnOnAtBoot) {
|
||||
if (briS > 0) bri = briS;
|
||||
@ -582,16 +546,16 @@ void WLED::beginStrip()
|
||||
Segment &seg = strip.getSegment(i);
|
||||
if (seg.isActive()) seg.colors[0] = BLACK;
|
||||
}
|
||||
col[0] = col[1] = col[2] = col[3] = 0; // needed for colorUpdated()
|
||||
colPri[0] = colPri[1] = colPri[2] = colPri[3] = 0; // needed for colorUpdated()
|
||||
}
|
||||
briLast = briS; bri = 0;
|
||||
strip.fill(BLACK);
|
||||
strip.show();
|
||||
}
|
||||
colorUpdated(CALL_MODE_INIT); // will not send notification but will initiate transition
|
||||
if (bootPreset > 0) {
|
||||
applyPreset(bootPreset, CALL_MODE_INIT);
|
||||
}
|
||||
colorUpdated(CALL_MODE_INIT); // will not send notification
|
||||
|
||||
// init relay pin
|
||||
if (rlyPin >= 0) {
|
||||
@ -639,146 +603,6 @@ void WLED::initAP(bool resetAP)
|
||||
apActive = true;
|
||||
}
|
||||
|
||||
bool WLED::initEthernet()
|
||||
{
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
|
||||
static bool successfullyConfiguredEthernet = false;
|
||||
|
||||
if (successfullyConfiguredEthernet) {
|
||||
// DEBUG_PRINTLN(F("initE: ETH already successfully configured, ignoring"));
|
||||
return false;
|
||||
}
|
||||
if (ethernetType == WLED_ETH_NONE) {
|
||||
return false;
|
||||
}
|
||||
if (ethernetType >= WLED_NUM_ETH_TYPES) {
|
||||
DEBUG_PRINTF_P(PSTR("initE: Ignoring attempt for invalid ethernetType (%d)\n"), ethernetType);
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_PRINTF_P(PSTR("initE: Attempting ETH config: %d\n"), ethernetType);
|
||||
|
||||
// Ethernet initialization should only succeed once -- else reboot required
|
||||
ethernet_settings es = ethernetBoards[ethernetType];
|
||||
managed_pin_type pinsToAllocate[10] = {
|
||||
// first six pins are non-configurable
|
||||
esp32_nonconfigurable_ethernet_pins[0],
|
||||
esp32_nonconfigurable_ethernet_pins[1],
|
||||
esp32_nonconfigurable_ethernet_pins[2],
|
||||
esp32_nonconfigurable_ethernet_pins[3],
|
||||
esp32_nonconfigurable_ethernet_pins[4],
|
||||
esp32_nonconfigurable_ethernet_pins[5],
|
||||
{ (int8_t)es.eth_mdc, true }, // [6] = MDC is output and mandatory
|
||||
{ (int8_t)es.eth_mdio, true }, // [7] = MDIO is bidirectional and mandatory
|
||||
{ (int8_t)es.eth_power, true }, // [8] = optional pin, not all boards use
|
||||
{ ((int8_t)0xFE), false }, // [9] = replaced with eth_clk_mode, mandatory
|
||||
};
|
||||
// update the clock pin....
|
||||
if (es.eth_clk_mode == ETH_CLOCK_GPIO0_IN) {
|
||||
pinsToAllocate[9].pin = 0;
|
||||
pinsToAllocate[9].isOutput = false;
|
||||
} else if (es.eth_clk_mode == ETH_CLOCK_GPIO0_OUT) {
|
||||
pinsToAllocate[9].pin = 0;
|
||||
pinsToAllocate[9].isOutput = true;
|
||||
} else if (es.eth_clk_mode == ETH_CLOCK_GPIO16_OUT) {
|
||||
pinsToAllocate[9].pin = 16;
|
||||
pinsToAllocate[9].isOutput = true;
|
||||
} else if (es.eth_clk_mode == ETH_CLOCK_GPIO17_OUT) {
|
||||
pinsToAllocate[9].pin = 17;
|
||||
pinsToAllocate[9].isOutput = true;
|
||||
} else {
|
||||
DEBUG_PRINTF_P(PSTR("initE: Failing due to invalid eth_clk_mode (%d)\n"), es.eth_clk_mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!PinManager::allocateMultiplePins(pinsToAllocate, 10, PinOwner::Ethernet)) {
|
||||
DEBUG_PRINTLN(F("initE: Failed to allocate ethernet pins"));
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
For LAN8720 the most correct way is to perform clean reset each time before init
|
||||
applying LOW to power or nRST pin for at least 100 us (please refer to datasheet, page 59)
|
||||
ESP_IDF > V4 implements it (150 us, lan87xx_reset_hw(esp_eth_phy_t *phy) function in
|
||||
/components/esp_eth/src/esp_eth_phy_lan87xx.c, line 280)
|
||||
but ESP_IDF < V4 does not. Lets do it:
|
||||
[not always needed, might be relevant in some EMI situations at startup and for hot resets]
|
||||
*/
|
||||
#if ESP_IDF_VERSION_MAJOR==3
|
||||
if(es.eth_power>0 && es.eth_type==ETH_PHY_LAN8720) {
|
||||
pinMode(es.eth_power, OUTPUT);
|
||||
digitalWrite(es.eth_power, 0);
|
||||
delayMicroseconds(150);
|
||||
digitalWrite(es.eth_power, 1);
|
||||
delayMicroseconds(10);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!ETH.begin(
|
||||
(uint8_t) es.eth_address,
|
||||
(int) es.eth_power,
|
||||
(int) es.eth_mdc,
|
||||
(int) es.eth_mdio,
|
||||
(eth_phy_type_t) es.eth_type,
|
||||
(eth_clock_mode_t) es.eth_clk_mode
|
||||
)) {
|
||||
DEBUG_PRINTLN(F("initC: ETH.begin() failed"));
|
||||
// de-allocate the allocated pins
|
||||
for (managed_pin_type mpt : pinsToAllocate) {
|
||||
PinManager::deallocatePin(mpt.pin, PinOwner::Ethernet);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
successfullyConfiguredEthernet = true;
|
||||
DEBUG_PRINTLN(F("initC: *** Ethernet successfully configured! ***"));
|
||||
return true;
|
||||
#else
|
||||
return false; // Ethernet not enabled for build
|
||||
#endif
|
||||
}
|
||||
|
||||
// performs asynchronous scan for available networks (which may take couple of seconds to finish)
|
||||
// returns configured WiFi ID with the strongest signal (or default if no configured networks available)
|
||||
int8_t WLED::findWiFi(bool doScan) {
|
||||
if (multiWiFi.size() <= 1) {
|
||||
DEBUG_PRINTLN(F("Defaulf WiFi used."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (doScan) WiFi.scanDelete(); // restart scan
|
||||
|
||||
int status = WiFi.scanComplete(); // complete scan may take as much as several seconds (usually <3s with not very crowded air)
|
||||
|
||||
if (status == WIFI_SCAN_FAILED) {
|
||||
DEBUG_PRINTLN(F("WiFi scan started."));
|
||||
WiFi.scanNetworks(true); // start scanning in asynchronous mode
|
||||
} else if (status >= 0) { // status contains number of found networks
|
||||
DEBUG_PRINT(F("WiFi scan completed: ")); DEBUG_PRINTLN(status);
|
||||
int rssi = -9999;
|
||||
unsigned selected = selectedWiFi;
|
||||
for (int o = 0; o < status; o++) {
|
||||
DEBUG_PRINT(F(" WiFi available: ")); DEBUG_PRINT(WiFi.SSID(o));
|
||||
DEBUG_PRINT(F(" RSSI: ")); DEBUG_PRINT(WiFi.RSSI(o)); DEBUG_PRINTLN(F("dB"));
|
||||
for (unsigned n = 0; n < multiWiFi.size(); n++)
|
||||
if (!strcmp(WiFi.SSID(o).c_str(), multiWiFi[n].clientSSID)) {
|
||||
// find the WiFi with the strongest signal (but keep priority of entry if signal difference is not big)
|
||||
if ((n < selected && WiFi.RSSI(o) > rssi-10) || WiFi.RSSI(o) > rssi) {
|
||||
rssi = WiFi.RSSI(o);
|
||||
selected = n;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
DEBUG_PRINT(F("Selected: ")); DEBUG_PRINT(multiWiFi[selected].clientSSID);
|
||||
DEBUG_PRINT(F(" RSSI: ")); DEBUG_PRINT(rssi); DEBUG_PRINTLN(F("dB"));
|
||||
return selected;
|
||||
}
|
||||
//DEBUG_PRINT(F("WiFi scan running."));
|
||||
return status; // scan is still running or there was an error
|
||||
}
|
||||
|
||||
void WLED::initConnection()
|
||||
{
|
||||
DEBUG_PRINTF_P(PSTR("initConnection() called @ %lus.\n"), millis()/1000);
|
||||
@ -795,6 +619,7 @@ void WLED::initConnection()
|
||||
#endif
|
||||
|
||||
WiFi.disconnect(true); // close old connections
|
||||
delay(5); // wait for hardware to be ready
|
||||
#ifdef ESP8266
|
||||
WiFi.setPhyMode(force802_3g ? WIFI_PHY_MODE_11G : WIFI_PHY_MODE_11N);
|
||||
#endif
|
||||
|
@ -15,7 +15,7 @@
|
||||
// ESP8266-01 (blue) got too little storage space to work with WLED. 0.10.2 is the last release supporting this unit.
|
||||
|
||||
// ESP8266-01 (black) has 1MB flash and can thus fit the whole program, although OTA update is not possible. Use 1M(128K SPIFFS).
|
||||
// 2-step OTA may still be possible: https://github.com/Aircoookie/WLED/issues/2040#issuecomment-981111096
|
||||
// 2-step OTA may still be possible: https://github.com/wled-dev/WLED/issues/2040#issuecomment-981111096
|
||||
// Uncomment some of the following lines to disable features:
|
||||
// Alternatively, with platformio pass your chosen flags to your custom build target in platformio_override.ini
|
||||
|
||||
@ -217,6 +217,10 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument<PSRAM_Allocator>;
|
||||
#define WLED_AP_PASS DEFAULT_AP_PASS
|
||||
#endif
|
||||
|
||||
#ifndef WLED_PIN
|
||||
#define WLED_PIN ""
|
||||
#endif
|
||||
|
||||
#ifndef SPIFFS_EDITOR_AIRCOOOKIE
|
||||
#error You are not using the Aircoookie fork of the ESPAsyncWebserver library.\
|
||||
Using upstream puts your WiFi password at risk of being served by the filesystem.\
|
||||
@ -277,7 +281,11 @@ WLED_GLOBAL char releaseString[] _INIT(WLED_RELEASE_NAME); // must include the q
|
||||
|
||||
// AP and OTA default passwords (for maximum security change them!)
|
||||
WLED_GLOBAL char apPass[65] _INIT(WLED_AP_PASS);
|
||||
#ifdef WLED_OTA_PASS
|
||||
WLED_GLOBAL char otaPass[33] _INIT(WLED_OTA_PASS);
|
||||
#else
|
||||
WLED_GLOBAL char otaPass[33] _INIT(DEFAULT_OTA_PASS);
|
||||
#endif
|
||||
|
||||
// Hardware and pin config
|
||||
#ifndef BTNPIN
|
||||
@ -359,7 +367,7 @@ WLED_GLOBAL wifi_options_t wifiOpt _INIT_N(({0, 1, false, AP_BEHAVIOR_BOOT_NO_CO
|
||||
#define noWifiSleep wifiOpt.noWifiSleep
|
||||
#define force802_3g wifiOpt.force802_3g
|
||||
#else
|
||||
WLED_GLOBAL uint8_t selectedWiFi _INIT(0);
|
||||
WLED_GLOBAL int8_t selectedWiFi _INIT(0);
|
||||
WLED_GLOBAL byte apChannel _INIT(1); // 2.4GHz WiFi AP channel (1-13)
|
||||
WLED_GLOBAL byte apHide _INIT(0); // hidden AP SSID
|
||||
WLED_GLOBAL byte apBehavior _INIT(AP_BEHAVIOR_BOOT_NO_CONN); // access point opens when no connection after boot by default
|
||||
@ -371,15 +379,15 @@ WLED_GLOBAL bool noWifiSleep _INIT(false);
|
||||
WLED_GLOBAL bool force802_3g _INIT(false);
|
||||
#endif // WLED_SAVE_RAM
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#if defined(LOLIN_WIFI_FIX) && (defined(ARDUINO_ARCH_ESP32C3) || defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32S3))
|
||||
#if defined(LOLIN_WIFI_FIX) && (defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3))
|
||||
WLED_GLOBAL uint8_t txPower _INIT(WIFI_POWER_8_5dBm);
|
||||
#else
|
||||
WLED_GLOBAL uint8_t txPower _INIT(WIFI_POWER_19_5dBm);
|
||||
#endif
|
||||
#endif
|
||||
#define WLED_WIFI_CONFIGURED (strlen(multiWiFi[0].clientSSID) >= 1 && strcmp(multiWiFi[0].clientSSID, DEFAULT_CLIENT_SSID) != 0)
|
||||
#define WLED_WIFI_CONFIGURED isWiFiConfigured()
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
#ifdef WLED_ETH_DEFAULT // default ethernet board type if specified
|
||||
WLED_GLOBAL int ethernetType _INIT(WLED_ETH_DEFAULT); // ethernet board type
|
||||
#else
|
||||
@ -398,6 +406,9 @@ WLED_GLOBAL byte bootPreset _INIT(0); // save preset to load
|
||||
WLED_GLOBAL bool useGlobalLedBuffer _INIT(false); // double buffering disabled on ESP8266
|
||||
#else
|
||||
WLED_GLOBAL bool useGlobalLedBuffer _INIT(true); // double buffering enabled on ESP32
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
WLED_GLOBAL bool useParallelI2S _INIT(false); // parallel I2S for ESP32
|
||||
#endif
|
||||
#endif
|
||||
#ifdef WLED_USE_IC_CCT
|
||||
WLED_GLOBAL bool cctICused _INIT(true); // CCT IC used (Athom 15W bulbs)
|
||||
@ -408,7 +419,7 @@ WLED_GLOBAL bool gammaCorrectCol _INIT(true); // use gamma correction on col
|
||||
WLED_GLOBAL bool gammaCorrectBri _INIT(false); // use gamma correction on brightness
|
||||
WLED_GLOBAL float gammaCorrectVal _INIT(2.8f); // gamma correction value
|
||||
|
||||
WLED_GLOBAL byte col[] _INIT_N(({ 255, 160, 0, 0 })); // current RGB(W) primary color. col[] should be updated if you want to change the color.
|
||||
WLED_GLOBAL byte colPri[] _INIT_N(({ 255, 160, 0, 0 })); // current RGB(W) primary color. colPri[] should be updated if you want to change the color.
|
||||
WLED_GLOBAL byte colSec[] _INIT_N(({ 0, 0, 0, 0 })); // current RGB(W) secondary color
|
||||
|
||||
WLED_GLOBAL byte nightlightTargetBri _INIT(0); // brightness after nightlight is over
|
||||
@ -547,7 +558,7 @@ WLED_GLOBAL byte currentTimezone _INIT(WLED_TIMEZONE); // Timezone ID. Refer
|
||||
WLED_GLOBAL int utcOffsetSecs _INIT(WLED_UTC_OFFSET); // Seconds to offset from UTC before timzone calculation
|
||||
|
||||
WLED_GLOBAL byte overlayCurrent _INIT(0); // 0: no overlay 1: analog clock 2: was single-digit clock 3: was cronixie
|
||||
WLED_GLOBAL byte overlayMin _INIT(0), overlayMax _INIT(DEFAULT_LED_COUNT - 1); // boundaries of overlay mode
|
||||
WLED_GLOBAL uint16_t overlayMin _INIT(0), overlayMax _INIT(DEFAULT_LED_COUNT - 1); // boundaries of overlay mode
|
||||
|
||||
WLED_GLOBAL byte analogClock12pixel _INIT(0); // The pixel in your strip where "midnight" would be
|
||||
WLED_GLOBAL bool analogClockSecondsTrail _INIT(false); // Display seconds as trail of LEDs instead of a single pixel
|
||||
@ -567,11 +578,15 @@ WLED_GLOBAL byte macroLongPress[WLED_MAX_BUTTONS] _INIT({0});
|
||||
WLED_GLOBAL byte macroDoublePress[WLED_MAX_BUTTONS] _INIT({0});
|
||||
|
||||
// Security CONFIG
|
||||
#ifdef WLED_OTA_PASS
|
||||
WLED_GLOBAL bool otaLock _INIT(true); // prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks
|
||||
#else
|
||||
WLED_GLOBAL bool otaLock _INIT(false); // prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks
|
||||
#endif
|
||||
WLED_GLOBAL bool wifiLock _INIT(false); // prevents access to WiFi settings when OTA lock is enabled
|
||||
WLED_GLOBAL bool aOtaEnabled _INIT(true); // ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on
|
||||
WLED_GLOBAL char settingsPIN[5] _INIT(""); // PIN for settings pages
|
||||
WLED_GLOBAL bool correctPIN _INIT(true);
|
||||
WLED_GLOBAL char settingsPIN[5] _INIT(WLED_PIN); // PIN for settings pages
|
||||
WLED_GLOBAL bool correctPIN _INIT(!strlen(settingsPIN));
|
||||
WLED_GLOBAL unsigned long lastEditTime _INIT(0);
|
||||
|
||||
WLED_GLOBAL uint16_t userVar0 _INIT(0), userVar1 _INIT(0); //available for use in usermod
|
||||
@ -579,6 +594,7 @@ WLED_GLOBAL uint16_t userVar0 _INIT(0), userVar1 _INIT(0); //available for use i
|
||||
// internal global variable declarations
|
||||
// wifi
|
||||
WLED_GLOBAL bool apActive _INIT(false);
|
||||
WLED_GLOBAL byte apClients _INIT(0);
|
||||
WLED_GLOBAL bool forceReconnect _INIT(false);
|
||||
WLED_GLOBAL unsigned long lastReconnectAttempt _INIT(0);
|
||||
WLED_GLOBAL bool interfacesInited _INIT(false);
|
||||
@ -861,7 +877,7 @@ WLED_GLOBAL byte errorFlag _INIT(0);
|
||||
WLED_GLOBAL String messageHead, messageSub;
|
||||
WLED_GLOBAL byte optionType;
|
||||
|
||||
WLED_GLOBAL bool doSerializeConfig _INIT(false); // flag to initiate saving of config
|
||||
WLED_GLOBAL bool configNeedsWrite _INIT(false); // flag to initiate saving of config
|
||||
WLED_GLOBAL bool doReboot _INIT(false); // flag to initiate reboot from async handlers
|
||||
|
||||
WLED_GLOBAL bool psramSafe _INIT(true); // is it safe to use PSRAM (on ESP32 rev.1; compiler fix used "-mfix-esp32-psram-cache-issue")
|
||||
@ -891,9 +907,8 @@ WLED_GLOBAL ESPAsyncE131 ddp _INIT_N(((handleE131Packet)));
|
||||
WLED_GLOBAL bool e131NewData _INIT(false);
|
||||
|
||||
// led fx library object
|
||||
WLED_GLOBAL BusManager busses _INIT(BusManager());
|
||||
WLED_GLOBAL WS2812FX strip _INIT(WS2812FX());
|
||||
WLED_GLOBAL BusConfig* busConfigs[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES] _INIT({nullptr}); //temporary, to remember values from network callback until after
|
||||
WLED_GLOBAL std::vector<BusConfig> busConfigs; //temporary, to remember values from network callback until after
|
||||
WLED_GLOBAL bool doInitBusses _INIT(false);
|
||||
WLED_GLOBAL int8_t loadLedmap _INIT(-1);
|
||||
WLED_GLOBAL uint8_t currentLedmap _INIT(0);
|
||||
@ -1046,11 +1061,9 @@ public:
|
||||
|
||||
void beginStrip();
|
||||
void handleConnection();
|
||||
bool initEthernet(); // result is informational
|
||||
void initAP(bool resetAP = false);
|
||||
void initConnection();
|
||||
void initInterfaces();
|
||||
int8_t findWiFi(bool doScan = false);
|
||||
#if defined(STATUSLED)
|
||||
void handleStatusLED();
|
||||
#endif
|
||||
|
@ -11,7 +11,7 @@
|
||||
* Only used to restore config from pre-0.11 installations using the deEEP() methods
|
||||
*
|
||||
* Methods to handle saving and loading to non-volatile memory
|
||||
* EEPROM Map: https://github.com/Aircoookie/WLED/wiki/EEPROM-Map
|
||||
* EEPROM Map: https://github.com/wled-dev/WLED/wiki/EEPROM-Map
|
||||
*/
|
||||
|
||||
//eeprom Version code, enables default settings instead of 0 init on update
|
||||
|
@ -328,7 +328,7 @@ void initServer()
|
||||
interfaceUpdateCallMode = CALL_MODE_WS_SEND; // schedule WS update
|
||||
serveJson(request); return; //if JSON contains "v"
|
||||
} else {
|
||||
doSerializeConfig = true; //serializeConfig(); //Save new settings to FS
|
||||
configNeedsWrite = true; //Save new settings to FS
|
||||
}
|
||||
}
|
||||
request->send(200, CONTENT_TYPE_JSON, F("{\"success\":true}"));
|
||||
@ -567,13 +567,14 @@ void serveSettings(AsyncWebServerRequest* request, bool post) {
|
||||
//else if (url.indexOf("/edit") >= 0) subPage = 10;
|
||||
else subPage = SUBPAGE_WELCOME;
|
||||
|
||||
if (!correctPIN && strlen(settingsPIN) > 0 && (subPage > 0 && subPage < 11)) {
|
||||
bool pinRequired = !correctPIN && strlen(settingsPIN) > 0 && (subPage > (WLED_WIFI_CONFIGURED ? SUBPAGE_MENU : SUBPAGE_WIFI) && subPage < SUBPAGE_LOCK);
|
||||
if (pinRequired) {
|
||||
originalSubPage = subPage;
|
||||
subPage = SUBPAGE_PINREQ; // require PIN
|
||||
}
|
||||
|
||||
// if OTA locked or too frequent PIN entry requests fail hard
|
||||
if ((subPage == SUBPAGE_WIFI && wifiLock && otaLock) || (post && !correctPIN && millis()-lastEditTime < PIN_RETRY_COOLDOWN))
|
||||
if ((subPage == SUBPAGE_WIFI && wifiLock && otaLock) || (post && pinRequired && millis()-lastEditTime < PIN_RETRY_COOLDOWN))
|
||||
{
|
||||
serveMessage(request, 401, FPSTR(s_accessdenied), FPSTR(s_unlock_ota), 254); return;
|
||||
}
|
||||
@ -609,7 +610,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post) {
|
||||
if (!s2[0]) strcpy_P(s2, s_redirecting);
|
||||
|
||||
bool redirectAfter9s = (subPage == SUBPAGE_WIFI || ((subPage == SUBPAGE_SEC || subPage == SUBPAGE_UM) && doReboot));
|
||||
serveMessage(request, (correctPIN ? 200 : 401), s, s2, redirectAfter9s ? 129 : (correctPIN ? 1 : 3));
|
||||
serveMessage(request, (!pinRequired ? 200 : 401), s, s2, redirectAfter9s ? 129 : (!pinRequired ? 1 : 3));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ void XML_response(Print& dest)
|
||||
dest.printf_P(PSTR("<?xml version=\"1.0\" ?><vs><ac>%d</ac>"), (nightlightActive && nightlightMode > NL_MODE_SET) ? briT : bri);
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
dest.printf_P(PSTR("<cl>%d</cl>"), col[i]);
|
||||
dest.printf_P(PSTR("<cl>%d</cl>"), colPri[i]);
|
||||
}
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
@ -20,7 +20,7 @@ void XML_response(Print& dest)
|
||||
dest.printf_P(PSTR("<ns>%d</ns><nr>%d</nr><nl>%d</nl><nf>%d</nf><nd>%d</nd><nt>%d</nt><fx>%d</fx><sx>%d</sx><ix>%d</ix><fp>%d</fp><wv>%d</wv><ws>%d</ws><ps>%d</ps><cy>%d</cy><ds>%s%s</ds><ss>%d</ss></vs>"),
|
||||
notifyDirect, receiveGroups!=0, nightlightActive, nightlightMode > NL_MODE_SET, nightlightDelayMins,
|
||||
nightlightTargetBri, effectCurrent, effectSpeed, effectIntensity, effectPalette,
|
||||
strip.hasWhiteChannel() ? col[3] : -1, colSec[3], currentPreset, currentPlaylist >= 0,
|
||||
strip.hasWhiteChannel() ? colPri[3] : -1, colSec[3], currentPreset, currentPlaylist >= 0,
|
||||
serverDescription, realtimeMode ? PSTR(" (live)") : "",
|
||||
strip.getFirstSelectedSegId()
|
||||
);
|
||||
@ -110,7 +110,7 @@ void appendGPIOinfo(Print& settingsScript) {
|
||||
settingsScript.print(hardwareTX); // debug output (TX) pin
|
||||
firstPin = false;
|
||||
#endif
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) {
|
||||
if (!firstPin) settingsScript.print(',');
|
||||
for (unsigned p=0; p<WLED_ETH_RSVD_PINS_COUNT; p++) { settingsScript.printf("%d,",esp32_nonconfigurable_ethernet_pins[p].pin); }
|
||||
@ -178,9 +178,12 @@ void getSettingsJS(byte subPage, Print& settingsScript)
|
||||
char fpass[l+1]; //fill password field with ***
|
||||
fpass[l] = 0;
|
||||
memset(fpass,'*',l);
|
||||
settingsScript.printf_P(PSTR("addWiFi(\"%s\",\"%s\",0x%X,0x%X,0x%X);"),
|
||||
char bssid[13];
|
||||
fillMAC2Str(bssid, multiWiFi[n].bssid);
|
||||
settingsScript.printf_P(PSTR("addWiFi(\"%s\",\"%s\",\"%s\",0x%X,0x%X,0x%X);"),
|
||||
multiWiFi[n].clientSSID,
|
||||
fpass,
|
||||
bssid,
|
||||
(uint32_t) multiWiFi[n].staticIP, // explicit cast required as this is a struct
|
||||
(uint32_t) multiWiFi[n].staticGW,
|
||||
(uint32_t) multiWiFi[n].staticSN);
|
||||
@ -219,7 +222,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
|
||||
settingsScript.print(F("toggle('ESPNOW');")); // hide ESP-NOW setting
|
||||
#endif
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
printSetFormValue(settingsScript,PSTR("ETH"),ethernetType);
|
||||
#else
|
||||
//hide ethernet setting if not compiled in
|
||||
@ -272,7 +275,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
|
||||
// set limits
|
||||
settingsScript.printf_P(PSTR("bLimits(%d,%d,%d,%d,%d,%d,%d,%d);"),
|
||||
WLED_MAX_BUSSES,
|
||||
WLED_MIN_VIRTUAL_BUSSES,
|
||||
WLED_MIN_VIRTUAL_BUSSES, // irrelevant, but kept to distinguish S2/S3 in UI
|
||||
MAX_LEDS_PER_BUS,
|
||||
MAX_LED_MEMORY,
|
||||
MAX_LEDS,
|
||||
@ -289,12 +292,13 @@ void getSettingsJS(byte subPage, Print& settingsScript)
|
||||
printSetFormValue(settingsScript,PSTR("FR"),strip.getTargetFps());
|
||||
printSetFormValue(settingsScript,PSTR("AW"),Bus::getGlobalAWMode());
|
||||
printSetFormCheckbox(settingsScript,PSTR("LD"),useGlobalLedBuffer);
|
||||
printSetFormCheckbox(settingsScript,PSTR("PR"),BusManager::hasParallelOutput()); // get it from bus manager not global variable
|
||||
|
||||
unsigned sumMa = 0;
|
||||
for (int s = 0; s < BusManager::getNumBusses(); s++) {
|
||||
Bus* bus = BusManager::getBus(s);
|
||||
if (bus == nullptr) continue;
|
||||
int offset = s < 10 ? 48 : 55;
|
||||
const Bus* bus = BusManager::getBus(s);
|
||||
if (!bus || !bus->isOk()) break; // should not happen but for safety
|
||||
int offset = s < 10 ? '0' : 'A' - 10;
|
||||
char lp[4] = "L0"; lp[2] = offset+s; lp[3] = 0; //ascii 0-9 //strip data pin
|
||||
char lc[4] = "LC"; lc[2] = offset+s; lc[3] = 0; //strip length
|
||||
char co[4] = "CO"; co[2] = offset+s; co[3] = 0; //strip color order
|
||||
@ -312,7 +316,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
|
||||
uint8_t pins[5];
|
||||
int nPins = bus->getPins(pins);
|
||||
for (int i = 0; i < nPins; i++) {
|
||||
lp[1] = offset+i;
|
||||
lp[1] = '0'+i;
|
||||
if (PinManager::isPinOk(pins[i]) || bus->isVirtual()) printSetFormValue(settingsScript,lp,pins[i]);
|
||||
}
|
||||
printSetFormValue(settingsScript,lc,bus->getLength());
|
||||
@ -357,7 +361,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
|
||||
const ColorOrderMap& com = BusManager::getColorOrderMap();
|
||||
for (int s = 0; s < com.count(); s++) {
|
||||
const ColorOrderMapEntry* entry = com.get(s);
|
||||
if (entry == nullptr) break;
|
||||
if (!entry || !entry->len) break;
|
||||
settingsScript.printf_P(PSTR("addCOM(%d,%d,%d);"), entry->start, entry->len, entry->colorOrder);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user