Merge branch '0_15' into new-KITT

This commit is contained in:
Blaz Kristan 2024-04-09 16:36:18 +02:00
commit 38539aac74
72 changed files with 3414 additions and 1259 deletions

View File

@ -37,7 +37,7 @@ jobs:
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
cache: 'npm' cache: 'npm'
- run: npm install - run: npm ci
- name: Cache PlatformIO - name: Cache PlatformIO
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
@ -45,7 +45,7 @@ jobs:
~/.platformio/.cache ~/.platformio/.cache
~/.buildcache ~/.buildcache
build_output build_output
key: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-${{ hashFiles('wled00/**') }} key: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-${{ hashFiles('wled00/**', 'usermods/**') }}
restore-keys: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}- restore-keys: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5
@ -61,7 +61,7 @@ jobs:
name: firmware-${{ matrix.environment }} name: firmware-${{ matrix.environment }}
path: | path: |
build_output/release/*.bin build_output/release/*.bin
build_output/release/*_ESP02.bin.gz build_output/release/*_ESP02*.bin.gz
release: release:
name: Create Release name: Create Release
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -86,7 +86,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Use Node.js - name: Use Node.js
uses: actions/setup-node@v3 uses: actions/setup-node@v4
with: with:
node-version: '20.x' node-version: '20.x'
cache: 'npm' cache: 'npm'

View File

@ -1,5 +1,66 @@
## WLED changelog ## WLED changelog
#### Build 2403280
- Individual color channel control for JSON API (fixes #3860)
- "col":[int|string|object|array, int|string|object|array, int|string|object|array]
int = Kelvin temperature or 0 for black
string = hex representation of [WW]RRGGBB
object = individual channel control {"r":0,"g":127,"b":255,"w":255}, each being optional (valid to send {})
array = direct channel values [r,g,b,w] (w element being optional)
- runtime selection for CCT IC (Athom 15W bulb)
- #3850 (by @w00000dy)
- Rotary encoder palette count bugfix
- bugfixes and optimisations
#### Build 2403240
- v0.15.0-b2
- WS2805 support (RGB + WW + CW, 600kbps)
- Unified PSRAM use
- NeoPixelBus v2.7.9 (for future WS2805 support)
- Ubiquitous PSRAM mode for all variants of ESP32
- SSD1309_64 I2C Support for FLD Usermod (#3836 by @THATDONFC)
- Palette cycling fix (add support for `{"seg":[{"pal":"X~Y~"}]}` or `{"seg":[{"pal":"X~Yr"}]}`)
- FW1906 Support (#3810 by @deece and @Robert-github-com)
- ESPAsyncWebServer 2.2.0 (#3828 by @willmmiles)
- Bugfixes: #3843, #3844
#### Build 2403190
- limit max PWM frequency (fix incorrect PWM resolution)
- Segment UI bugfix
- Updated AsyncWebServer (by @wlillmmiles)
- Simpler boot preset (fix for #3806)
- Effect: Fix for 2D Drift animation (#3816 by @BaptisteHudyma)
- Effect: Add twin option to 2D Drift
- MQTT cleanup
- DDP: Support sources that don't push (#3833 by @willmmiles)
- Usermod: Tetris AI usermod (#3711 by @muebau)
#### Build 2403171
- merge 0.14.2 changes into 0.15
#### Build 2403070
- Add additional segment options when controlling over e1.31 (#3616 by @demophoon)
- LockedJsonResponse: Release early if possible (#3760 by @willmmiles)
- Update setup-node and cache usermods in wled-ci.yml (#3737 by @WoodyLetsCode)
- Fix preset sorting (#3790 by @WoodyLetsCode)
- compile time button configuration #3792
- remove IR config if not compiled
- additional string optimisations
- Better low brightness level PWM handling (fixes #2767, #2868)
#### Build 2402290
- Multiple analog button fix for #3549
- Preset caching on chips with PSRAM (credit @akaricchi)
- Fixing stairway usermod and adding buildflags (by @lost-hope)
- ESP-NOW packet modification
- JSON buffer lock error messages / Reduce wait time for lock to 100ms
- Reduce string RAM usage for ESP8266
- Fixing a potential array bounds violation in ESPDMX
- Move timezone table to PROGMEM (#3766 by @willmmiles)
- Reposition upload warning message. (fixes #3778)
- ABL display fix & optimisation
- Add virtual Art-Net RGBW option (#3783 by @shammy642)
#### Build 2402090 #### Build 2402090
- Added new Ethernet controller RGB2Go Tetra (duplicate of ESP3DEUXQuattro) - Added new Ethernet controller RGB2Go Tetra (duplicate of ESP3DEUXQuattro)
- Usermod: httpPullLightControl (#3560 by @roelbroersma) - Usermod: httpPullLightControl (#3560 by @roelbroersma)
@ -15,7 +76,7 @@
#### Build 2309120 till build 2402010 #### Build 2309120 till build 2402010
- WLED version 0.15.0-a0 - WLED version 0.15.0-a0
- Multi-WiFi support. Add up to 3 (or more via cusom compile) WiFis to connect to - Multi-WiFi support. Add up to 3 (or more via cusom compile) WiFis to connect to (with help from @JPZV)
- Temporary AP. Use your WLED in public with temporary AP. - Temporary AP. Use your WLED in public with temporary AP.
- Github CI build system enhancements (#3718 by @WoodyLetsCode) - Github CI build system enhancements (#3718 by @WoodyLetsCode)
- Accessibility: Node list ( #3715 by @WoodyLetsCode) - Accessibility: Node list ( #3715 by @WoodyLetsCode)
@ -97,6 +158,26 @@
- send UDP/WS on segment change - send UDP/WS on segment change
- pop_back() when removing last segment - pop_back() when removing last segment
#### Build 2403170
- WLED 0.14.2 release
#### Build 2403110
- Beta WLED 0.14.2-b2
- New AsyncWebServer (improved performance and reduced memory use)
- New builds for ESP8266 with 160MHz CPU clock
- Fixing stairway usermod and adding buildflags (#3758 by @lost-hope)
- Fixing a potential array bounds violation in ESPDMX
- Reduced RAM usage (moved strings and TZ data (by @willmmiles) to PROGMEM)
- LockedJsonResponse: Release early if possible (by @willmmiles)
#### Build 2402120
- Beta WLED 0.14.2-b1
- Possible fix for #3589 & partial fix for #3605
- Prevent JSON buffer clear after failed lock attempt
- Multiple analog button fix for #3549
- UM Audioreactive: add two compiler options (#3732 by @wled-install)
- Fix for #3693
#### Build 2401141 #### Build 2401141
- Official release of WLED 0.14.1 - Official release of WLED 0.14.1
- Fix for #3566, #3665, #3672 - Fix for #3566, #3665, #3672

View File

@ -2,6 +2,20 @@
Here are a few suggestions to make it easier for you to contribute! Here are a few suggestions to make it easier for you to contribute!
### Describe your PR
Please add a description of your proposed code changes. It does not need to be an exhaustive essay, however a PR with no description or just a few words might not get accepted, simply because very basic information is missing.
A good description helps us to review and understand your proposed changes. For example, you could say a few words about
* what you try to achieve (new feature, fixing a bug, refactoring, security enhancements, etc.)
* how your code works (short technical summary - focus on important aspects that might not be obvious when reading the code)
* testing you performed, known limitations, open ends you possibly could not solve.
* any areas where you like to get help from an experienced maintainer (yes WLED has become big 😉)
### Target branch for pull requests
Please make all PRs against the `0_15` branch.
### Code style ### Code style
When in doubt, it is easiest to replicate the code style you find in the files you want to edit :) When in doubt, it is easiest to replicate the code style you find in the files you want to edit :)
@ -73,6 +87,6 @@ Good:
<!-- This is an HTML comment --> <!-- This is an HTML comment -->
``` ```
There is no set character limit for a comment within a line, There is no hard character limit for a comment within a line,
though as a rule of thumb you should wrap your comment if it exceeds the width of your editor window. though as a rule of thumb consider wrapping after 120 characters.
Inline comments are OK if they describe that line only and are not exceedingly wide. Inline comments are OK if they describe that line only and are not exceedingly wide.

102
package-lock.json generated
View File

@ -1,57 +1,56 @@
{ {
"name": "wled", "name": "wled",
"version": "0.15.0-b1", "version": "0.15.0-b2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "wled", "name": "wled",
"version": "0.15.0-a0", "version": "0.15.0-b2",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"clean-css": "^5.3.3", "clean-css": "^5.3.3",
"html-minifier-terser": "^7.2.0", "html-minifier-terser": "^7.2.0",
"inliner": "^1.13.1", "inliner": "^1.13.1",
"nodemon": "^3.0.2", "nodemon": "^3.0.2"
"zlib": "^1.0.5"
} }
}, },
"node_modules/@jridgewell/gen-mapping": { "node_modules/@jridgewell/gen-mapping": {
"version": "0.3.3", "version": "0.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
"dependencies": { "dependencies": {
"@jridgewell/set-array": "^1.0.1", "@jridgewell/set-array": "^1.2.1",
"@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/sourcemap-codec": "^1.4.10",
"@jridgewell/trace-mapping": "^0.3.9" "@jridgewell/trace-mapping": "^0.3.24"
}, },
"engines": { "engines": {
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/@jridgewell/resolve-uri": { "node_modules/@jridgewell/resolve-uri": {
"version": "3.1.1", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"engines": { "engines": {
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/@jridgewell/set-array": { "node_modules/@jridgewell/set-array": {
"version": "1.1.2", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
"engines": { "engines": {
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/@jridgewell/source-map": { "node_modules/@jridgewell/source-map": {
"version": "0.3.5", "version": "0.3.6",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
"integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
"dependencies": { "dependencies": {
"@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.9" "@jridgewell/trace-mapping": "^0.3.25"
} }
}, },
"node_modules/@jridgewell/sourcemap-codec": { "node_modules/@jridgewell/sourcemap-codec": {
@ -60,9 +59,9 @@
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
}, },
"node_modules/@jridgewell/trace-mapping": { "node_modules/@jridgewell/trace-mapping": {
"version": "0.3.21", "version": "0.3.25",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.21.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
"integrity": "sha512-SRfKmRe1KvYnxjEMtxEr+J4HIeMX5YBg/qhRHpxEIGjhX1rshcHlnFUE9K0GazhVKWM7B+nARSkV8LuvJdJ5/g==", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
"dependencies": { "dependencies": {
"@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14" "@jridgewell/sourcemap-codec": "^1.4.14"
@ -209,11 +208,14 @@
} }
}, },
"node_modules/binary-extensions": { "node_modules/binary-extensions": {
"version": "2.2.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
"engines": { "engines": {
"node": ">=8" "node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/boolbase": { "node_modules/boolbase": {
@ -324,15 +326,9 @@
"integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w=="
}, },
"node_modules/chokidar": { "node_modules/chokidar": {
"version": "3.5.3", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
],
"dependencies": { "dependencies": {
"anymatch": "~3.1.2", "anymatch": "~3.1.2",
"braces": "~3.0.2", "braces": "~3.0.2",
@ -345,6 +341,9 @@
"engines": { "engines": {
"node": ">= 8.10.0" "node": ">= 8.10.0"
}, },
"funding": {
"url": "https://paulmillr.com/funding/"
},
"optionalDependencies": { "optionalDependencies": {
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
@ -1376,9 +1375,9 @@
} }
}, },
"node_modules/nodemon": { "node_modules/nodemon": {
"version": "3.0.3", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.3.tgz", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.0.tgz",
"integrity": "sha512-7jH/NXbFPxVaMwmBCC2B9F/V6X1VkEdNgx3iu9jji8WxWcvhMWkmhNWhI5077zknOnZnBzba9hZP6bCPJLSReQ==", "integrity": "sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==",
"dependencies": { "dependencies": {
"chokidar": "^3.5.2", "chokidar": "^3.5.2",
"debug": "^4", "debug": "^4",
@ -1827,9 +1826,9 @@
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
}, },
"node_modules/semver": { "node_modules/semver": {
"version": "7.5.4", "version": "7.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
"dependencies": { "dependencies": {
"lru-cache": "^6.0.0" "lru-cache": "^6.0.0"
}, },
@ -1925,9 +1924,9 @@
} }
}, },
"node_modules/stream-shift": { "node_modules/stream-shift": {
"version": "1.0.2", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.2.tgz", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
"integrity": "sha512-rV4Bovi9xx0BFzOb/X0B2GqoIjvqPCttZdu0Wgtx2Dxkj7ETyWl9gmqJ4EutWRLvtZWm8dxE+InQZX1IryZn/w==" "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ=="
}, },
"node_modules/string_decoder": { "node_modules/string_decoder": {
"version": "0.10.31", "version": "0.10.31",
@ -1994,9 +1993,9 @@
} }
}, },
"node_modules/terser": { "node_modules/terser": {
"version": "5.27.0", "version": "5.29.2",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz", "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.2.tgz",
"integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==", "integrity": "sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==",
"dependencies": { "dependencies": {
"@jridgewell/source-map": "^0.3.3", "@jridgewell/source-map": "^0.3.3",
"acorn": "^8.8.2", "acorn": "^8.8.2",
@ -2245,15 +2244,6 @@
"decamelize": "^1.0.0", "decamelize": "^1.0.0",
"window-size": "0.1.0" "window-size": "0.1.0"
} }
},
"node_modules/zlib": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/zlib/-/zlib-1.0.5.tgz",
"integrity": "sha512-40fpE2II+Cd3k8HWTWONfeKE2jL+P42iWJ1zzps5W51qcTsOUKM5Q5m2PFb0CLxlmFAaUuUdJGc3OfZy947v0w==",
"hasInstallScript": true,
"engines": {
"node": ">=0.2.0"
}
} }
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "wled", "name": "wled",
"version": "0.15.0-b1", "version": "0.15.0-b2",
"description": "Tools for WLED project", "description": "Tools for WLED project",
"main": "tools/cdata.js", "main": "tools/cdata.js",
"directories": { "directories": {
@ -26,7 +26,6 @@
"clean-css": "^5.3.3", "clean-css": "^5.3.3",
"html-minifier-terser": "^7.2.0", "html-minifier-terser": "^7.2.0",
"inliner": "^1.13.1", "inliner": "^1.13.1",
"nodemon": "^3.0.2", "nodemon": "^3.0.2"
"zlib": "^1.0.5"
} }
} }

View File

@ -4,6 +4,7 @@ import shutil
import gzip import gzip
OUTPUT_DIR = "build_output{}".format(os.path.sep) OUTPUT_DIR = "build_output{}".format(os.path.sep)
#OUTPUT_DIR = os.path.join("build_output")
def _get_cpp_define_value(env, define): def _get_cpp_define_value(env, define):
define_list = [item[-1] for item in env["CPPDEFINES"] if item[0] == define] define_list = [item[-1] for item in env["CPPDEFINES"] if item[0] == define]
@ -13,24 +14,24 @@ def _get_cpp_define_value(env, define):
return None return None
def _create_dirs(dirs=["firmware", "map"]): def _create_dirs(dirs=["map", "release", "firmware"]):
# check if output directories exist and create if necessary
if not os.path.isdir(OUTPUT_DIR):
os.mkdir(OUTPUT_DIR)
for d in dirs: for d in dirs:
if not os.path.isdir("{}{}".format(OUTPUT_DIR, d)): os.makedirs(os.path.join(OUTPUT_DIR, d), exist_ok=True)
os.mkdir("{}{}".format(OUTPUT_DIR, d))
def create_release(source): def create_release(source):
release_name = _get_cpp_define_value(env, "WLED_RELEASE_NAME") release_name = _get_cpp_define_value(env, "WLED_RELEASE_NAME")
if release_name: if release_name:
_create_dirs(["release"])
version = _get_cpp_define_value(env, "WLED_VERSION") version = _get_cpp_define_value(env, "WLED_VERSION")
# get file extension of source file (.bin or .bin.gz) release_file = os.path.join(OUTPUT_DIR, "release", f"WLED_{version}_{release_name}.bin")
ext = source.split(".", 1)[1] release_gz_file = release_file + ".gz"
release_file = "{}release{}WLED_{}_{}.{}".format(OUTPUT_DIR, os.path.sep, version, release_name, ext) print(f"Copying {source} to {release_file}")
shutil.copy(source, release_file) shutil.copy(source, release_file)
bin_gzip(release_file, release_gz_file)
else:
variant = env["PIOENV"]
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
print(f"Copying {source} to {bin_file}")
shutil.copy(source, bin_file)
def bin_rename_copy(source, target, env): def bin_rename_copy(source, target, env):
_create_dirs() _create_dirs()
@ -38,38 +39,21 @@ def bin_rename_copy(source, target, env):
# create string with location and file names based on variant # create string with location and file names based on variant
map_file = "{}map{}{}.map".format(OUTPUT_DIR, os.path.sep, variant) map_file = "{}map{}{}.map".format(OUTPUT_DIR, os.path.sep, variant)
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
# check if new target files exist and remove if necessary create_release(str(target[0]))
for f in [map_file, bin_file]:
if os.path.isfile(f):
os.remove(f)
# copy firmware.bin to firmware/<variant>.bin
shutil.copy(str(target[0]), bin_file)
create_release(bin_file)
# copy firmware.map to map/<variant>.map # copy firmware.map to map/<variant>.map
if os.path.isfile("firmware.map"): if os.path.isfile("firmware.map"):
shutil.move("firmware.map", map_file) shutil.move("firmware.map", map_file)
def bin_gzip(source, target, env): def bin_gzip(source, target):
_create_dirs() # only create gzip for esp8266
variant = env["PIOENV"] if not env["PIOPLATFORM"] == "espressif8266":
return
# create string with location and file names based on variant print(f"Creating gzip file {target} from {source}")
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant) with open(source,"rb") as fp:
gzip_file = "{}firmware{}{}.bin.gz".format(OUTPUT_DIR, os.path.sep, variant) with gzip.open(target, "wb", compresslevel = 9) as f:
# check if new target files exist and remove if necessary
if os.path.isfile(gzip_file): os.remove(gzip_file)
# write gzip firmware file
with open(bin_file,"rb") as fp:
with gzip.open(gzip_file, "wb", compresslevel = 9) as f:
shutil.copyfileobj(fp, f) shutil.copyfileobj(fp, f)
create_release(gzip_file) env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", bin_rename_copy)
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_rename_copy, bin_gzip])

View File

@ -10,7 +10,7 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# CI/release binaries # CI/release binaries
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, esp32dev_audioreactive, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB, esp32s3dev_8MB_PSRAM_opi, esp32_wrover default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, nodemcuv2_160, esp8266_2m_160, esp01_1m_full_160, esp32dev, esp32_eth, esp32dev_audioreactive, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB, esp32s3dev_8MB_PSRAM_opi, esp32s3_4M_PSRAM_qspi, esp32_wrover
src_dir = ./wled00 src_dir = ./wled00
data_dir = ./wled00/data data_dir = ./wled00/data
@ -41,14 +41,13 @@ arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/
platform_wled_default = ${common.arduino_core_3_1_2} platform_wled_default = ${common.arduino_core_3_1_2}
# We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization # We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization
#platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7 #platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
platform_packages = platformio/framework-arduinoespressif8266 platform_packages = platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502
platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502
platformio/tool-esptool #@ ~1.413.0 platformio/tool-esptool #@ ~1.413.0
platformio/tool-esptoolpy #@ ~1.30000.0 platformio/tool-esptoolpy #@ ~1.30000.0
## previous platform for 8266, in case of problems with the new one ## previous platform for 8266, in case of problems with the new one
## you'll need makuna/NeoPixelBus@ 2.6.9 for arduino_core_3_2_0, which does not support Ucs890x ## you'll need makuna/NeoPixelBus@ 2.6.9 for arduino_core_3_0_2, which does not support Ucs890x
;; platform_wled_default = ${common.arduino_core_3_2_0} ;; platform_wled_default = ${common.arduino_core_3_0_2}
;; platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7 ;; platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
;; platformio/toolchain-xtensa @ ~2.40802.200502 ;; platformio/toolchain-xtensa @ ~2.40802.200502
;; platformio/tool-esptool @ ~1.413.0 ;; platformio/tool-esptool @ ~1.413.0
@ -143,8 +142,8 @@ lib_compat_mode = strict
lib_deps = lib_deps =
fastled/FastLED @ 3.6.0 fastled/FastLED @ 3.6.0
IRremoteESP8266 @ 2.8.2 IRremoteESP8266 @ 2.8.2
makuna/NeoPixelBus @ 2.7.5 makuna/NeoPixelBus @ 2.7.9
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.7 https://github.com/Aircoookie/ESPAsyncWebServer.git @ 2.2.1
# for I2C interface # for I2C interface
;Wire ;Wire
# ESP-NOW library # ESP-NOW library
@ -164,6 +163,9 @@ lib_deps =
#For ADS1115 sensor uncomment following #For ADS1115 sensor uncomment following
;adafruit/Adafruit BusIO @ 1.13.2 ;adafruit/Adafruit BusIO @ 1.13.2
;adafruit/Adafruit ADS1X15 @ 2.4.0 ;adafruit/Adafruit ADS1X15 @ 2.4.0
#For MAX1704x Lipo Monitor / Fuel Gauge uncomment following
; https://github.com/adafruit/Adafruit_BusIO @ 1.14.5
; https://github.com/adafruit/Adafruit_MAX1704X @ 1.0.2
#For MPU6050 IMU uncomment follwoing #For MPU6050 IMU uncomment follwoing
;electroniccats/MPU6050 @1.0.1 ;electroniccats/MPU6050 @1.0.1
# For -D USERMOD_ANIMARTRIX # For -D USERMOD_ANIMARTRIX
@ -172,7 +174,7 @@ lib_deps =
# SHT85 # SHT85
;robtillaart/SHT85@~0.3.3 ;robtillaart/SHT85@~0.3.3
# Audioreactive usermod # Audioreactive usermod
;https://github.com/kosme/arduinoFFT#develop @ ^1.9.2 ;kosme/arduinoFFT @ 2.0.0
extra_scripts = ${scripts_defaults.extra_scripts} extra_scripts = ${scripts_defaults.extra_scripts}
@ -222,8 +224,8 @@ lib_deps =
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps} ${env.lib_deps}
# additional build flags for audioreactive # additional build flags for audioreactive
AR_build_flags = -D USERMOD_AUDIOREACTIVE -D UM_AUDIOREACTIVE_USE_NEW_FFT AR_build_flags = -D USERMOD_AUDIOREACTIVE
AR_lib_deps = https://github.com/kosme/arduinoFFT#develop @ ^1.9.2 AR_lib_deps = kosme/arduinoFFT @ 2.0.0
[esp32_idf_V4] [esp32_idf_V4]
;; experimental build environment for ESP32 using ESP-IDF 4.4.x / arduino-esp32 v2.0.5 ;; experimental build environment for ESP32 using ESP-IDF 4.4.x / arduino-esp32 v2.0.5
@ -314,6 +316,11 @@ build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 #-DWLED
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
monitor_filters = esp8266_exception_decoder monitor_filters = esp8266_exception_decoder
[env:nodemcuv2_160]
extends = env:nodemcuv2
board_build.f_cpu = 160000000L
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266_160 #-DWLED_DISABLE_2D
[env:esp8266_2m] [env:esp8266_2m]
board = esp_wroom_02 board = esp_wroom_02
platform = ${common.platform_wled_default} platform = ${common.platform_wled_default}
@ -323,6 +330,11 @@ build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02 build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:esp8266_2m_160]
extends = env:esp8266_2m
board_build.f_cpu = 160000000L
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02_160
[env:esp01_1m_full] [env:esp01_1m_full]
board = esp01_1m board = esp01_1m
platform = ${common.platform_wled_default} platform = ${common.platform_wled_default}
@ -333,6 +345,12 @@ build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01 -D WLED_D
; -D WLED_USE_UNREAL_MATH ;; may cause wrong sunset/sunrise times, but saves 7064 bytes FLASH and 975 bytes RAM ; -D WLED_USE_UNREAL_MATH ;; may cause wrong sunset/sunrise times, but saves 7064 bytes FLASH and 975 bytes RAM
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:esp01_1m_full_160]
extends = env:esp01_1m_full
board_build.f_cpu = 160000000L
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01_160 -D WLED_DISABLE_OTA
; -D WLED_USE_UNREAL_MATH ;; may cause wrong sunset/sunrise times, but saves 7064 bytes FLASH and 975 bytes RAM
[env:esp32dev] [env:esp32dev]
board = esp32dev board = esp32dev
platform = ${esp32.platform} platform = ${esp32.platform}
@ -373,11 +391,10 @@ platform = ${esp32.platform}
board = ttgo-t7-v14-mini32 board = ttgo-t7-v14-mini32
board_build.f_flash = 80000000L board_build.f_flash = 80000000L
board_build.flash_mode = qio board_build.flash_mode = qio
board_build.partitions = ${esp32.default_partitions} board_build.partitions = tools/WLED_ESP32-wrover_4MB.csv
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_WROVER build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_WROVER
-DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue ;; Older ESP32 (rev.<3) need a PSRAM fix (increases static RAM used) https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/external-ram.html
-D WLED_USE_PSRAM
-D LEDPIN=25 -D LEDPIN=25
lib_deps = ${esp32.lib_deps} lib_deps = ${esp32.lib_deps}
@ -408,8 +425,9 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip -D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
;-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") ;-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
;-D WLED_DEBUG ${esp32.AR_build_flags}
lib_deps = ${esp32s3.lib_deps} lib_deps = ${esp32s3.lib_deps}
${esp32.AR_lib_deps}
board_build.partitions = tools/WLED_ESP32_8MB.csv board_build.partitions = tools/WLED_ESP32_8MB.csv
board_build.f_flash = 80000000L board_build.f_flash = 80000000L
board_build.flash_mode = qio board_build.flash_mode = qio
@ -428,14 +446,35 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip ;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
-D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
; -D WLED_RELEASE_NAME=ESP32-S3_PSRAM -DBOARD_HAS_PSRAM
-D WLED_USE_PSRAM -DBOARD_HAS_PSRAM ; tells WLED that PSRAM shall be used ${esp32.AR_build_flags}
lib_deps = ${esp32s3.lib_deps} lib_deps = ${esp32s3.lib_deps}
${esp32.AR_lib_deps}
board_build.partitions = tools/WLED_ESP32_8MB.csv board_build.partitions = tools/WLED_ESP32_8MB.csv
board_build.f_flash = 80000000L board_build.f_flash = 80000000L
board_build.flash_mode = qio board_build.flash_mode = qio
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
[env:esp32s3_4M_PSRAM_qspi]
;; ESP32-S3, with 4MB FLASH and <= 4MB PSRAM (memory_type: qio_qspi)
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
platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_4M_PSRAM_qspi
-DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
-DBOARD_HAS_PSRAM
-D WLED_WATCHDOG_TIMEOUT=0
${esp32.AR_build_flags}
lib_deps = ${esp32s3.lib_deps}
${esp32.AR_lib_deps}
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
board_build.f_flash = 80000000L
board_build.flash_mode = qio
monitor_filters = esp32_exception_decoder
[env:lolin_s2_mini] [env:lolin_s2_mini]
platform = ${esp32s2.platform} platform = ${esp32s2.platform}
platform_packages = ${esp32s2.platform_packages} platform_packages = ${esp32s2.platform_packages}
@ -445,12 +484,11 @@ board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
;board_build.f_flash = 80000000L ;board_build.f_flash = 80000000L
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=ESP32-S2 build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=ESP32-S2
-DBOARD_HAS_PSRAM
-DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_CDC_ON_BOOT=1
-DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0
-DARDUINO_USB_DFU_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0
-DBOARD_HAS_PSRAM
-DLOLIN_WIFI_FIX ; seems to work much better with this -DLOLIN_WIFI_FIX ; seems to work much better with this
-D WLED_USE_PSRAM
-D WLED_WATCHDOG_TIMEOUT=0 -D WLED_WATCHDOG_TIMEOUT=0
-D CONFIG_ASYNC_TCP_USE_WDT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0
-D LEDPIN=16 -D LEDPIN=16
@ -460,4 +498,6 @@ build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=
-D HW_PIN_DATASPI=11 -D HW_PIN_DATASPI=11
-D HW_PIN_MISOSPI=9 -D HW_PIN_MISOSPI=9
; -D STATUSLED=15 ; -D STATUSLED=15
${esp32.AR_build_flags}
lib_deps = ${esp32s2.lib_deps} lib_deps = ${esp32s2.lib_deps}
${esp32.AR_lib_deps}

View File

@ -155,9 +155,8 @@ build_flags = ${common.build_flags_esp8266}
; set default color order of your led strip ; set default color order of your led strip
; -D DEFAULT_LED_COLOR_ORDER=COL_ORDER_GRB ; -D DEFAULT_LED_COLOR_ORDER=COL_ORDER_GRB
; ;
; use PSRAM if a device (ESP) has one ; use PSRAM on classic ESP32 rev.1 (rev.3 or above has no issues)
; -DBOARD_HAS_PSRAM ; -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue
; -D WLED_USE_PSRAM
; ;
; configure I2C and SPI interface (for various hardware) ; configure I2C and SPI interface (for various hardware)
; -D I2CSDAPIN=33 # initialise interface ; -D I2CSDAPIN=33 # initialise interface

View File

@ -36,7 +36,7 @@ marshmallow==3.19.0
# via platformio # via platformio
packaging==23.1 packaging==23.1
# via marshmallow # via marshmallow
platformio==6.1.6 platformio==6.1.14
# via -r requirements.in # via -r requirements.in
pyelftools==0.29 pyelftools==0.29
# via platformio # via platformio

View File

@ -1,6 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags # Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000, nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000, otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x180000, app0, app, ota_0, 0x10000, 0x1A0000,
app1, app, ota_1, 0x190000,0x180000, app1, app, ota_1, 0x1B0000,0x1A0000,
spiffs, data, spiffs, 0x310000,0xF0000, spiffs, data, spiffs, 0x350000,0xB0000,

1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0xe000 0x2000
4 app0 app ota_0 0x10000 0x180000 0x1A0000
5 app1 app ota_1 0x190000 0x1B0000 0x180000 0x1A0000
6 spiffs data spiffs 0x310000 0x350000 0xF0000 0xB0000

View File

@ -83,6 +83,7 @@ describe('Script', () => {
// Backup files // Backup files
fs.cpSync("wled00/data", "wled00Backup", { recursive: true }); fs.cpSync("wled00/data", "wled00Backup", { recursive: true });
fs.cpSync("tools/cdata.js", "cdata.bak.js"); fs.cpSync("tools/cdata.js", "cdata.bak.js");
fs.cpSync("package.json", "package.bak.json");
}); });
after(() => { after(() => {
// Restore backup // Restore backup
@ -90,6 +91,8 @@ describe('Script', () => {
fs.renameSync("wled00Backup", "wled00/data"); fs.renameSync("wled00Backup", "wled00/data");
fs.rmSync("tools/cdata.js"); fs.rmSync("tools/cdata.js");
fs.renameSync("cdata.bak.js", "tools/cdata.js"); fs.renameSync("cdata.bak.js", "tools/cdata.js");
fs.rmSync("package.json");
fs.renameSync("package.bak.json", "package.json");
}); });
// delete all html_*.h files // delete all html_*.h files
@ -131,7 +134,7 @@ describe('Script', () => {
// run script cdata.js again and wait for it to finish // run script cdata.js again and wait for it to finish
await execPromise('node tools/cdata.js'); await execPromise('node tools/cdata.js');
checkIfFileWasNewlyCreated(path.join(folderPath, resultFile)); await checkIfFileWasNewlyCreated(path.join(folderPath, resultFile));
} }
describe('should build if', () => { describe('should build if', () => {
@ -182,6 +185,10 @@ describe('Script', () => {
it('cdata.js changes', async () => { it('cdata.js changes', async () => {
await testFileModification('tools/cdata.js', 'html_ui.h'); await testFileModification('tools/cdata.js', 'html_ui.h');
}); });
it('package.json changes', async () => {
await testFileModification('package.json', 'html_ui.h');
});
}); });
describe('should not build if', () => { describe('should not build if', () => {

View File

@ -2,7 +2,7 @@
* Writes compressed C arrays of data files (web interface) * Writes compressed C arrays of data files (web interface)
* How to use it? * How to use it?
* *
* 1) Install Node 11+ and npm * 1) Install Node 20+ and npm
* 2) npm install * 2) npm install
* 3) npm run build * 3) npm run build
* *
@ -15,10 +15,10 @@
* It uses NodeJS packages to inline, minify and GZIP files. See writeHtmlGzipped and writeChunks invocations at the bottom of the page. * It uses NodeJS packages to inline, minify and GZIP files. See writeHtmlGzipped and writeChunks invocations at the bottom of the page.
*/ */
const fs = require("fs"); const fs = require("node:fs");
const path = require("path"); const path = require("path");
const inliner = require("inliner"); const inliner = require("inliner");
const zlib = require("zlib"); const zlib = require("node:zlib");
const CleanCSS = require("clean-css"); const CleanCSS = require("clean-css");
const minifyHtml = require("html-minifier-terser").minify; const minifyHtml = require("html-minifier-terser").minify;
const packageJson = require("../package.json"); const packageJson = require("../package.json");
@ -30,14 +30,12 @@ const output = ["wled00/html_ui.h", "wled00/html_pixart.h", "wled00/html_cpal.h"
// \x1b[34m is blue, \x1b[36m is cyan, \x1b[0m is reset // \x1b[34m is blue, \x1b[36m is cyan, \x1b[0m is reset
const wledBanner = ` const wledBanner = `
\t\x1b[34m## ## ## ######## ######## \t\x1b[34m ## ## ## ###### ######
\t\x1b[34m## ## ## ## ## ## ## \t\x1b[34m## ## ## ## ## ## ##
\t\x1b[34m## ## ## ## ## ## ## \t\x1b[34m## ## ## ## ###### ## ##
\t\x1b[34m## ## ## ## ###### ## ## \t\x1b[34m## ## ## ## ## ## ##
\t\x1b[34m## ## ## ## ## ## ## \t\x1b[34m ## ## ###### ###### ######
\t\x1b[34m## ## ## ## ## ## ## \t\t\x1b[36m build script for web UI
\t\x1b[34m ### ### ######## ######## ########
\t\t\x1b[36mbuild script for web UI
\x1b[0m`; \x1b[0m`;
const singleHeader = `/* const singleHeader = `/*
@ -209,7 +207,7 @@ function isAnyFileInFolderNewerThan(folderPath, time) {
} }
// Check if the web UI is already built // Check if the web UI is already built
function isAlreadyBuilt(folderPath) { function isAlreadyBuilt(webUIPath, packageJsonPath = "package.json") {
let lastBuildTime = Infinity; let lastBuildTime = Infinity;
for (const file of output) { for (const file of output) {
@ -222,7 +220,7 @@ function isAlreadyBuilt(folderPath) {
} }
} }
return !isAnyFileInFolderNewerThan(folderPath, lastBuildTime) && !isFileNewerThan("tools/cdata.js", lastBuildTime); return !isAnyFileInFolderNewerThan(webUIPath, lastBuildTime) && !isFileNewerThan(packageJsonPath, lastBuildTime) && !isFileNewerThan(__filename, lastBuildTime);
} }
// Don't run this script if we're in a test environment // Don't run this script if we're in a test environment

View File

@ -0,0 +1,64 @@
# Adafruit MAX17048 Usermod (LiPo & LiIon Battery Monitor & Fuel Gauge)
This usermod reads information from an Adafruit MAX17048 and outputs the following:
- Battery Voltage
- Battery Level Percentage
## Dependencies
Libraries:
- `Adafruit_BusIO@~1.14.5` (by [adafruit](https://github.com/adafruit/Adafruit_BusIO))
- `Adafruit_MAX1704X@~1.0.2` (by [adafruit](https://github.com/adafruit/Adafruit_MAX1704X))
These must be added under `lib_deps` in your `platform.ini` (or `platform_override.ini`).
Data is published over MQTT - make sure you've enabled the MQTT sync interface.
## Compilation
To enable, compile with `USERMOD_MAX17048` define in the build_flags (e.g. in `platformio.ini` or `platformio_override.ini`) such as in the example below:
```ini
[env:usermod_max17048_d1_mini]
extends = env:d1_mini
build_flags =
${common.build_flags_esp8266}
-D USERMOD_MAX17048
lib_deps =
${esp8266.lib_deps}
https://github.com/adafruit/Adafruit_BusIO @ 1.14.5
https://github.com/adafruit/Adafruit_MAX1704X @ 1.0.2
```
### Configuration Options
The following settings can be set at compile-time but are configurable on the usermod menu (except First Monitor time):
- USERMOD_MAX17048_MIN_MONITOR_INTERVAL (the min number of milliseconds between checks, defaults to 10,000 ms)
- USERMOD_MAX17048_MAX_MONITOR_INTERVAL (the max number of milliseconds between checks, defaults to 10,000 ms)
- USERMOD_MAX17048_FIRST_MONITOR_AT
Additionally, the Usermod Menu allows you to:
- Enable or Disable the usermod
- Enable or Disable Home Assistant Discovery (turn on/off to sent MQTT Discovery entries for Home Assistant)
- Configure SCL/SDA GPIO Pins
## API
The following method is available to interact with the usermod from other code modules:
- `getBatteryVoltageV` read the last battery voltage (in Volt) obtained from the sensor
- `getBatteryPercent` reads the last battery percentage obtained from the sensor
## MQTT
MQTT topics are as follows (`<deviceTopic>` is set in MQTT section of Sync Setup menu):
Measurement type | MQTT topic
--- | ---
Battery Voltage | `<deviceTopic>/batteryVoltage`
Battery Percent | `<deviceTopic>/batteryPercent`
## Authors
Carlos Cruz [@ccruz09](https://github.com/ccruz09)
## Revision History
Jan 2024
- Added Home Assistant Discovery
- Implemented PinManager to register pins
- Added API call for other modules to read battery voltage and percentage
- Added info-screen outputs
- Updated `readme.md`

View File

@ -0,0 +1,281 @@
// force the compiler to show a warning to confirm that this file is included
#warning **** Included USERMOD_MAX17048 V2.0 ****
#pragma once
#include "wled.h"
#include "Adafruit_MAX1704X.h"
// the max interval to check battery level, 10 seconds
#ifndef USERMOD_MAX17048_MAX_MONITOR_INTERVAL
#define USERMOD_MAX17048_MAX_MONITOR_INTERVAL 10000
#endif
// the min interval to check battery level, 500 ms
#ifndef USERMOD_MAX17048_MIN_MONITOR_INTERVAL
#define USERMOD_MAX17048_MIN_MONITOR_INTERVAL 500
#endif
// how many seconds after boot to perform the first check, 10 seconds
#ifndef USERMOD_MAX17048_FIRST_MONITOR_AT
#define USERMOD_MAX17048_FIRST_MONITOR_AT 10000
#endif
/*
* Usermod to display Battery Life using Adafruit's MAX17048 LiPoly/ LiIon Fuel Gauge and Battery Monitor.
*/
class Usermod_MAX17048 : public Usermod {
private:
bool enabled = true;
unsigned long maxReadingInterval = USERMOD_MAX17048_MAX_MONITOR_INTERVAL;
unsigned long minReadingInterval = USERMOD_MAX17048_MIN_MONITOR_INTERVAL;
unsigned long lastCheck = UINT32_MAX - (USERMOD_MAX17048_MAX_MONITOR_INTERVAL - USERMOD_MAX17048_FIRST_MONITOR_AT);
unsigned long lastSend = UINT32_MAX - (USERMOD_MAX17048_MAX_MONITOR_INTERVAL - USERMOD_MAX17048_FIRST_MONITOR_AT);
uint8_t VoltageDecimals = 3; // Number of decimal places in published voltage values
uint8_t PercentDecimals = 1; // Number of decimal places in published percent values
// string that are used multiple time (this will save some flash memory)
static const char _name[];
static const char _enabled[];
static const char _maxReadInterval[];
static const char _minReadInterval[];
static const char _HomeAssistantDiscovery[];
bool monitorFound = false;
bool firstReadComplete = false;
bool initDone = false;
Adafruit_MAX17048 maxLipo;
float lastBattVoltage = -10;
float lastBattPercent = -1;
// MQTT and Home Assistant Variables
bool HomeAssistantDiscovery = false; // Publish Home Assistant Device Information
bool mqttInitialized = false;
void _mqttInitialize()
{
char mqttBatteryVoltageTopic[128];
char mqttBatteryPercentTopic[128];
snprintf_P(mqttBatteryVoltageTopic, 127, PSTR("%s/batteryVoltage"), mqttDeviceTopic);
snprintf_P(mqttBatteryPercentTopic, 127, PSTR("%s/batteryPercent"), mqttDeviceTopic);
if (HomeAssistantDiscovery) {
_createMqttSensor(F("BatteryVoltage"), mqttBatteryVoltageTopic, "voltage", F("V"));
_createMqttSensor(F("BatteryPercent"), mqttBatteryPercentTopic, "battery", F("%"));
}
}
void _createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement)
{
String t = String(F("homeassistant/sensor/")) + mqttClientID + F("/") + name + F("/config");
StaticJsonDocument<600> doc;
doc[F("name")] = String(serverDescription) + " " + name;
doc[F("state_topic")] = topic;
doc[F("unique_id")] = String(mqttClientID) + name;
if (unitOfMeasurement != "")
doc[F("unit_of_measurement")] = unitOfMeasurement;
if (deviceClass != "")
doc[F("device_class")] = deviceClass;
doc[F("expire_after")] = 1800;
JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device
device[F("name")] = serverDescription;
device[F("identifiers")] = "wled-sensor-" + String(mqttClientID);
device[F("manufacturer")] = F("WLED");
device[F("model")] = F("FOSS");
device[F("sw_version")] = versionString;
String temp;
serializeJson(doc, temp);
DEBUG_PRINTLN(t);
DEBUG_PRINTLN(temp);
mqtt->publish(t.c_str(), 0, true, temp.c_str());
}
void publishMqtt(const char *topic, const char* state) {
#ifndef WLED_DISABLE_MQTT
//Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED){
char subuf[128];
snprintf_P(subuf, 127, PSTR("%s/%s"), mqttDeviceTopic, topic);
mqtt->publish(subuf, 0, false, state);
}
#endif
}
public:
inline void enable(bool enable) { enabled = enable; }
inline bool isEnabled() { return enabled; }
void setup() {
// do your set-up here
if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; }
monitorFound = maxLipo.begin();
initDone = true;
}
void loop() {
// if usermod is disabled or called during strip updating just exit
// NOTE: on very long strips strip.isUpdating() may always return true so update accordingly
if (!enabled || strip.isUpdating()) return;
unsigned long now = millis();
if (now - lastCheck < minReadingInterval) { return; }
bool shouldUpdate = now - lastSend > maxReadingInterval;
float battVoltage = maxLipo.cellVoltage();
float battPercent = maxLipo.cellPercent();
lastCheck = millis();
firstReadComplete = true;
if (shouldUpdate)
{
lastBattVoltage = roundf(battVoltage * powf(10, VoltageDecimals)) / powf(10, VoltageDecimals);
lastBattPercent = roundf(battPercent * powf(10, PercentDecimals)) / powf(10, PercentDecimals);
lastSend = millis();
publishMqtt("batteryVoltage", String(lastBattVoltage, VoltageDecimals).c_str());
publishMqtt("batteryPercent", String(lastBattPercent, PercentDecimals).c_str());
DEBUG_PRINTLN(F("Battery Voltage: ") + String(lastBattVoltage, VoltageDecimals) + F("V"));
DEBUG_PRINTLN(F("Battery Percent: ") + String(lastBattPercent, PercentDecimals) + F("%"));
}
}
void onMqttConnect(bool sessionPresent)
{
if (WLED_MQTT_CONNECTED && !mqttInitialized)
{
_mqttInitialize();
mqttInitialized = true;
}
}
inline float getBatteryVoltageV() {
return (float) lastBattVoltage;
}
inline float getBatteryPercent() {
return (float) lastBattPercent;
}
void addToJsonInfo(JsonObject& root)
{
// if "u" object does not exist yet wee need to create it
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
JsonArray battery_json = user.createNestedArray(F("Battery Monitor"));
if (!enabled) {
battery_json.add(F("Disabled"));
}
else if(!monitorFound) {
battery_json.add(F("MAX17048 Not Found"));
}
else if (!firstReadComplete) {
// if we haven't read the sensor yet, let the user know
// that we are still waiting for the first measurement
battery_json.add((USERMOD_MAX17048_FIRST_MONITOR_AT - millis()) / 1000);
battery_json.add(F(" sec until read"));
} else {
battery_json.add(F("Enabled"));
JsonArray voltage_json = user.createNestedArray(F("Battery Voltage"));
voltage_json.add(lastBattVoltage);
voltage_json.add(F("V"));
JsonArray percent_json = user.createNestedArray(F("Battery Percent"));
percent_json.add(lastBattPercent);
percent_json.add(F("%"));
}
}
void addToJsonState(JsonObject& root)
{
JsonObject usermod = root[FPSTR(_name)];
if (usermod.isNull())
{
usermod = root.createNestedObject(FPSTR(_name));
}
usermod[FPSTR(_enabled)] = enabled;
}
void readFromJsonState(JsonObject& root)
{
JsonObject usermod = root[FPSTR(_name)];
if (!usermod.isNull())
{
if (usermod[FPSTR(_enabled)].is<bool>())
{
enabled = usermod[FPSTR(_enabled)].as<bool>();
}
}
}
void addToConfig(JsonObject& root)
{
JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled;
top[FPSTR(_maxReadInterval)] = maxReadingInterval;
top[FPSTR(_minReadInterval)] = minReadingInterval;
top[FPSTR(_HomeAssistantDiscovery)] = HomeAssistantDiscovery;
DEBUG_PRINT(F(_name));
DEBUG_PRINTLN(F(" config saved."));
}
bool readFromConfig(JsonObject& root)
{
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
DEBUG_PRINT(F(_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
bool configComplete = !top.isNull();
configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled);
configComplete &= getJsonValue(top[FPSTR(_maxReadInterval)], maxReadingInterval, USERMOD_MAX17048_MAX_MONITOR_INTERVAL);
configComplete &= getJsonValue(top[FPSTR(_minReadInterval)], minReadingInterval, USERMOD_MAX17048_MIN_MONITOR_INTERVAL);
configComplete &= getJsonValue(top[FPSTR(_HomeAssistantDiscovery)], HomeAssistantDiscovery, false);
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
// first run: reading from cfg.json
DEBUG_PRINTLN(F(" config loaded."));
} else {
DEBUG_PRINTLN(F(" config (re)loaded."));
// changing parameters from settings page
}
return configComplete;
}
uint16_t getId()
{
return USERMOD_ID_MAX17048;
}
};
// add more strings here to reduce flash memory usage
const char Usermod_MAX17048::_name[] PROGMEM = "Adafruit MAX17048 Battery Monitor";
const char Usermod_MAX17048::_enabled[] PROGMEM = "enabled";
const char Usermod_MAX17048::_maxReadInterval[] PROGMEM = "max-read-interval-ms";
const char Usermod_MAX17048::_minReadInterval[] PROGMEM = "min-read-interval-ms";
const char Usermod_MAX17048::_HomeAssistantDiscovery[] PROGMEM = "HomeAssistantDiscovery";

View File

@ -57,6 +57,9 @@ class UsermodTemperature : public Usermod {
static const char _parasite[]; static const char _parasite[];
static const char _parasitePin[]; static const char _parasitePin[];
static const char _domoticzIDX[]; static const char _domoticzIDX[];
static const char _sensor[];
static const char _temperature[];
static const char _Temperature[];
//Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013 //Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013
float readDallas(); float readDallas();
@ -191,9 +194,9 @@ void UsermodTemperature::publishHomeAssistantAutodiscovery() {
sprintf_P(buf, PSTR("%s Temperature"), serverDescription); sprintf_P(buf, PSTR("%s Temperature"), serverDescription);
json[F("name")] = buf; json[F("name")] = buf;
strcpy(buf, mqttDeviceTopic); strcpy(buf, mqttDeviceTopic);
strcat_P(buf, PSTR("/temperature")); strcat_P(buf, _Temperature);
json[F("state_topic")] = buf; json[F("state_topic")] = buf;
json[F("device_class")] = F("temperature"); json[F("device_class")] = FPSTR(_temperature);
json[F("unique_id")] = escapedMac.c_str(); json[F("unique_id")] = escapedMac.c_str();
json[F("unit_of_measurement")] = F("°C"); json[F("unit_of_measurement")] = F("°C");
payload_size = serializeJson(json, json_str); payload_size = serializeJson(json, json_str);
@ -272,7 +275,7 @@ void UsermodTemperature::loop() {
// dont publish super low temperature as the graph will get messed up // dont publish super low temperature as the graph will get messed up
// the DallasTemperature library returns -127C or -196.6F when problem // the DallasTemperature library returns -127C or -196.6F when problem
// reading the sensor // reading the sensor
strcat_P(subuf, PSTR("/temperature")); strcat_P(subuf, _Temperature);
mqtt->publish(subuf, 0, false, String(getTemperatureC()).c_str()); mqtt->publish(subuf, 0, false, String(getTemperatureC()).c_str());
strcat_P(subuf, PSTR("_f")); strcat_P(subuf, PSTR("_f"));
mqtt->publish(subuf, 0, false, String(getTemperatureF()).c_str()); mqtt->publish(subuf, 0, false, String(getTemperatureF()).c_str());
@ -335,9 +338,9 @@ void UsermodTemperature::addToJsonInfo(JsonObject& root) {
temp.add(getTemperature()); temp.add(getTemperature());
temp.add(getTemperatureUnit()); temp.add(getTemperatureUnit());
JsonObject sensor = root[F("sensor")]; JsonObject sensor = root[FPSTR(_sensor)];
if (sensor.isNull()) sensor = root.createNestedObject(F("sensor")); if (sensor.isNull()) sensor = root.createNestedObject(FPSTR(_sensor));
temp = sensor.createNestedArray(F("temperature")); temp = sensor.createNestedArray(FPSTR(_temperature));
temp.add(getTemperature()); temp.add(getTemperature());
temp.add(getTemperatureUnit()); temp.add(getTemperatureUnit());
} }
@ -367,7 +370,7 @@ void UsermodTemperature::addToConfig(JsonObject &root) {
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
top[FPSTR(_enabled)] = enabled; top[FPSTR(_enabled)] = enabled;
top["pin"] = temperaturePin; // usermodparam top["pin"] = temperaturePin; // usermodparam
top["degC"] = degC; // usermodparam top[F("degC")] = degC; // usermodparam
top[FPSTR(_readInterval)] = readingInterval / 1000; top[FPSTR(_readInterval)] = readingInterval / 1000;
top[FPSTR(_parasite)] = parasite; top[FPSTR(_parasite)] = parasite;
top[FPSTR(_parasitePin)] = parasitePin; top[FPSTR(_parasitePin)] = parasitePin;
@ -393,7 +396,7 @@ bool UsermodTemperature::readFromConfig(JsonObject &root) {
enabled = top[FPSTR(_enabled)] | enabled; enabled = top[FPSTR(_enabled)] | enabled;
newTemperaturePin = top["pin"] | newTemperaturePin; newTemperaturePin = top["pin"] | newTemperaturePin;
degC = top["degC"] | degC; degC = top[F("degC")] | degC;
readingInterval = top[FPSTR(_readInterval)] | readingInterval/1000; readingInterval = top[FPSTR(_readInterval)] | readingInterval/1000;
readingInterval = min(120,max(10,(int)readingInterval)) * 1000; // convert to ms readingInterval = min(120,max(10,(int)readingInterval)) * 1000; // convert to ms
parasite = top[FPSTR(_parasite)] | parasite; parasite = top[FPSTR(_parasite)] | parasite;
@ -444,3 +447,6 @@ const char UsermodTemperature::_readInterval[] PROGMEM = "read-interval-s";
const char UsermodTemperature::_parasite[] PROGMEM = "parasite-pwr"; const char UsermodTemperature::_parasite[] PROGMEM = "parasite-pwr";
const char UsermodTemperature::_parasitePin[] PROGMEM = "parasite-pwr-pin"; const char UsermodTemperature::_parasitePin[] PROGMEM = "parasite-pwr-pin";
const char UsermodTemperature::_domoticzIDX[] PROGMEM = "domoticz-idx"; const char UsermodTemperature::_domoticzIDX[] PROGMEM = "domoticz-idx";
const char UsermodTemperature::_sensor[] PROGMEM = "sensor";
const char UsermodTemperature::_temperature[] PROGMEM = "temperature";
const char UsermodTemperature::_Temperature[] PROGMEM = "/temperature";

View File

@ -0,0 +1,117 @@
/******************************************************************************
* @file : gridbw.h
* @brief : contains the tetris grid as binary so black and white version
******************************************************************************
* @attention
*
* Copyright (c) muebau 2023
* All rights reserved.</center></h2>
*
******************************************************************************
*/
#ifndef __GRIDBW_H__
#define __GRIDBW_H__
#include <iterator>
#include <vector>
#include "pieces.h"
using namespace std;
class GridBW
{
private:
public:
uint8_t width;
uint8_t height;
std::vector<uint32_t> pixels;
GridBW(uint8_t width, uint8_t height):
width(width),
height(height),
pixels(height)
{
if (width > 32)
{
throw std::invalid_argument("maximal width is 32");
}
}
void placePiece(Piece* piece, uint8_t x, uint8_t y)
{
for (uint8_t row = 4 - piece->getRotation().height; row < 4; row++)
{
pixels[y + (row - (4 - piece->getRotation().height))] |= piece->getGridRow(x, row, width);
}
}
void erasePiece(Piece* piece, uint8_t x, uint8_t y)
{
for (uint8_t row = 4 - piece->getRotation().height; row < 4; row++)
{
pixels[y + (row - (4 - piece->getRotation().height))] &= ~piece->getGridRow(x, row, width);
}
}
bool noCollision(Piece* piece, uint8_t x, uint8_t y)
{
//if it touches a wall it is a collision
if (x > (this->width - piece->getRotation().width) || y > this->height - piece->getRotation().height)
{
return false;
}
for (uint8_t row = 4 - piece->getRotation().height; row < 4; row++)
{
if (piece->getGridRow(x, row, width) & pixels[y + (row - (4 - piece->getRotation().height))])
{
return false;
}
}
return true;
}
void findLandingPosition(Piece* piece)
{
// move down until the piece bumps into some occupied pixels or the 'wall'
while (noCollision(piece, piece->x, piece->landingY))
{
piece->landingY++;
}
//at this point the positon is 'in the wall' or 'over some occupied pixel'
//so the previous position was the last correct one (clamped to 0 as minimum).
piece->landingY = piece->landingY > 0 ? piece->landingY - 1 : 0;
}
void cleanupFullLines()
{
uint8_t offset = 0;
//from "height - 1" to "0", so from bottom row to top
for (uint8_t row = height; row-- > 0; )
{
//full line?
if (isLineFull(row))
{
offset++;
pixels[row] = 0x0;
continue;
}
if (offset > 0)
{
pixels[row + offset] = pixels[row];
pixels[row] = 0x0;
}
}
}
bool isLineFull(uint8_t y)
{
return pixels[y] == (uint32_t)((1 << width) - 1);
}
};
#endif /* __GRIDBW_H__ */

View File

@ -0,0 +1,132 @@
/******************************************************************************
* @file : gridcolor.h
* @brief : contains the tetris grid as 8bit indexed color version
******************************************************************************
* @attention
*
* Copyright (c) muebau 2023
* All rights reserved.</center></h2>
*
******************************************************************************
*/
#ifndef __GRIDCOLOR_H__
#define __GRIDCOLOR_H__
#include <stdint.h>
#include <stdbool.h>
#include <vector>
#include "gridbw.h"
#include "gridcolor.h"
using namespace std;
class GridColor
{
private:
public:
uint8_t width;
uint8_t height;
GridBW gridBW;
std::vector<uint8_t> pixels;
GridColor(uint8_t width, uint8_t height):
width(width),
height(height),
gridBW(width, height),
pixels(width* height)
{}
void clear()
{
for (uint8_t y = 0; y < height; y++)
{
gridBW.pixels[y] = 0x0;
for (int8_t x = 0; x < width; x++)
{
*getPixel(x, y) = 0;
}
}
}
void placePiece(Piece* piece, uint8_t x, uint8_t y)
{
for (uint8_t pieceY = 0; pieceY < piece->getRotation().height; pieceY++)
{
for (uint8_t pieceX = 0; pieceX < piece->getRotation().width; pieceX++)
{
if (piece->getPixel(pieceX, pieceY))
{
*getPixel(x + pieceX, y + pieceY) = piece->pieceData->colorIndex;
}
}
}
}
void erasePiece(Piece* piece, uint8_t x, uint8_t y)
{
for (uint8_t pieceY = 0; pieceY < piece->getRotation().height; pieceY++)
{
for (uint8_t pieceX = 0; pieceX < piece->getRotation().width; pieceX++)
{
if (piece->getPixel(pieceX, pieceY))
{
*getPixel(x + pieceX, y + pieceY) = 0;
}
}
}
}
void cleanupFullLines()
{
uint8_t offset = 0;
//from "height - 1" to "0", so from bottom row to top
for (uint8_t y = height; y-- > 0; )
{
if (gridBW.isLineFull(y))
{
offset++;
for (uint8_t x = 0; x < width; x++)
{
pixels[y * width + x] = 0;
}
continue;
}
if (offset > 0)
{
if (gridBW.pixels[y])
{
for (uint8_t x = 0; x < width; x++)
{
pixels[(y + offset) * width + x] = pixels[y * width + x];
pixels[y * width + x] = 0;
}
}
}
}
gridBW.cleanupFullLines();
}
uint8_t* getPixel(uint8_t x, uint8_t y)
{
return &pixels[y * width + x];
}
void sync()
{
for (uint8_t y = 0; y < height; y++)
{
gridBW.pixels[y] = 0x0;
for (int8_t x = 0; x < width; x++)
{
gridBW.pixels[y] <<= 1;
if (*getPixel(x, y) != 0)
{
gridBW.pixels[y] |= 0x1;
}
}
}
}
};
#endif /* __GRIDCOLOR_H__ */

View File

@ -0,0 +1,184 @@
/******************************************************************************
* @file : pieces.h
* @brief : contains the tetris pieces with their colors indecies
******************************************************************************
* @attention
*
* Copyright (c) muebau 2022
* All rights reserved.</center></h2>
*
******************************************************************************
*/
#ifndef __PIECES_H__
#define __PIECES_H__
#include <stdint.h>
#include <stdbool.h>
#include <bitset>
#include <cstddef>
#include <cassert>
#include <iostream>
#define numPieces 7
struct PieceRotation
{
uint8_t width;
uint8_t height;
uint16_t rows;
};
struct PieceData
{
uint8_t rotCount;
uint8_t colorIndex;
PieceRotation rotations[4];
};
PieceData piecesData[numPieces] = {
// I
{
2,
1,
{
{ 1, 4, 0b0001000100010001},
{ 4, 1, 0b0000000000001111}
}
},
// O
{
1,
2,
{
{ 2, 2, 0b0000000000110011}
}
},
// Z
{
2,
3,
{
{ 3, 2, 0b0000000001100011},
{ 2, 3, 0b0000000100110010}
}
},
// S
{
2,
4,
{
{ 3, 2, 0b0000000000110110},
{ 2, 3, 0b0000001000110001}
}
},
// L
{
4,
5,
{
{ 2, 3, 0b0000001000100011},
{ 3, 2, 0b0000000001110100},
{ 2, 3, 0b0000001100010001},
{ 3, 2, 0b0000000000010111}
}
},
// J
{
4,
6,
{
{ 2, 3, 0b0000000100010011},
{ 3, 2, 0b0000000001000111},
{ 2, 3, 0b0000001100100010},
{ 3, 2, 0b0000000001110001}
}
},
// T
{
4,
7,
{
{ 3, 2, 0b0000000001110010},
{ 2, 3, 0b0000000100110001},
{ 3, 2, 0b0000000000100111},
{ 2, 3, 0b0000001000110010}
}
},
};
class Piece
{
private:
public:
uint8_t x;
uint8_t y;
PieceData* pieceData;
uint8_t rotation;
uint8_t landingY;
Piece(uint8_t pieceIndex = 0):
x(0),
y(0),
rotation(0),
landingY(0)
{
this->pieceData = &piecesData[pieceIndex];
}
void reset()
{
this->rotation = 0;
this->x = 0;
this->y = 0;
this->landingY = 0;
}
uint32_t getGridRow(uint8_t x, uint8_t y, uint8_t width)
{
if (x < width)
{
//shift the row with the "top-left" position to the "x" position
auto shiftx = (width - 1) - x;
auto topleftx = (getRotation().width - 1);
auto finalShift = shiftx - topleftx;
auto row = getRow(y);
auto finalResult = row << finalShift;
return finalResult;
}
return 0xffffffff;
}
uint8_t getRow(uint8_t y)
{
if (y < 4)
{
return (getRotation().rows >> (12 - (4 * y))) & 0xf;
}
return 0xf;
}
bool getPixel(uint8_t x, uint8_t y)
{
if(x > getRotation().width - 1 || y > getRotation().height - 1 )
{
return false;
}
if (x < 4 && y < 4)
{
return (getRow((4 - getRotation().height) + y) >> (3 - ((4 - getRotation().width) + x))) & 0x1;
}
return false;
}
PieceRotation getRotation()
{
return this->pieceData->rotations[rotation];
}
};
#endif /* __PIECES_H__ */

View File

@ -0,0 +1,64 @@
/******************************************************************************
* @file : rating.h
* @brief : contains the tetris rating of a grid
******************************************************************************
* @attention
*
* Copyright (c) muebau 2022
* All rights reserved.</center></h2>
*
******************************************************************************
*/
#ifndef __RATING_H__
#define __RATING_H__
#include <stdint.h>
#include <float.h>
#include <stdbool.h>
#include <math.h>
#include <vector>
#include "rating.h"
using namespace std;
class Rating
{
private:
public:
uint8_t minHeight;
uint8_t maxHeight;
uint16_t holes;
uint8_t fullLines;
uint16_t bumpiness;
uint16_t aggregatedHeight;
double score;
uint8_t width;
std::vector<uint8_t> lineHights;
Rating(uint8_t width):
width(width),
lineHights(width)
{
reset();
}
void reset()
{
this->minHeight = 0;
this->maxHeight = 0;
for (uint8_t line = 0; line < this->width; line++)
{
this->lineHights[line] = 0;
}
this->holes = 0;
this->fullLines = 0;
this->bumpiness = 0;
this->aggregatedHeight = 0;
this->score = -DBL_MAX;
}
};
#endif /* __RATING_H__ */

View File

@ -0,0 +1,33 @@
# Tetris AI effect usermod
This usermod brings you a effect brings a self playing Tetris game. The mod needs version 0.14 or above as it is based on matrix support. The effect was tested on an ESP32 with a WS2812B 16x16 matrix.
Version 1.0
## Installation
Just activate the usermod with `-D USERMOD_TETRISAI` and the effect will become available under the name 'Tetris AI'.
## Usage
It is best to set the background color to black, the border color to light grey and the game over color (foreground) to dark grey.
### Sliders and boxes
#### Sliders
* speed: speed the game plays
* look ahead: how many pieces is the AI allowed to know the next pieces (0 - 2)
* intelligence: how good the AI will play
* Rotate color: make the colors shift (rotate) every few cicles
* Mistakes free: how many good moves between mistakes (if activated)
#### Checkboxes
* show next: if true a space of 5 pixels from the right is used to show the next pieces. The whole segment is used for the grid otherwise.
* show border: if true an additional column of 1 pixel is used to draw a border between the grid and the next pieces
* mistakes: if true the worst instead of the best move is choosen every few moves (read above)
## Best results
If the speed is set to be a little bit faster than a good human could play with maximal intelligence and very few mistakes it makes people furious/happy at a party.

View File

@ -0,0 +1,302 @@
/******************************************************************************
* @file : ai.h
* @brief : contains the heuristic
******************************************************************************
* @attention
*
* Copyright (c) muebau 2023
* All rights reserved.</center></h2>
*
******************************************************************************
*/
#ifndef __AI_H__
#define __AI_H__
#include "gridbw.h"
#include "rating.h"
using namespace std;
class TetrisAI
{
private:
public:
double aHeight;
double fullLines;
double holes;
double bumpiness;
bool findWorstMove = false;
uint8_t countOnes(uint32_t vector)
{
uint8_t count = 0;
while (vector)
{
vector &= (vector - 1);
count++;
}
return count;
}
void updateRating(GridBW grid, Rating* rating)
{
rating->minHeight = 0;
rating->maxHeight = 0;
rating->holes = 0;
rating->fullLines = 0;
rating->bumpiness = 0;
rating->aggregatedHeight = 0;
fill(rating->lineHights.begin(), rating->lineHights.end(), 0);
uint32_t columnvector = 0x0;
uint32_t lastcolumnvector = 0x0;
for (uint8_t row = 0; row < grid.height; row++)
{
columnvector |= grid.pixels[row];
//first (highest) column makes it
if (rating->maxHeight == 0 && columnvector)
{
rating->maxHeight = grid.height - row;
}
//if column vector is full we found the minimal height (or it stays zero)
if (rating->minHeight == 0 && (columnvector == (uint32_t)((1 << grid.width) - 1)))
{
rating->minHeight = grid.height - row;
}
//line full if all ones in mask :-)
if (grid.isLineFull(row))
{
rating->fullLines++;
}
//holes are basically a XOR with the "full" columns
rating->holes += countOnes(columnvector ^ grid.pixels[row]);
//calculate the difference (XOR) between the current column vector and the last one
uint32_t columnDelta = columnvector ^ lastcolumnvector;
//process every new column
uint8_t index = 0;
while (columnDelta)
{
//if this is a new column
if (columnDelta & 0x1)
{
//update hight of this column
rating->lineHights[(grid.width - 1) - index] = grid.height - row;
// update aggregatedHeight
rating->aggregatedHeight += grid.height - row;
}
index++;
columnDelta >>= 1;
}
lastcolumnvector = columnvector;
}
//compare every two columns to get the difference and add them up
for (uint8_t column = 1; column < grid.width; column++)
{
rating->bumpiness += abs(rating->lineHights[column - 1] - rating->lineHights[column]);
}
rating->score = (aHeight * (rating->aggregatedHeight)) + (fullLines * (rating->fullLines)) + (holes * (rating->holes)) + (bumpiness * (rating->bumpiness));
}
TetrisAI(): TetrisAI(-0.510066, 0.760666, -0.35663, -0.184483)
{}
TetrisAI(double aHeight, double fullLines, double holes, double bumpiness):
aHeight(aHeight),
fullLines(fullLines),
holes(holes),
bumpiness(bumpiness)
{}
void findBestMove(GridBW grid, Piece *piece)
{
vector<Piece> pieces = {*piece};
findBestMove(grid, &pieces);
*piece = pieces[0];
}
void findBestMove(GridBW grid, std::vector<Piece> *pieces)
{
findBestMove(grid, pieces->begin(), pieces->end());
}
void findBestMove(GridBW grid, std::vector<Piece>::iterator start, std::vector<Piece>::iterator end)
{
Rating bestRating(grid.width);
findBestMove(grid, start, end, &bestRating);
}
void findBestMove(GridBW grid, std::vector<Piece>::iterator start, std::vector<Piece>::iterator end, Rating* bestRating)
{
grid.cleanupFullLines();
Rating curRating(grid.width);
Rating deeperRating(grid.width);
Piece piece = *start;
// for every rotation of the piece
for (piece.rotation = 0; piece.rotation < piece.pieceData->rotCount; piece.rotation++)
{
// put piece to top left corner
piece.x = 0;
piece.y = 0;
//test for every column
for (piece.x = 0; piece.x <= grid.width - piece.getRotation().width; piece.x++)
{
//todo optimise by the use of the previous grids height
piece.landingY = 0;
//will set landingY to final position
grid.findLandingPosition(&piece);
// draw piece
grid.placePiece(&piece, piece.x, piece.landingY);
if(start == end - 1)
{
//at the deepest level
updateRating(grid, &curRating);
}
else
{
//go deeper to take another piece into account
findBestMove(grid, start + 1, end, &deeperRating);
curRating = deeperRating;
}
// eraese piece
grid.erasePiece(&piece, piece.x, piece.landingY);
if(findWorstMove)
{
//init rating for worst
if(bestRating->score == -DBL_MAX)
{
bestRating->score = DBL_MAX;
}
// update if we found a worse one
if (bestRating->score > curRating.score)
{
*bestRating = curRating;
(*start) = piece;
}
}
else
{
// update if we found a better one
if (bestRating->score < curRating.score)
{
*bestRating = curRating;
(*start) = piece;
}
}
}
}
}
bool findBestMoveNonBlocking(GridBW grid, std::vector<Piece>::iterator start, std::vector<Piece>::iterator end, Rating* bestRating)
{
//vector with pieces
//for every piece
//for every
switch (expression)
{
case INIT:
break;
default:
break;
}
}
bool findBestMoveNonBlocking(GridBW grid, std::vector<Piece>::iterator start, std::vector<Piece>::iterator end, Rating* bestRating)
{
//INIT
grid.cleanupFullLines();
Rating curRating(grid.width);
Rating deeperRating(grid.width);
Piece piece = *start;
// for every rotation of the piece
piece.rotation = 0;
//HANDLE
while (piece.rotation < piece.pieceData->rotCount)
{
// put piece to top left corner
piece.x = 0;
piece.y = 0;
//test for every column
piece.x = 0;
while (piece.x <= grid.width - piece.getRotation().width)
{
//todo optimise by the use of the previous grids height
piece.landingY = 0;
//will set landingY to final position
grid.findLandingPosition(&piece);
// draw piece
grid.placePiece(&piece, piece.x, piece.landingY);
if(start == end - 1)
{
//at the deepest level
updateRating(grid, &curRating);
}
else
{
//go deeper to take another piece into account
findBestMove(grid, start + 1, end, &deeperRating);
curRating = deeperRating;
}
// eraese piece
grid.erasePiece(&piece, piece.x, piece.landingY);
if(findWorstMove)
{
//init rating for worst
if(bestRating->score == -DBL_MAX)
{
bestRating->score = DBL_MAX;
}
// update if we found a worse one
if (bestRating->score > curRating.score)
{
*bestRating = curRating;
(*start) = piece;
}
}
else
{
// update if we found a better one
if (bestRating->score < curRating.score)
{
*bestRating = curRating;
(*start) = piece;
}
}
piece.x++;
}
piece.rotation++;
}
//EXIT
return true;
}
};
#endif /* __AI_H__ */

View File

@ -0,0 +1,150 @@
/******************************************************************************
* @file : tetrisaigame.h
* @brief : main tetris functions
******************************************************************************
* @attention
*
* Copyright (c) muebau 2022
* All rights reserved.</center></h2>
*
******************************************************************************
*/
#ifndef __TETRISAIGAME_H__
#define __TETRISAIGAME_H__
#include <stdint.h>
#include <stdbool.h>
#include <vector>
#include "pieces.h"
#include "gridcolor.h"
#include "tetrisbag.h"
#include "tetrisai.h"
using namespace std;
class TetrisAIGame
{
private:
bool animateFallOfPiece(Piece* piece, bool skip)
{
if (skip || piece->y >= piece->landingY)
{
piece->y = piece->landingY;
grid.gridBW.placePiece(piece, piece->x, piece->landingY);
grid.placePiece(piece, piece->x, piece->y);
return false;
}
else
{
// eraese last drawing
grid.erasePiece(piece, piece->x, piece->y);
//move piece down
piece->y++;
// draw piece
grid.placePiece(piece, piece->x, piece->y);
return true;
}
}
public:
uint8_t width;
uint8_t height;
uint8_t nLookAhead;
TetrisBag bag;
GridColor grid;
TetrisAI ai;
Piece curPiece;
PieceData* piecesData;
enum States { INIT, TEST_GAME_OVER, GET_NEXT_PIECE, FIND_BEST_MOVE, ANIMATE_MOVE, ANIMATE_GAME_OVER } state = INIT;
TetrisAIGame(uint8_t width, uint8_t height, uint8_t nLookAhead, PieceData* piecesData, uint8_t nPieces):
width(width),
height(height),
nLookAhead(nLookAhead),
bag(nPieces, 1, nLookAhead),
grid(width, height + 4),
ai(),
piecesData(piecesData)
{
}
void nextPiece()
{
grid.cleanupFullLines();
bag.queuePiece();
}
void findBestMove()
{
ai.findBestMove(grid.gridBW, &bag.piecesQueue);
}
bool animateFall(bool skip)
{
return animateFallOfPiece(&(bag.piecesQueue[0]), skip);
}
bool isGameOver()
{
//if there is something in the 4 lines of the hidden area the game is over
return grid.gridBW.pixels[0] || grid.gridBW.pixels[1] || grid.gridBW.pixels[2] || grid.gridBW.pixels[3];
}
void poll()
{
switch (state)
{
case INIT:
reset();
state = TEST_GAME_OVER;
break;
case TEST_GAME_OVER:
if (isGameOver())
{
state = ANIMATE_GAME_OVER;
}
else
{
state = GET_NEXT_PIECE;
}
break;
case GET_NEXT_PIECE:
nextPiece();
state = FIND_BEST_MOVE;
break;
case FIND_BEST_MOVE:
findBestMove();
state = ANIMATE_MOVE;
break;
case ANIMATE_MOVE:
if (!animateFall(false))
{
state = TEST_GAME_OVER;
}
break;
case ANIMATE_GAME_OVER:
static auto curPixel = grid.pixels.size();
grid.pixels[curPixel] = 254;
if (curPixel == 0)
{
state = INIT;
curPixel = grid.pixels.size();
}
curPixel--;
break;
}
}
void reset()
{
grid.clear();
bag.init();
}
};
#endif /* __TETRISAIGAME_H__ */

View File

@ -0,0 +1,100 @@
/******************************************************************************
* @file : tetrisbag.h
* @brief : the tetris implementation of a random piece generator
******************************************************************************
* @attention
*
* Copyright (c) muebau 2022
* All rights reserved.</center></h2>
*
******************************************************************************
*/
#ifndef __TETRISBAG_H__
#define __TETRISBAG_H__
#include <stdint.h>
#include <vector>
#include <algorithm>
#include "tetrisbag.h"
class TetrisBag
{
private:
public:
uint8_t nPieces;
uint8_t nBagLength;
uint8_t bagIdx;
std::vector<uint8_t> bag;
std::vector<Piece> piecesQueue;
TetrisBag(uint8_t nPieces, uint8_t nBagLength, uint8_t queueLength):
nPieces(nPieces),
nBagLength(nBagLength),
bag(nPieces * nBagLength),
piecesQueue(queueLength)
{
init();
}
void init()
{
//will shuffle the bag at first use
bagIdx = nPieces - 1;
for (uint8_t bagIndex = 0; bagIndex < nPieces * nBagLength; bagIndex++)
{
bag[bagIndex] = bagIndex % nPieces;
}
//will init the queue
for (uint8_t index = 0; index < piecesQueue.size(); index++)
{
queuePiece();
}
}
void shuffleBag()
{
uint8_t temp;
uint8_t swapIdx;
for (int index = nPieces - 1; index > 0; index--)
{
//get candidate to swap
swapIdx = rand() % index;
//swap it!
temp = bag[swapIdx];
bag[swapIdx] = bag[index];
bag[index] = temp;
}
}
Piece getNextPiece()
{
bagIdx++;
if (bagIdx >= nPieces)
{
shuffleBag();
bagIdx = 0;
}
return Piece(bag[bagIdx]);
}
void queuePiece()
{
//move vector to left
std::rotate(piecesQueue.begin(), piecesQueue.begin() + 1, piecesQueue.end());
piecesQueue[piecesQueue.size() - 1] = getNextPiece();
}
void queuePiece(uint8_t idx)
{
//move vector to left
std::rotate(piecesQueue.begin(), piecesQueue.begin() + 1, piecesQueue.end());
piecesQueue[piecesQueue.size() - 1] = Piece(idx % nPieces);
}
};
#endif /* __TETRISBAG_H__ */

View File

@ -0,0 +1,222 @@
#pragma once
#include "wled.h"
#include "FX.h"
#include "fcn_declare.h"
#include "tetrisaigame.h"
// By: muebau
typedef struct TetrisAI_data
{
unsigned long lastTime = 0;
TetrisAIGame tetris;
uint8_t intelligence;
uint8_t rotate;
bool showNext;
bool showBorder;
uint8_t colorOffset;
uint8_t colorInc;
uint8_t mistaceCountdown;
} tetrisai_data;
void drawGrid(TetrisAIGame* tetris, TetrisAI_data* tetrisai_data)
{
SEGMENT.fill(SEGCOLOR(1));
//GRID
for (auto index_y = 4; index_y < tetris->grid.height; index_y++)
{
for (auto index_x = 0; index_x < tetris->grid.width; index_x++)
{
CRGB color;
if (*tetris->grid.getPixel(index_x, index_y) == 0)
{
//BG color
color = SEGCOLOR(1);
}
//game over animation
else if(*tetris->grid.getPixel(index_x, index_y) == 254)
{
//use fg
color = SEGCOLOR(0);
}
else
{
//spread the color over the whole palette
uint8_t colorIndex = *tetris->grid.getPixel(index_x, index_y) * 32;
colorIndex += tetrisai_data->colorOffset;
color = ColorFromPalette(SEGPALETTE, colorIndex, 255, NOBLEND);
}
SEGMENT.setPixelColorXY(index_x, index_y - 4, color);
}
}
tetrisai_data->colorOffset += tetrisai_data->colorInc;
//NEXT PIECE AREA
if (tetrisai_data->showNext)
{
//BORDER
if (tetrisai_data->showBorder)
{
//draw a line 6 pixels from right with the border color
for (auto index_y = 0; index_y < SEGMENT.virtualHeight(); index_y++)
{
SEGMENT.setPixelColorXY(SEGMENT.virtualWidth() - 6, index_y, SEGCOLOR(2));
}
}
//NEXT PIECE
int piecesOffsetX = SEGMENT.virtualWidth() - 4;
int piecesOffsetY = 1;
for (uint8_t nextPieceIdx = 1; nextPieceIdx < tetris->nLookAhead; nextPieceIdx++)
{
uint8_t pieceNbrOffsetY = (nextPieceIdx - 1) * 5;
Piece piece(tetris->bag.piecesQueue[nextPieceIdx]);
for (uint8_t pieceY = 0; pieceY < piece.getRotation().height; pieceY++)
{
for (uint8_t pieceX = 0; pieceX < piece.getRotation().width; pieceX++)
{
if (piece.getPixel(pieceX, pieceY))
{
uint8_t colIdx = ((piece.pieceData->colorIndex * 32) + tetrisai_data->colorOffset);
SEGMENT.setPixelColorXY(piecesOffsetX + pieceX, piecesOffsetY + pieceNbrOffsetY + pieceY, ColorFromPalette(SEGPALETTE, colIdx, 255, NOBLEND));
}
}
}
}
}
}
////////////////////////////
// 2D Tetris AI //
////////////////////////////
uint16_t mode_2DTetrisAI()
{
if (!strip.isMatrix || !SEGENV.allocateData(sizeof(tetrisai_data)))
{
// not a 2D set-up
SEGMENT.fill(SEGCOLOR(0));
return 350;
}
TetrisAI_data* tetrisai_data = reinterpret_cast<TetrisAI_data*>(SEGENV.data);
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
//range 0 - 1024ms => 1024/255 ~ 4
uint16_t msDelayMove = 1024 - (4 * SEGMENT.speed);
int16_t msDelayGameOver = msDelayMove / 4;
//range 0 - 2 (not including current)
uint8_t nLookAhead = SEGMENT.intensity ? (SEGMENT.intensity >> 7) + 2 : 1;
//range 0 - 16
tetrisai_data->colorInc = SEGMENT.custom2 >> 4;
if (!tetrisai_data->tetris || (tetrisai_data->tetris.nLookAhead != nLookAhead
|| tetrisai_data->showNext != SEGMENT.check1
|| tetrisai_data->showBorder != SEGMENT.check2
)
)
{
tetrisai_data->showNext = SEGMENT.check1;
tetrisai_data->showBorder = SEGMENT.check2;
//not more than 32 as this is the limit of this implementation
uint8_t gridWidth = cols < 32 ? cols : 32;
uint8_t gridHeight = rows;
// do we need space for the 'next' section?
if (tetrisai_data->showNext)
{
// make space for the piece and one pixel of space
gridWidth = gridWidth - 5;
// do we need space for a border?
if (tetrisai_data->showBorder)
{
gridWidth = gridWidth - 1;
}
}
tetrisai_data->tetris = TetrisAIGame(gridWidth, gridHeight, nLookAhead, piecesData, numPieces);
SEGMENT.fill(SEGCOLOR(1));
}
if (tetrisai_data->intelligence != SEGMENT.custom1)
{
tetrisai_data->intelligence = SEGMENT.custom1;
double dui = 0.2 - (0.2 * (tetrisai_data->intelligence / 255.0));
tetrisai_data->tetris.ai.aHeight = -0.510066 + dui;
tetrisai_data->tetris.ai.fullLines = 0.760666 - dui;
tetrisai_data->tetris.ai.holes = -0.35663 + dui;
tetrisai_data->tetris.ai.bumpiness = -0.184483 + dui;
}
if (tetrisai_data->tetris.state == TetrisAIGame::ANIMATE_MOVE)
{
if (millis() - tetrisai_data->lastTime > msDelayMove)
{
drawGrid(&tetrisai_data->tetris, tetrisai_data);
tetrisai_data->lastTime = millis();
tetrisai_data->tetris.poll();
}
}
else if (tetrisai_data->tetris.state == TetrisAIGame::ANIMATE_GAME_OVER)
{
if (millis() - tetrisai_data->lastTime > msDelayGameOver)
{
drawGrid(&tetrisai_data->tetris, tetrisai_data);
tetrisai_data->lastTime = millis();
tetrisai_data->tetris.poll();
}
}
else if (tetrisai_data->tetris.state == TetrisAIGame::FIND_BEST_MOVE)
{
if (SEGMENT.check3)
{
if(tetrisai_data->mistaceCountdown == 0)
{
tetrisai_data->tetris.ai.findWorstMove = true;
tetrisai_data->tetris.poll();
tetrisai_data->tetris.ai.findWorstMove = false;
tetrisai_data->mistaceCountdown = SEGMENT.custom3;
}
tetrisai_data->mistaceCountdown--;
}
tetrisai_data->tetris.poll();
}
else
{
tetrisai_data->tetris.poll();
}
return FRAMETIME;
} // mode_2DTetrisAI()
static const char _data_FX_MODE_2DTETRISAI[] PROGMEM = "Tetris AI@!,Look ahead,Intelligence,Rotate color,Mistake free,Show next,Border,Mistakes;Game Over,!,Border;!;2;sx=127,ix=64,c1=255,c2=0,c3=31,o1=1,o2=1,o3=0,pal=11";
class TetrisAIUsermod : public Usermod
{
private:
public:
void setup()
{
strip.addEffect(255, &mode_2DTetrisAI, _data_FX_MODE_2DTETRISAI);
}
void loop()
{
}
uint16_t getId()
{
return USERMOD_ID_TETRISAI;
}
};

View File

@ -183,31 +183,20 @@ constexpr uint16_t samplesFFT_2 = 256; // meaningfull part of FFT resul
// These are the input and output vectors. Input vectors receive computed results from FFT. // These are the input and output vectors. Input vectors receive computed results from FFT.
static float vReal[samplesFFT] = {0.0f}; // FFT sample inputs / freq output - these are our raw result bins static float vReal[samplesFFT] = {0.0f}; // FFT sample inputs / freq output - these are our raw result bins
static float vImag[samplesFFT] = {0.0f}; // imaginary parts static float vImag[samplesFFT] = {0.0f}; // imaginary parts
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
static float windowWeighingFactors[samplesFFT] = {0.0f}; static float windowWeighingFactors[samplesFFT] = {0.0f};
#endif
// Create FFT object // Create FFT object
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT // lib_deps += https://github.com/kosme/arduinoFFT#develop @ 1.9.2
// lib_deps += https://github.com/kosme/arduinoFFT#develop @ 1.9.2 // these options actually cause slow-downs on all esp32 processors, don't use them.
// these options actually cause slow-downs on all esp32 processors, don't use them. // #define FFT_SPEED_OVER_PRECISION // enables use of reciprocals (1/x etc) - not faster on ESP32
// #define FFT_SPEED_OVER_PRECISION // enables use of reciprocals (1/x etc) - not faster on ESP32 // #define FFT_SQRT_APPROXIMATION // enables "quake3" style inverse sqrt - slower on ESP32
// #define FFT_SQRT_APPROXIMATION // enables "quake3" style inverse sqrt - slower on ESP32 // Below options are forcing ArduinoFFT to use sqrtf() instead of sqrt()
// Below options are forcing ArduinoFFT to use sqrtf() instead of sqrt() #define sqrt(x) sqrtf(x) // little hack that reduces FFT time by 10-50% on ESP32
#define sqrt(x) sqrtf(x) // little hack that reduces FFT time by 10-50% on ESP32 #define sqrt_internal sqrtf // see https://github.com/kosme/arduinoFFT/pull/83
#define sqrt_internal sqrtf // see https://github.com/kosme/arduinoFFT/pull/83
#else
// around 40% slower on -S2
// lib_deps += https://github.com/blazoncek/arduinoFFT.git
#endif
#include <arduinoFFT.h> #include <arduinoFFT.h>
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
static ArduinoFFT<float> FFT = ArduinoFFT<float>( vReal, vImag, samplesFFT, SAMPLE_RATE, windowWeighingFactors); static ArduinoFFT<float> FFT = ArduinoFFT<float>( vReal, vImag, samplesFFT, SAMPLE_RATE, windowWeighingFactors);
#else
static arduinoFFT FFT = arduinoFFT(vReal, vImag, samplesFFT, SAMPLE_RATE);
#endif
// Helper functions // Helper functions
@ -288,28 +277,13 @@ void FFTcode(void * parameter)
#endif #endif
// run FFT (takes 3-5ms on ESP32, ~12ms on ESP32-S2) // run FFT (takes 3-5ms on ESP32, ~12ms on ESP32-S2)
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
FFT.dcRemoval(); // remove DC offset FFT.dcRemoval(); // remove DC offset
FFT.windowing( FFTWindow::Flat_top, FFTDirection::Forward); // Weigh data using "Flat Top" function - better amplitude accuracy FFT.windowing( FFTWindow::Flat_top, FFTDirection::Forward); // Weigh data using "Flat Top" function - better amplitude accuracy
//FFT.windowing(FFTWindow::Blackman_Harris, FFTDirection::Forward); // Weigh data using "Blackman- Harris" window - sharp peaks due to excellent sideband rejection //FFT.windowing(FFTWindow::Blackman_Harris, FFTDirection::Forward); // Weigh data using "Blackman- Harris" window - sharp peaks due to excellent sideband rejection
FFT.compute( FFTDirection::Forward ); // Compute FFT FFT.compute( FFTDirection::Forward ); // Compute FFT
FFT.complexToMagnitude(); // Compute magnitudes FFT.complexToMagnitude(); // Compute magnitudes
#else
FFT.DCRemoval(); // let FFT lib remove DC component, so we don't need to care about this in getSamples()
//FFT.Windowing( FFT_WIN_TYP_HAMMING, FFT_FORWARD ); // Weigh data - standard Hamming window FFT.majorPeak(&FFT_MajorPeak, &FFT_Magnitude); // let the effects know which freq was most dominant
//FFT.Windowing( FFT_WIN_TYP_BLACKMAN, FFT_FORWARD ); // Blackman window - better side freq rejection
//FFT.Windowing( FFT_WIN_TYP_BLACKMAN_HARRIS, FFT_FORWARD );// Blackman-Harris - excellent sideband rejection
FFT.Windowing( FFT_WIN_TYP_FLT_TOP, FFT_FORWARD ); // Flat Top Window - better amplitude accuracy
FFT.Compute( FFT_FORWARD ); // Compute FFT
FFT.ComplexToMagnitude(); // Compute magnitudes
#endif
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
FFT.majorPeak(FFT_MajorPeak, FFT_Magnitude); // let the effects know which freq was most dominant
#else
FFT.MajorPeak(&FFT_MajorPeak, &FFT_Magnitude); // let the effects know which freq was most dominant
#endif
FFT_MajorPeak = constrain(FFT_MajorPeak, 1.0f, 11025.0f); // restrict value to range expected by effects FFT_MajorPeak = constrain(FFT_MajorPeak, 1.0f, 11025.0f); // restrict value to range expected by effects
#if defined(WLED_DEBUG) || defined(SR_DEBUG) #if defined(WLED_DEBUG) || defined(SR_DEBUG)

View File

@ -104,6 +104,9 @@ class MultiRelay : public Usermod {
static const char _HAautodiscovery[]; static const char _HAautodiscovery[];
static const char _pcf8574[]; static const char _pcf8574[];
static const char _pcfAddress[]; static const char _pcfAddress[];
static const char _switch[];
static const char _toggle[];
static const char _Command[];
void handleOffTimer(); void handleOffTimer();
void InitHtmlAPIHandle(); void InitHtmlAPIHandle();
@ -261,7 +264,7 @@ void MultiRelay::handleOffTimer() {
void MultiRelay::InitHtmlAPIHandle() { // https://github.com/me-no-dev/ESPAsyncWebServer void MultiRelay::InitHtmlAPIHandle() { // https://github.com/me-no-dev/ESPAsyncWebServer
DEBUG_PRINTLN(F("Relays: Initialize HTML API")); DEBUG_PRINTLN(F("Relays: Initialize HTML API"));
server.on("/relays", HTTP_GET, [this](AsyncWebServerRequest *request) { server.on(SET_F("/relays"), HTTP_GET, [this](AsyncWebServerRequest *request) {
DEBUG_PRINTLN(F("Relays: HTML API")); DEBUG_PRINTLN(F("Relays: HTML API"));
String janswer; String janswer;
String error = ""; String error = "";
@ -271,9 +274,9 @@ void MultiRelay::InitHtmlAPIHandle() { // https://github.com/me-no-dev/ESPAsync
if (getActiveRelayCount()) { if (getActiveRelayCount()) {
// Commands // Commands
if(request->hasParam("switch")) { if (request->hasParam(FPSTR(_switch))) {
/**** Switch ****/ /**** Switch ****/
AsyncWebParameter* p = request->getParam("switch"); AsyncWebParameter* p = request->getParam(FPSTR(_switch));
// Get Values // Get Values
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) { for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
int value = getValue(p->value(), ',', i); int value = getValue(p->value(), ',', i);
@ -284,9 +287,9 @@ void MultiRelay::InitHtmlAPIHandle() { // https://github.com/me-no-dev/ESPAsync
if (_relay[i].external) switchRelay(i, (bool)value); if (_relay[i].external) switchRelay(i, (bool)value);
} }
} }
} else if(request->hasParam("toggle")) { } else if (request->hasParam(FPSTR(_toggle))) {
/**** Toggle ****/ /**** Toggle ****/
AsyncWebParameter* p = request->getParam("toggle"); AsyncWebParameter* p = request->getParam(FPSTR(_toggle));
// Get Values // Get Values
for (int i=0;i<MULTI_RELAY_MAX_RELAYS;i++) { for (int i=0;i<MULTI_RELAY_MAX_RELAYS;i++) {
int value = getValue(p->value(), ',', i); int value = getValue(p->value(), ',', i);
@ -314,7 +317,7 @@ void MultiRelay::InitHtmlAPIHandle() { // https://github.com/me-no-dev/ESPAsync
janswer += error; janswer += error;
janswer += F("\","); janswer += F("\",");
janswer += F("\"SW Version\":\""); janswer += F("\"SW Version\":\"");
janswer += String(GEOGABVERSION); janswer += String(F(GEOGABVERSION));
janswer += F("\"}"); janswer += F("\"}");
request->send(200, "application/json", janswer); request->send(200, "application/json", janswer);
}); });
@ -420,7 +423,7 @@ uint8_t MultiRelay::getActiveRelayCount() {
* topic should look like: /relay/X/command; where X is relay number, 0 based * topic should look like: /relay/X/command; where X is relay number, 0 based
*/ */
bool MultiRelay::onMqttMessage(char* topic, char* payload) { bool MultiRelay::onMqttMessage(char* topic, char* payload) {
if (strlen(topic) > 8 && strncmp_P(topic, PSTR("/relay/"), 7) == 0 && strncmp_P(topic+8, PSTR("/command"), 8) == 0) { if (strlen(topic) > 8 && strncmp_P(topic, PSTR("/relay/"), 7) == 0 && strncmp_P(topic+8, _Command, 8) == 0) {
uint8_t relay = strtoul(topic+7, NULL, 10); uint8_t relay = strtoul(topic+7, NULL, 10);
if (relay<MULTI_RELAY_MAX_RELAYS) { if (relay<MULTI_RELAY_MAX_RELAYS) {
String action = payload; String action = payload;
@ -430,7 +433,7 @@ bool MultiRelay::onMqttMessage(char* topic, char* payload) {
} else if (action == "off") { } else if (action == "off") {
if (_relay[relay].external) switchRelay(relay, false); if (_relay[relay].external) switchRelay(relay, false);
return true; return true;
} else if (action == "toggle") { } else if (action == FPSTR(_toggle)) {
if (_relay[relay].external) toggleRelay(relay); if (_relay[relay].external) toggleRelay(relay);
return true; return true;
} }
@ -470,7 +473,7 @@ void MultiRelay::publishHomeAssistantAutodiscovery() {
sprintf_P(buf, PSTR("%s/relay/%d"), mqttDeviceTopic, i); //max length: 33 + 7 + 3 = 43 sprintf_P(buf, PSTR("%s/relay/%d"), mqttDeviceTopic, i); //max length: 33 + 7 + 3 = 43
json["~"] = buf; json["~"] = buf;
strcat_P(buf, PSTR("/command")); strcat_P(buf, _Command);
mqtt->subscribe(buf, 0); mqtt->subscribe(buf, 0);
json[F("stat_t")] = "~"; json[F("stat_t")] = "~";
@ -675,8 +678,8 @@ void MultiRelay::addToJsonInfo(JsonObject &root) {
uiDomString += F(",on:"); uiDomString += F(",on:");
uiDomString += _relay[i].state ? "false" : "true"; uiDomString += _relay[i].state ? "false" : "true";
uiDomString += F("}});\">"); uiDomString += F("}});\">");
uiDomString += F("<i class=\"icons"); uiDomString += F("<i class=\"icons ");
uiDomString += _relay[i].state ? F(" on") : F(" off"); uiDomString += _relay[i].state ? "on" : "off";
uiDomString += F("\">&#xe08f;</i></button>"); uiDomString += F("\">&#xe08f;</i></button>");
infoArr.add(uiDomString); infoArr.add(uiDomString);
} }
@ -699,11 +702,11 @@ void MultiRelay::addToJsonState(JsonObject &root) {
if (_relay[i].pin < 0) continue; if (_relay[i].pin < 0) continue;
JsonObject relay = rel_arr.createNestedObject(); JsonObject relay = rel_arr.createNestedObject();
relay[FPSTR(_relay_str)] = i; relay[FPSTR(_relay_str)] = i;
relay[F("state")] = _relay[i].state; relay["state"] = _relay[i].state;
} }
#else #else
multiRelay[FPSTR(_relay_str)] = 0; multiRelay[FPSTR(_relay_str)] = 0;
multiRelay[F("state")] = _relay[0].state; multiRelay["state"] = _relay[0].state;
#endif #endif
} }
@ -836,3 +839,6 @@ const char MultiRelay::_broadcast[] PROGMEM = "broadcast-sec";
const char MultiRelay::_HAautodiscovery[] PROGMEM = "HA-autodiscovery"; const char MultiRelay::_HAautodiscovery[] PROGMEM = "HA-autodiscovery";
const char MultiRelay::_pcf8574[] PROGMEM = "use-PCF8574"; const char MultiRelay::_pcf8574[] PROGMEM = "use-PCF8574";
const char MultiRelay::_pcfAddress[] PROGMEM = "PCF8574-address"; const char MultiRelay::_pcfAddress[] PROGMEM = "PCF8574-address";
const char MultiRelay::_switch[] PROGMEM = "switch";
const char MultiRelay::_toggle[] PROGMEM = "toggle";
const char MultiRelay::_Command[] PROGMEM = "/command";

View File

@ -1,4 +1,4 @@
# I2C 4 Line Display Usermod ALT # I2C/SPI 4 Line Display Usermod ALT
Thank you to the authors of the original version of these usermods. It would not have been possible without them! Thank you to the authors of the original version of these usermods. It would not have been possible without them!
"usermod_v2_four_line_display" "usermod_v2_four_line_display"
@ -9,20 +9,19 @@ The display usermod UI has been completely changed.
The changes made to the RotaryEncoder usermod were made to support the new UI in the display usermod. The changes made to the RotaryEncoder usermod were made to support the new UI in the display usermod.
Without the display it, functions identical to the original. Without the display, it functions identical to the original.
The original "usermod_v2_auto_save" will not work with the display just yet. The original "usermod_v2_auto_save" will not work with the display just yet.
Press the encoder to cycle through the options: Press the encoder to cycle through the options:
*Brightness * Brightness
*Speed * Speed
*Intensity * Intensity
*Palette * Palette
*Effect * Effect
*Main Color (only if display is used) * Main Color (only if display is used)
*Saturation (only if display is used) * Saturation (only if display is used)
Press and hold the encoder to display Network Info Press and hold the encoder to display Network Info. If AP is active, it will display AP, SSID and password
if AP is active, it will display AP, SSID and password
Also shows if the timer is enabled Also shows if the timer is enabled
@ -30,11 +29,47 @@ Also shows if the timer is enabled
## Installation ## Installation
Please refer to the original `usermod_v2_rotary_encoder_ui` readme for the main instructions Please refer to the original `usermod_v2_rotary_encoder_ui` readme for the main instructions.
Then to activate this alternative usermod add `#define USE_ALT_DISPlAY` to the `usermods_list.cpp` file,
Copy the example `platformio_override.sample.ini` from the usermod_v2_rotary_encoder_ui_ALT folder to the root directory of your particular build and rename it to `platformio_override.ini`.
This file should be placed in the same directory as `platformio.ini`.
Then, to activate this alternative usermod, add `#define USE_ALT_DISPlAY` (NOTE: CASE SENSITIVE) to the `usermods_list.cpp` file,
or add `-D USE_ALT_DISPlAY` to the original `platformio_override.ini.sample` file or add `-D USE_ALT_DISPlAY` to the original `platformio_override.ini.sample` file
## Configuration
These options are configurable in Config > Usermods
### Usermod Setup
* Global I2C GPIOs (HW) - Set the SDA and SCL pins
### 4LineDisplay
* `enabled` - enable/disable usermod
* `type` - display type in numeric format
* 1 = I2C SSD1306 128x32
* 2 = I2C SH1106 128x32
* 3 = I2C SSD1306 128x64 (4 double-height lines)
* 4 = I2C SSD1305 128x32
* 5 = I2C SSD1305 128x64 (4 double-height lines)
* 6 = SPI SSD1306 128x32
* 7 = SPI SSD1306 128x64 (4 double-height lines)
* 8 = SPI SSD1309 128x64 (4 double-height lines)
* 9 = I2C SSD1309 128x64 (4 double-height lines)
* `pin` - GPIO pins used for display; SPI displays can use SCK, MOSI, CS, DC & RST
* `flip` - flip/rotate display 180°
* `contrast` - set display contrast (higher contrast may reduce display lifetime)
* `screenTimeOutSec` - screen saver time-out in seconds
* `sleepMode` - enable/disable screen saver
* `clockMode` - enable/disable clock display in screen saver mode
* `showSeconds` - Show seconds on the clock display
* `i2c-freq-kHz` - I2C clock frequency in kHz (may help reduce dropped frames, range: 400-3400)
### PlatformIO requirements ### PlatformIO requirements
Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`. Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`.

View File

@ -89,7 +89,8 @@ typedef enum {
SSD1305_64, // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C SSD1305_64, // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C
SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI
SSD1306_SPI64, // U8X8_SSD1306_128X64_NONAME_HW_SPI SSD1306_SPI64, // U8X8_SSD1306_128X64_NONAME_HW_SPI
SSD1309_SPI64 // U8X8_SSD1309_128X64_NONAME0_4W_HW_SPI SSD1309_SPI64, // U8X8_SSD1309_128X64_NONAME0_4W_HW_SPI
SSD1309_64 // U8X8_SSD1309_128X64_NONAME0_HW_I2C
} DisplayType; } DisplayType;
@ -556,6 +557,7 @@ void FourLineDisplayUsermod::setup() {
case SSD1306_64: u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(); break; case SSD1306_64: u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(); break;
case SSD1305: u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(); break; case SSD1305: u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(); break;
case SSD1305_64: u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(); break; case SSD1305_64: u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(); break;
case SSD1309_64: u8x8 = (U8X8 *) new U8X8_SSD1309_128X64_NONAME0_HW_I2C(); break;
// U8X8 uses global SPI variable that is attached to VSPI bus on ESP32 // U8X8 uses global SPI variable that is attached to VSPI bus on ESP32
case SSD1306_SPI: u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset case SSD1306_SPI: u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset
case SSD1306_SPI64: u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset case SSD1306_SPI64: u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset
@ -1205,6 +1207,7 @@ void FourLineDisplayUsermod::appendConfigData() {
oappend(SET_F("addOption(dd,'SSD1306 128x64',3);")); oappend(SET_F("addOption(dd,'SSD1306 128x64',3);"));
oappend(SET_F("addOption(dd,'SSD1305',4);")); oappend(SET_F("addOption(dd,'SSD1305',4);"));
oappend(SET_F("addOption(dd,'SSD1305 128x64',5);")); oappend(SET_F("addOption(dd,'SSD1305 128x64',5);"));
oappend(SET_F("addOption(dd,'SSD1309 128x64',9);"));
oappend(SET_F("addOption(dd,'SSD1306 SPI',6);")); oappend(SET_F("addOption(dd,'SSD1306 SPI',6);"));
oappend(SET_F("addOption(dd,'SSD1306 SPI 128x64',7);")); oappend(SET_F("addOption(dd,'SSD1306 SPI 128x64',7);"));
oappend(SET_F("addOption(dd,'SSD1309 SPI 128x64',8);")); oappend(SET_F("addOption(dd,'SSD1309 SPI 128x64',8);"));
@ -1346,6 +1349,10 @@ bool FourLineDisplayUsermod::readFromConfig(JsonObject& root) {
u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1305_128x64_adafruit, u8x8_cad_ssd13xx_fast_i2c, u8x8_byte_arduino_hw_i2c, u8x8_gpio_and_delay_arduino); u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1305_128x64_adafruit, u8x8_cad_ssd13xx_fast_i2c, u8x8_byte_arduino_hw_i2c, u8x8_gpio_and_delay_arduino);
u8x8_SetPin_HW_I2C(u8x8->getU8x8(), U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE); u8x8_SetPin_HW_I2C(u8x8->getU8x8(), U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE);
break; break;
case SSD1309_64:
u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1309_128x64_noname0, u8x8_cad_ssd13xx_fast_i2c, u8x8_byte_arduino_hw_i2c, u8x8_gpio_and_delay_arduino);
u8x8_SetPin_HW_I2C(u8x8->getU8x8(), U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE);
break;
case SSD1306_SPI: case SSD1306_SPI:
u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1306_128x32_univision, u8x8_cad_001, u8x8_byte_arduino_hw_spi, u8x8_gpio_and_delay_arduino); u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1306_128x32_univision, u8x8_cad_001, u8x8_byte_arduino_hw_spi, u8x8_gpio_and_delay_arduino);
u8x8_SetPin_4Wire_HW_SPI(u8x8->getU8x8(), ioPin[0], ioPin[1], ioPin[2]); // Pins are cs, dc, reset u8x8_SetPin_4Wire_HW_SPI(u8x8->getU8x8(), ioPin[0], ioPin[1], ioPin[2]); // Pins are cs, dc, reset

View File

@ -0,0 +1,17 @@
[platformio]
default_envs = esp32dev
[env:esp32dev]
board = esp32dev
platform = ${esp32.platform}
build_unflags = ${common.build_unflags}
build_flags =
${common.build_flags_esp32}
-D USERMOD_FOUR_LINE_DISPLAY -D USE_ALT_DISPlAY
-D USERMOD_ROTARY_ENCODER_UI -D ENCODER_DT_PIN=18 -D ENCODER_CLK_PIN=5 -D ENCODER_SW_PIN=19
upload_speed = 460800
lib_deps =
${esp32.lib_deps}
U8g2@~2.34.4
Wire

View File

@ -13,13 +13,13 @@ Without the display, it functions identical to the original.
The original "usermod_v2_auto_save" will not work with the display just yet. The original "usermod_v2_auto_save" will not work with the display just yet.
Press the encoder to cycle through the options: Press the encoder to cycle through the options:
*Brightness * Brightness
*Speed * Speed
*Intensity * Intensity
*Palette * Palette
*Effect * Effect
*Main Color (only if display is used) * Main Color (only if display is used)
*Saturation (only if display is used) * Saturation (only if display is used)
Press and hold the encoder to display Network Info Press and hold the encoder to display Network Info
if AP is active, it will display the AP, SSID and Password if AP is active, it will display the AP, SSID and Password
@ -30,10 +30,23 @@ Also shows if the timer is enabled.
## Installation ## Installation
Please refer to the original `usermod_v2_rotary_encoder_ui` readme for the main instructions.<br/> Copy the example `platformio_override.sample.ini` to the root directory of your particular build and rename it to `platformio_override.ini`.
To activate this alternative usermod, add `#define USE_ALT_DISPlAY` to the `usermods_list.cpp` file,
or add `-D USE_ALT_DISPlAY` to the original `platformio_override.ini.sample` file.
To activate this alternative usermod, add `#define USE_ALT_DISPlAY` (NOTE: CASE SENSITIVE) to the `usermods_list.cpp` file, or add `-D USE_ALT_DISPlAY` to your `platformio_override.ini` file
### Define Your Options
* `USERMOD_ROTARY_ENCODER_UI` - define this to have this user mod included wled00\usermods_list.cpp
* `USERMOD_FOUR_LINE_DISPLAY` - define this to have this the Four Line Display mod included wled00\usermods_list.cpp
also tells this usermod that the display is available
(see the Four Line Display usermod `readme.md` for more details)
* `USE_ALT_DISPlAY` - Mandatory to use Four Line Display
* `ENCODER_DT_PIN` - defaults to 18
* `ENCODER_CLK_PIN` - defaults to 5
* `ENCODER_SW_PIN` - defaults to 19
* `USERMOD_ROTARY_ENCODER_GPIO` - GPIO functionality:
`INPUT_PULLUP` to use internal pull-up
`INPUT` to use pull-up on the PCB
### PlatformIO requirements ### PlatformIO requirements

View File

@ -392,26 +392,26 @@ byte RotaryEncoderUIUsermod::readPin(uint8_t pin) {
* modes_alpha_indexes and palettes_alpha_indexes. * modes_alpha_indexes and palettes_alpha_indexes.
*/ */
void RotaryEncoderUIUsermod::sortModesAndPalettes() { void RotaryEncoderUIUsermod::sortModesAndPalettes() {
DEBUG_PRINTLN(F("Sorting modes and palettes.")); DEBUG_PRINT(F("Sorting modes: ")); DEBUG_PRINTLN(strip.getModeCount());
//modes_qstrings = re_findModeStrings(JSON_mode_names, strip.getModeCount()); //modes_qstrings = re_findModeStrings(JSON_mode_names, strip.getModeCount());
modes_qstrings = strip.getModeDataSrc(); modes_qstrings = strip.getModeDataSrc();
modes_alpha_indexes = re_initIndexArray(strip.getModeCount()); modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT); re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT);
palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount()+strip.customPalettes.size()); DEBUG_PRINT(F("Sorting palettes: ")); DEBUG_PRINT(strip.getPaletteCount()); DEBUG_PRINT('/'); DEBUG_PRINTLN(strip.customPalettes.size());
palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount()+strip.customPalettes.size()); palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount());
palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount());
if (strip.customPalettes.size()) { if (strip.customPalettes.size()) {
for (int i=0; i<strip.customPalettes.size(); i++) { for (int i=0; i<strip.customPalettes.size(); i++) {
palettes_alpha_indexes[strip.getPaletteCount()+i] = 255-i; palettes_alpha_indexes[strip.getPaletteCount()-strip.customPalettes.size()+i] = 255-i;
palettes_qstrings[strip.getPaletteCount()+i] = PSTR("~Custom~"); palettes_qstrings[strip.getPaletteCount()-strip.customPalettes.size()+i] = PSTR("~Custom~");
} }
} }
// How many palette names start with '*' and should not be sorted? // How many palette names start with '*' and should not be sorted?
// (Also skipping the first one, 'Default'). // (Also skipping the first one, 'Default').
int skipPaletteCount = 1; int skipPaletteCount = 1;
while (pgm_read_byte_near(palettes_qstrings[skipPaletteCount++]) == '*') ; while (pgm_read_byte_near(palettes_qstrings[skipPaletteCount]) == '*') skipPaletteCount++;
re_sortModes(palettes_qstrings, palettes_alpha_indexes, strip.getPaletteCount(), skipPaletteCount); re_sortModes(palettes_qstrings, palettes_alpha_indexes, strip.getPaletteCount()-strip.customPalettes.size(), skipPaletteCount);
} }
byte *RotaryEncoderUIUsermod::re_initIndexArray(int numModes) { byte *RotaryEncoderUIUsermod::re_initIndexArray(int numModes) {

View File

@ -5016,21 +5016,25 @@ uint16_t mode_2DDrift() { // By: Stepko https://editor.soulmateli
const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
const uint16_t colsCenter = (cols>>1) + (cols%2);
const uint16_t rowsCenter = (rows>>1) + (rows%2);
SEGMENT.fadeToBlackBy(128); SEGMENT.fadeToBlackBy(128);
const uint16_t maxDim = MAX(cols, rows)/2; const uint16_t maxDim = MAX(cols, rows)/2;
unsigned long t = strip.now / (32 - (SEGMENT.speed>>3)); unsigned long t = strip.now / (32 - (SEGMENT.speed>>3));
unsigned long t_20 = t/20; // softhack007: pre-calculating this gives about 10% speedup unsigned long t_20 = t/20; // softhack007: pre-calculating this gives about 10% speedup
for (float i = 1; i < maxDim; i += 0.25) { for (float i = 1.0f; i < maxDim; i += 0.25f) {
float angle = radians(t * (maxDim - i)); float angle = radians(t * (maxDim - i));
uint16_t myX = (cols>>1) + (uint16_t)(sin_t(angle) * i) + (cols%2); int16_t mySin = sin_t(angle) * i;
uint16_t myY = (rows>>1) + (uint16_t)(cos_t(angle) * i) + (rows%2); int16_t myCos = cos_t(angle) * i;
SEGMENT.setPixelColorXY(myX, myY, ColorFromPalette(SEGPALETTE, (i * 20) + t_20, 255, LINEARBLEND)); SEGMENT.setPixelColorXY(colsCenter + mySin, rowsCenter + myCos, ColorFromPalette(SEGPALETTE, (i * 20) + t_20, 255, LINEARBLEND));
if (SEGMENT.check1) SEGMENT.setPixelColorXY(colsCenter + myCos, rowsCenter + mySin, ColorFromPalette(SEGPALETTE, (i * 20) + t_20, 255, LINEARBLEND));
} }
SEGMENT.blur(SEGMENT.intensity>>3); SEGMENT.blur(SEGMENT.intensity>>3);
return FRAMETIME; return FRAMETIME;
} // mode_2DDrift() } // mode_2DDrift()
static const char _data_FX_MODE_2DDRIFT[] PROGMEM = "Drift@Rotation speed,Blur amount;;!;2"; static const char _data_FX_MODE_2DDRIFT[] PROGMEM = "Drift@Rotation speed,Blur amount,,,,Twin;;!;2";
////////////////////////// //////////////////////////
@ -6203,8 +6207,9 @@ uint16_t mode_2Ddriftrose(void) {
SEGMENT.fadeToBlackBy(32+(SEGMENT.speed>>3)); SEGMENT.fadeToBlackBy(32+(SEGMENT.speed>>3));
for (size_t i = 1; i < 37; i++) { for (size_t i = 1; i < 37; i++) {
uint32_t x = (CX + (sin_t(radians(i * 10)) * (beatsin8(i, 0, L*2)-L))) * 255.f; float angle = radians(i * 10);
uint32_t y = (CY + (cos_t(radians(i * 10)) * (beatsin8(i, 0, L*2)-L))) * 255.f; uint32_t x = (CX + (sin_t(angle) * (beatsin8(i, 0, L*2)-L))) * 255.f;
uint32_t y = (CY + (cos_t(angle) * (beatsin8(i, 0, L*2)-L))) * 255.f;
SEGMENT.wu_pixel(x, y, CHSV(i * 10, 255, 255)); SEGMENT.wu_pixel(x, y, CHSV(i * 10, 255, 255));
} }
SEGMENT.blur((SEGMENT.intensity>>4)+1); SEGMENT.blur((SEGMENT.intensity>>4)+1);

View File

@ -59,13 +59,12 @@
/* Not used in all effects yet */ /* Not used in all effects yet */
#define WLED_FPS 42 #define WLED_FPS 42
#define FRAMETIME_FIXED (1000/WLED_FPS) #define FRAMETIME_FIXED (1000/WLED_FPS)
//#define FRAMETIME _frametime
#define FRAMETIME strip.getFrameTime() #define FRAMETIME strip.getFrameTime()
/* each segment uses 82 bytes of SRAM memory, so if you're application fails because of /* each segment uses 82 bytes of SRAM memory, so if you're application fails because of
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */ insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
#ifdef ESP8266 #ifdef ESP8266
#define MAX_NUM_SEGMENTS 12 #define MAX_NUM_SEGMENTS 16
/* How much data bytes all segments combined may allocate */ /* How much data bytes all segments combined may allocate */
#define MAX_SEGMENT_DATA 5120 #define MAX_SEGMENT_DATA 5120
#else #else
@ -73,11 +72,7 @@
#define MAX_NUM_SEGMENTS 32 #define MAX_NUM_SEGMENTS 32
#endif #endif
#if defined(ARDUINO_ARCH_ESP32S2) #if defined(ARDUINO_ARCH_ESP32S2)
#if defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM) #define MAX_SEGMENT_DATA MAX_NUM_SEGMENTS*768 // 24k by default (S2 is short on free RAM)
#define MAX_SEGMENT_DATA MAX_NUM_SEGMENTS*1024 // 32k by default
#else
#define MAX_SEGMENT_DATA MAX_NUM_SEGMENTS*768 // 24k by default
#endif
#else #else
#define MAX_SEGMENT_DATA MAX_NUM_SEGMENTS*1280 // 40k by default #define MAX_SEGMENT_DATA MAX_NUM_SEGMENTS*1280 // 40k by default
#endif #endif
@ -810,7 +805,7 @@ class WS2812FX { // 96 bytes
inline uint8_t getSegmentsNum(void) { return _segments.size(); } // returns currently present segments inline uint8_t getSegmentsNum(void) { return _segments.size(); } // returns currently present segments
inline uint8_t getCurrSegmentId(void) { return _segment_index; } // returns current segment index (only valid while strip.isServicing()) inline uint8_t getCurrSegmentId(void) { return _segment_index; } // returns current segment index (only valid while strip.isServicing())
inline uint8_t getMainSegmentId(void) { return _mainSegment; } // returns main segment index inline uint8_t getMainSegmentId(void) { return _mainSegment; } // returns main segment index
inline uint8_t getPaletteCount() { return 13 + GRADIENT_PALETTE_COUNT; } // will only return built-in palette count inline uint8_t getPaletteCount() { return 13 + GRADIENT_PALETTE_COUNT + customPalettes.size(); }
inline uint8_t getTargetFps() { return _targetFps; } // returns rough FPS value for las 2s interval inline uint8_t getTargetFps() { return _targetFps; } // returns rough FPS value for las 2s interval
inline uint8_t getModeCount() { return _modeCount; } // returns number of registered modes/effects inline uint8_t getModeCount() { return _modeCount; } // returns number of registered modes/effects

View File

@ -65,9 +65,10 @@ void WS2812FX::setUpMatrix() {
customMappingSize = 0; // prevent use of mapping if anything goes wrong customMappingSize = 0; // prevent use of mapping if anything goes wrong
if (customMappingTable == nullptr) customMappingTable = new uint16_t[getLengthTotal()]; if (customMappingTable) delete[] customMappingTable;
customMappingTable = new uint16_t[getLengthTotal()];
if (customMappingTable != nullptr) { if (customMappingTable) {
customMappingSize = getLengthTotal(); customMappingSize = getLengthTotal();
// fill with empty in case we don't fill the entire matrix // fill with empty in case we don't fill the entire matrix
@ -138,7 +139,7 @@ void WS2812FX::setUpMatrix() {
DEBUG_PRINTLN(); DEBUG_PRINTLN();
#endif #endif
} else { // memory allocation error } else { // memory allocation error
DEBUG_PRINTLN(F("Ledmap alloc error.")); DEBUG_PRINTLN(F("ERROR 2D LED map allocation error."));
isMatrix = false; isMatrix = false;
panels = 0; panels = 0;
panel.clear(); panel.clear();

View File

@ -455,18 +455,18 @@ CRGBPalette16 IRAM_ATTR &Segment::currentPalette(CRGBPalette16 &targetPalette, u
// relies on WS2812FX::service() to call it for each frame // relies on WS2812FX::service() to call it for each frame
void Segment::handleRandomPalette() { void Segment::handleRandomPalette() {
// is it time to generate a new palette? // is it time to generate a new palette?
if ((millis()/1000U) - _lastPaletteChange > randomPaletteChangeTime) { if ((uint16_t)((uint16_t)(millis() / 1000U) - _lastPaletteChange) > randomPaletteChangeTime){
_newRandomPalette = useHarmonicRandomPalette ? generateHarmonicRandomPalette(_randomPalette) : generateRandomPalette(); _newRandomPalette = useHarmonicRandomPalette ? generateHarmonicRandomPalette(_randomPalette) : generateRandomPalette();
_lastPaletteChange = millis()/1000U; _lastPaletteChange = (uint16_t)(millis() / 1000U);
_lastPaletteBlend = (uint16_t)(millis() & 0xFFFF)-512; // starts blending immediately _lastPaletteBlend = (uint16_t)((uint16_t)millis() - 512); // starts blending immediately
} }
// if palette transitions is enabled, blend it according to Transition Time (if longer than minimum given by service calls) // if palette transitions is enabled, blend it according to Transition Time (if longer than minimum given by service calls)
if (strip.paletteFade) { if (strip.paletteFade) {
// assumes that 128 updates are sufficient to blend a palette, so shift by 7 (can be more, can be less) // assumes that 128 updates are sufficient to blend a palette, so shift by 7 (can be more, can be less)
// in reality there need to be 255 blends to fully blend two entirely different palettes // in reality there need to be 255 blends to fully blend two entirely different palettes
if ((millis() & 0xFFFF) - _lastPaletteBlend < strip.getTransition() >> 7) return; // not yet time to fade, delay the update if ((uint16_t)((uint16_t)millis() - _lastPaletteBlend) < strip.getTransition() >> 7) return; // not yet time to fade, delay the update
_lastPaletteBlend = millis(); _lastPaletteBlend = (uint16_t)millis();
} }
nblendPaletteTowardPalette(_randomPalette, _newRandomPalette, 48); nblendPaletteTowardPalette(_randomPalette, _newRandomPalette, 48);
} }
@ -1098,6 +1098,12 @@ void WS2812FX::finalizeInit(void) {
uint16_t prevLen = 0; uint16_t prevLen = 0;
for (int i = 0; i < defNumBusses && i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) { for (int i = 0; i < defNumBusses && i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
uint8_t defPin[] = {defDataPins[i]}; uint8_t defPin[] = {defDataPins[i]};
// when booting without config (1st boot) we need to make sure GPIOs defined for LED output don't clash with hardware
// i.e. DEBUG (GPIO1), DMX (2), SPI RAM/FLASH (16&17 on ESP32-WROVER/PICO), etc
if (pinManager.isPinAllocated(defPin[0])) {
defPin[0] = 1; // start with GPIO1 and work upwards
while (pinManager.isPinAllocated(defPin[0]) && defPin[0] < WLED_NUM_PINS) defPin[0]++;
}
uint16_t start = prevLen; uint16_t start = prevLen;
uint16_t count = defCounts[(i < defNumCounts) ? i : defNumCounts -1]; uint16_t count = defCounts[(i < defNumCounts) ? i : defNumCounts -1];
prevLen += count; prevLen += count;
@ -1162,12 +1168,16 @@ void WS2812FX::service() {
uint16_t delay = FRAMETIME; uint16_t delay = FRAMETIME;
if (!seg.freeze) { //only run effect function if not frozen if (!seg.freeze) { //only run effect function if not frozen
int16_t oldCCT = BusManager::getSegmentCCT(); // store original CCT value (actually it is not Segment based)
_virtualSegmentLength = seg.virtualLength(); //SEGLEN _virtualSegmentLength = seg.virtualLength(); //SEGLEN
_colors_t[0] = gamma32(seg.currentColor(0)); _colors_t[0] = gamma32(seg.currentColor(0));
_colors_t[1] = gamma32(seg.currentColor(1)); _colors_t[1] = gamma32(seg.currentColor(1));
_colors_t[2] = gamma32(seg.currentColor(2)); _colors_t[2] = gamma32(seg.currentColor(2));
seg.currentPalette(_currentPalette, seg.palette); // we need to pass reference seg.currentPalette(_currentPalette, seg.palette); // we need to pass reference
if (!cctFromRgb || correctWB) BusManager::setSegmentCCT(seg.currentBri(true), correctWB); // when correctWB is true we need to correct/adjust RGB value according to desired CCT value, but it will also affect actual WW/CW ratio
// when cctFromRgb is true we implicitly calculate WW and CW from RGB values
if (cctFromRgb) BusManager::setSegmentCCT(-1);
else BusManager::setSegmentCCT(seg.currentBri(true), correctWB);
// Effect blending // Effect blending
// When two effects are being blended, each may have different segment data, this // When two effects are being blended, each may have different segment data, this
// data needs to be saved first and then restored before running previous mode. // data needs to be saved first and then restored before running previous mode.
@ -1190,20 +1200,19 @@ void WS2812FX::service() {
#endif #endif
seg.call++; seg.call++;
if (seg.isInTransition() && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition if (seg.isInTransition() && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
BusManager::setSegmentCCT(oldCCT); // restore old CCT for ABL adjustments
} }
seg.next_time = nowUp + delay; seg.next_time = nowUp + delay;
} }
// if (_segment_index == _queuedChangesSegId) setUpSegmentFromQueuedChanges();
_segment_index++; _segment_index++;
} }
_virtualSegmentLength = 0; _virtualSegmentLength = 0;
BusManager::setSegmentCCT(-1);
_isServicing = false; _isServicing = false;
_triggered = false; _triggered = false;
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
if (millis() - nowUp > _frametime) DEBUG_PRINTLN(F("Slow effects.")); if (millis() - nowUp > _frametime) DEBUG_PRINTF_P(PSTR("Slow effects %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
#endif #endif
if (doShow) { if (doShow) {
yield(); yield();
@ -1211,7 +1220,7 @@ void WS2812FX::service() {
show(); show();
} }
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
if (millis() - nowUp > _frametime) DEBUG_PRINTLN(F("Slow strip.")); if (millis() - nowUp > _frametime) DEBUG_PRINTF_P(PSTR("Slow strip %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
#endif #endif
} }
@ -1390,11 +1399,7 @@ bool WS2812FX::hasCCTBus(void) {
for (size_t b = 0; b < BusManager::getNumBusses(); b++) { for (size_t b = 0; b < BusManager::getNumBusses(); b++) {
Bus *bus = BusManager::getBus(b); Bus *bus = BusManager::getBus(b);
if (bus == nullptr || bus->getLength()==0) break; if (bus == nullptr || bus->getLength()==0) break;
switch (bus->getType()) { if (bus->hasCCT()) return true;
case TYPE_ANALOG_5CH:
case TYPE_ANALOG_2CH:
return true;
}
} }
return false; return false;
} }
@ -1425,31 +1430,12 @@ void WS2812FX::setSegment(uint8_t segId, uint16_t i1, uint16_t i2, uint8_t group
appendSegment(Segment(0, strip.getLengthTotal())); appendSegment(Segment(0, strip.getLengthTotal()));
segId = getSegmentsNum()-1; // segments are added at the end of list segId = getSegmentsNum()-1; // segments are added at the end of list
} }
/*
if (_queuedChangesSegId == segId) _queuedChangesSegId = 255; // cancel queued change if already queued for this segment
if (segId < getMaxSegments() && segId == getCurrSegmentId() && isServicing()) { // queue change to prevent concurrent access
// queuing a change for a second segment will lead to the loss of the first change if not yet applied
// however this is not a problem as the queued change is applied immediately after the effect function in that segment returns
_qStart = i1; _qStop = i2; _qStartY = startY; _qStopY = stopY;
_qGrouping = grouping; _qSpacing = spacing; _qOffset = offset;
_queuedChangesSegId = segId;
DEBUG_PRINT(F("Segment queued: ")); DEBUG_PRINTLN(segId);
return; // queued changes are applied immediately after effect function returns
}
*/
suspend(); suspend();
_segments[segId].setUp(i1, i2, grouping, spacing, offset, startY, stopY); _segments[segId].setUp(i1, i2, grouping, spacing, offset, startY, stopY);
resume(); resume();
if (segId > 0 && segId == getSegmentsNum()-1 && i2 <= i1) _segments.pop_back(); // if last segment was deleted remove it from vector if (segId > 0 && segId == getSegmentsNum()-1 && i2 <= i1) _segments.pop_back(); // if last segment was deleted remove it from vector
} }
/*
void WS2812FX::setUpSegmentFromQueuedChanges() {
if (_queuedChangesSegId >= getSegmentsNum()) return;
_segments[_queuedChangesSegId].setUp(_qStart, _qStop, _qGrouping, _qSpacing, _qOffset, _qStartY, _qStopY);
_queuedChangesSegId = 255;
}
*/
void WS2812FX::resetSegments() { void WS2812FX::resetSegments() {
_segments.clear(); // destructs all Segment as part of clearing _segments.clear(); // destructs all Segment as part of clearing
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
@ -1670,19 +1656,23 @@ bool WS2812FX::deserializeMap(uint8_t n) {
return false; // if file does not load properly then exit return false; // if file does not load properly then exit
} }
DEBUG_PRINT(F("Reading LED map from ")); DEBUG_PRINTLN(fileName); if (customMappingTable) delete[] customMappingTable;
customMappingTable = new uint16_t[getLengthTotal()];
if (customMappingTable == nullptr) customMappingTable = new uint16_t[getLengthTotal()]; if (customMappingTable) {
DEBUG_PRINT(F("Reading LED map from ")); DEBUG_PRINTLN(fileName);
JsonObject root = pDoc->as<JsonObject>(); JsonObject root = pDoc->as<JsonObject>();
JsonArray map = root[F("map")]; JsonArray map = root[F("map")];
if (!map.isNull() && map.size()) { // not an empty map if (!map.isNull() && map.size()) { // not an empty map
customMappingSize = min((unsigned)map.size(), (unsigned)getLengthTotal()); customMappingSize = min((unsigned)map.size(), (unsigned)getLengthTotal());
for (unsigned i=0; i<customMappingSize; i++) customMappingTable[i] = (uint16_t) (map[i]<0 ? 0xFFFFU : map[i]); for (unsigned i=0; i<customMappingSize; i++) customMappingTable[i] = (uint16_t) (map[i]<0 ? 0xFFFFU : map[i]);
}
} else {
DEBUG_PRINTLN(F("ERROR LED map allocation error."));
} }
releaseJSONBufferLock(); releaseJSONBufferLock();
return true; return (customMappingSize > 0);
} }
uint16_t IRAM_ATTR WS2812FX::getMappedPixelIndex(uint16_t index) { uint16_t IRAM_ATTR WS2812FX::getMappedPixelIndex(uint16_t index) {

173
wled00/bus_manager.cpp Executable file → Normal file
View File

@ -9,10 +9,10 @@
#include "bus_wrapper.h" #include "bus_wrapper.h"
#include "bus_manager.h" #include "bus_manager.h"
extern bool cctICused;
//colors.cpp //colors.cpp
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb); uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
uint16_t approximateKelvinFromRGB(uint32_t rgb);
void colorRGBtoRGBW(byte* rgb);
//udp.cpp //udp.cpp
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte *buffer, uint8_t bri=255, bool isRGBW=false); uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte *buffer, uint8_t bri=255, bool isRGBW=false);
@ -123,13 +123,13 @@ BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com)
} }
_iType = PolyBus::getI(bc.type, _pins, nr); _iType = PolyBus::getI(bc.type, _pins, nr);
if (_iType == I_NONE) return; if (_iType == I_NONE) return;
if (bc.doubleBuffer && !allocData(bc.count * (Bus::hasWhite(_type) + 3*Bus::hasRGB(_type)))) return; //warning: hardcoded channel count if (bc.doubleBuffer && !allocData(bc.count * Bus::getNumberOfChannels(bc.type))) return;
//_buffering = bc.doubleBuffer; //_buffering = bc.doubleBuffer;
uint16_t lenToCreate = bc.count; 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 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, _frequencykHz); _busPtr = PolyBus::create(_iType, _pins, lenToCreate + _skip, nr, _frequencykHz);
_valid = (_busPtr != nullptr); _valid = (_busPtr != nullptr);
DEBUG_PRINTF("%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], _pins[1], _iType, _milliAmpsPerLed, _milliAmpsMax); 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], IS_2PIN(bc.type)?_pins[1]:255, _iType, _milliAmpsPerLed, _milliAmpsMax);
} }
//fine tune power estimation constants for your setup //fine tune power estimation constants for your setup
@ -206,13 +206,15 @@ void BusDigital::show() {
_milliAmpsTotal = 0; _milliAmpsTotal = 0;
if (!_valid) return; if (!_valid) return;
uint8_t cctWW = 0, cctCW = 0;
uint8_t newBri = estimateCurrentAndLimitBri(); // will fill _milliAmpsTotal uint8_t newBri = estimateCurrentAndLimitBri(); // will fill _milliAmpsTotal
if (newBri < _bri) PolyBus::setBrightness(_busPtr, _iType, newBri); // limit brightness to stay within current limits if (newBri < _bri) PolyBus::setBrightness(_busPtr, _iType, newBri); // limit brightness to stay within current limits
if (_data) { // use _buffering this causes ~20% FPS drop if (_data) {
size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type); size_t channels = getNumberOfChannels();
int16_t oldCCT = Bus::_cct; // temporarily save bus CCT
for (size_t i=0; i<_len; i++) { for (size_t i=0; i<_len; i++) {
size_t offset = i*channels; size_t offset = i * channels;
uint8_t co = _colorOrderMap.getPixelColorOrder(i+_start, _colorOrder); uint8_t co = _colorOrderMap.getPixelColorOrder(i+_start, _colorOrder);
uint32_t c; uint32_t c;
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs (_len is always a multiple of 3) if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs (_len is always a multiple of 3)
@ -222,17 +224,26 @@ void BusDigital::show() {
case 2: c = RGBW32(_data[offset-2], _data[offset-1], _data[offset] , 0); break; case 2: c = RGBW32(_data[offset-2], _data[offset-1], _data[offset] , 0); break;
} }
} else { } else {
c = RGBW32(_data[offset],_data[offset+1],_data[offset+2],(Bus::hasWhite(_type)?_data[offset+3]:0)); if (hasRGB()) c = RGBW32(_data[offset], _data[offset+1], _data[offset+2], hasWhite() ? _data[offset+3] : 0);
else c = RGBW32(0, 0, 0, _data[offset]);
}
if (hasCCT()) {
// unfortunately as a segment may span multiple buses or a bus may contain multiple segments and each segment may have different CCT
// we need to extract and appy CCT value for each pixel individually even though all buses share the same _cct variable
// 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);
} }
uint16_t pix = i; uint16_t pix = i;
if (_reversed) pix = _len - pix -1; if (_reversed) pix = _len - pix -1;
pix += _skip; pix += _skip;
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co); PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, (cctCW<<8) | cctWW);
} }
#if !defined(STATUSLED) || STATUSLED>=0 #if !defined(STATUSLED) || STATUSLED>=0
if (_skip) PolyBus::setPixelColor(_busPtr, _iType, 0, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black if (_skip) PolyBus::setPixelColor(_busPtr, _iType, 0, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black
#endif #endif
for (int i=1; i<_skip; i++) PolyBus::setPixelColor(_busPtr, _iType, i, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black for (int i=1; i<_skip; i++) PolyBus::setPixelColor(_busPtr, _iType, i, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black
Bus::_cct = oldCCT;
} else { } else {
if (newBri < _bri) { if (newBri < _bri) {
uint16_t hwLen = _len; uint16_t hwLen = _len;
@ -240,7 +251,8 @@ void BusDigital::show() {
for (unsigned i = 0; i < hwLen; i++) { for (unsigned i = 0; i < hwLen; i++) {
// use 0 as color order, actual order does not matter here as we just update the channel values as-is // use 0 as color order, actual order does not matter here as we just update the channel values as-is
uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, i, 0), _bri); uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, i, 0), _bri);
PolyBus::setPixelColor(_busPtr, _iType, i, c, 0); // repaint all pixels with new brightness if (hasCCT()) Bus::calculateCCT(c, cctWW, cctCW); // this will unfortunately corrupt (segment) CCT data on every bus
PolyBus::setPixelColor(_busPtr, _iType, i, c, 0, (cctCW<<8) | cctWW); // repaint all pixels with new brightness
} }
} }
} }
@ -279,17 +291,20 @@ void BusDigital::setStatusPixel(uint32_t c) {
void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) { void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) {
if (!_valid) return; if (!_valid) return;
if (Bus::hasWhite(_type)) c = autoWhiteCalc(c); uint8_t cctWW = 0, cctCW = 0;
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT if (hasWhite()) c = autoWhiteCalc(c);
if (_data) { // use _buffering this causes ~20% FPS drop if (Bus::_cct >= 1900) c = colorBalanceFromKelvin(Bus::_cct, c); //color correction from CCT
size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type); if (_data) {
size_t offset = pix*channels; size_t offset = pix * getNumberOfChannels();
if (Bus::hasRGB(_type)) { if (hasRGB()) {
_data[offset++] = R(c); _data[offset++] = R(c);
_data[offset++] = G(c); _data[offset++] = G(c);
_data[offset++] = B(c); _data[offset++] = B(c);
} }
if (Bus::hasWhite(_type)) _data[offset] = W(c); if (hasWhite()) _data[offset++] = W(c);
// unfortunately as a segment may span multiple buses or a bus may contain multiple segments and each segment may have different CCT
// we need to store CCT value for each pixel (if there is a color correction in play, convert K in CCT ratio)
if (hasCCT()) _data[offset] = Bus::_cct >= 1900 ? (Bus::_cct - 1900) >> 5 : (Bus::_cct < 0 ? 127 : Bus::_cct); // TODO: if _cct == -1 we simply ignore it
} else { } else {
if (_reversed) pix = _len - pix -1; if (_reversed) pix = _len - pix -1;
pix += _skip; pix += _skip;
@ -304,21 +319,21 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) {
case 2: c = RGBW32(R(cOld), G(cOld), W(c) , 0); break; case 2: c = RGBW32(R(cOld), G(cOld), W(c) , 0); break;
} }
} }
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co); if (hasCCT()) Bus::calculateCCT(c, cctWW, cctCW);
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, (cctCW<<8) | cctWW);
} }
} }
// returns original color if global buffering is enabled, else returns lossly restored color from bus // returns original color if global buffering is enabled, else returns lossly restored color from bus
uint32_t IRAM_ATTR BusDigital::getPixelColor(uint16_t pix) { uint32_t IRAM_ATTR BusDigital::getPixelColor(uint16_t pix) {
if (!_valid) return 0; if (!_valid) return 0;
if (_data) { // use _buffering this causes ~20% FPS drop if (_data) {
size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type); size_t offset = pix * getNumberOfChannels();
size_t offset = pix*channels;
uint32_t c; uint32_t c;
if (!Bus::hasRGB(_type)) { if (!hasRGB()) {
c = RGBW32(_data[offset], _data[offset], _data[offset], _data[offset]); c = RGBW32(_data[offset], _data[offset], _data[offset], _data[offset]);
} else { } else {
c = RGBW32(_data[offset], _data[offset+1], _data[offset+2], Bus::hasWhite(_type) ? _data[offset+3] : 0); c = RGBW32(_data[offset], _data[offset+1], _data[offset+2], hasWhite() ? _data[offset+3] : 0);
} }
return c; return c;
} else { } else {
@ -377,13 +392,22 @@ BusPwm::BusPwm(BusConfig &bc)
_frequency = bc.frequency ? bc.frequency : WLED_PWM_FREQ; _frequency = bc.frequency ? bc.frequency : WLED_PWM_FREQ;
#ifdef ESP8266 #ifdef ESP8266
analogWriteRange(255); //same range as one RGB channel // duty cycle resolution (_depth) can be extracted from this formula: 1MHz > _frequency * 2^_depth
if (_frequency > 1760) _depth = 8;
else if (_frequency > 880) _depth = 9;
else _depth = 10; // WLED_PWM_FREQ <= 880Hz
analogWriteRange((1<<_depth)-1);
analogWriteFreq(_frequency); analogWriteFreq(_frequency);
#else #else
_ledcStart = pinManager.allocateLedc(numPins); _ledcStart = pinManager.allocateLedc(numPins);
if (_ledcStart == 255) { //no more free LEDC channels if (_ledcStart == 255) { //no more free LEDC channels
deallocatePins(); return; deallocatePins(); return;
} }
// duty cycle resolution (_depth) can be extracted from this formula: 80MHz > _frequency * 2^_depth
if (_frequency > 78124) _depth = 9;
else if (_frequency > 39062) _depth = 10;
else if (_frequency > 19531) _depth = 11;
else _depth = 12; // WLED_PWM_FREQ <= 19531Hz
#endif #endif
for (unsigned i = 0; i < numPins; i++) { for (unsigned i = 0; i < numPins; i++) {
@ -395,7 +419,7 @@ BusPwm::BusPwm(BusConfig &bc)
#ifdef ESP8266 #ifdef ESP8266
pinMode(_pins[i], OUTPUT); pinMode(_pins[i], OUTPUT);
#else #else
ledcSetup(_ledcStart + i, _frequency, 8); ledcSetup(_ledcStart + i, _frequency, _depth);
ledcAttachPin(_pins[i], _ledcStart + i); ledcAttachPin(_pins[i], _ledcStart + i);
#endif #endif
} }
@ -406,48 +430,31 @@ BusPwm::BusPwm(BusConfig &bc)
void BusPwm::setPixelColor(uint16_t pix, uint32_t c) { void BusPwm::setPixelColor(uint16_t pix, uint32_t c) {
if (pix != 0 || !_valid) return; //only react to first pixel if (pix != 0 || !_valid) return; //only react to first pixel
if (_type != TYPE_ANALOG_3CH) c = autoWhiteCalc(c); if (_type != TYPE_ANALOG_3CH) c = autoWhiteCalc(c);
if (_cct >= 1900 && (_type == TYPE_ANALOG_3CH || _type == TYPE_ANALOG_4CH)) { if (Bus::_cct >= 1900 && (_type == TYPE_ANALOG_3CH || _type == TYPE_ANALOG_4CH)) {
c = colorBalanceFromKelvin(_cct, c); //color correction from CCT c = colorBalanceFromKelvin(Bus::_cct, c); //color correction from CCT
} }
uint8_t r = R(c); uint8_t r = R(c);
uint8_t g = G(c); uint8_t g = G(c);
uint8_t b = B(c); uint8_t b = B(c);
uint8_t w = W(c); uint8_t w = W(c);
uint8_t cct = 0; //0 - full warm white, 255 - full cold white
if (_cct > -1) {
if (_cct >= 1900) cct = (_cct - 1900) >> 5;
else if (_cct < 256) cct = _cct;
} else {
cct = (approximateKelvinFromRGB(c) - 1900) >> 5;
}
uint8_t ww, cw;
#ifdef WLED_USE_IC_CCT
ww = w;
cw = cct;
#else
//0 - linear (CCT 127 = 50% warm, 50% cold), 127 - additive CCT blending (CCT 127 = 100% warm, 100% cold)
if (cct < _cctBlend) ww = 255;
else ww = ((255-cct) * 255) / (255 - _cctBlend);
if ((255-cct) < _cctBlend) cw = 255;
else cw = (cct * 255) / (255 - _cctBlend);
ww = (w * ww) / 255; //brightness scaling
cw = (w * cw) / 255;
#endif
switch (_type) { switch (_type) {
case TYPE_ANALOG_1CH: //one channel (white), relies on auto white calculation case TYPE_ANALOG_1CH: //one channel (white), relies on auto white calculation
_data[0] = w; _data[0] = w;
break; break;
case TYPE_ANALOG_2CH: //warm white + cold white case TYPE_ANALOG_2CH: //warm white + cold white
_data[1] = cw; if (cctICused) {
_data[0] = ww; _data[0] = w;
_data[1] = Bus::_cct < 0 || Bus::_cct > 255 ? 127 : Bus::_cct;
} else {
Bus::calculateCCT(c, _data[0], _data[1]);
}
break; break;
case TYPE_ANALOG_5CH: //RGB + warm white + cold white case TYPE_ANALOG_5CH: //RGB + warm white + cold white
_data[4] = cw; if (cctICused)
w = ww; _data[4] = Bus::_cct < 0 || Bus::_cct > 255 ? 127 : Bus::_cct;
else
Bus::calculateCCT(c, w, _data[4]);
case TYPE_ANALOG_4CH: //RGBW case TYPE_ANALOG_4CH: //RGBW
_data[3] = w; _data[3] = w;
case TYPE_ANALOG_3CH: //standard dumb RGB case TYPE_ANALOG_3CH: //standard dumb RGB
@ -462,12 +469,49 @@ uint32_t BusPwm::getPixelColor(uint16_t pix) {
return RGBW32(_data[0], _data[1], _data[2], _data[3]); return RGBW32(_data[0], _data[1], _data[2], _data[3]);
} }
#ifndef ESP8266
static const uint16_t cieLUT[256] = {
0, 2, 4, 5, 7, 9, 11, 13, 15, 16,
18, 20, 22, 24, 26, 27, 29, 31, 33, 35,
34, 36, 37, 39, 41, 43, 45, 47, 49, 52,
54, 56, 59, 61, 64, 67, 69, 72, 75, 78,
81, 84, 87, 90, 94, 97, 100, 104, 108, 111,
115, 119, 123, 127, 131, 136, 140, 144, 149, 154,
158, 163, 168, 173, 178, 183, 189, 194, 200, 205,
211, 217, 223, 229, 235, 241, 247, 254, 261, 267,
274, 281, 288, 295, 302, 310, 317, 325, 333, 341,
349, 357, 365, 373, 382, 391, 399, 408, 417, 426,
436, 445, 455, 464, 474, 484, 494, 505, 515, 526,
536, 547, 558, 569, 580, 592, 603, 615, 627, 639,
651, 663, 676, 689, 701, 714, 727, 741, 754, 768,
781, 795, 809, 824, 838, 853, 867, 882, 897, 913,
928, 943, 959, 975, 991, 1008, 1024, 1041, 1058, 1075,
1092, 1109, 1127, 1144, 1162, 1180, 1199, 1217, 1236, 1255,
1274, 1293, 1312, 1332, 1352, 1372, 1392, 1412, 1433, 1454,
1475, 1496, 1517, 1539, 1561, 1583, 1605, 1628, 1650, 1673,
1696, 1719, 1743, 1767, 1791, 1815, 1839, 1864, 1888, 1913,
1939, 1964, 1990, 2016, 2042, 2068, 2095, 2121, 2148, 2176,
2203, 2231, 2259, 2287, 2315, 2344, 2373, 2402, 2431, 2461,
2491, 2521, 2551, 2581, 2612, 2643, 2675, 2706, 2738, 2770,
2802, 2835, 2867, 2900, 2934, 2967, 3001, 3035, 3069, 3104,
3138, 3174, 3209, 3244, 3280, 3316, 3353, 3389, 3426, 3463,
3501, 3539, 3576, 3615, 3653, 3692, 3731, 3770, 3810, 3850,
3890, 3930, 3971, 4012, 4053, 4095
};
#endif
void BusPwm::show() { void BusPwm::show() {
if (!_valid) return; if (!_valid) return;
uint8_t numPins = NUM_PWM_PINS(_type); uint8_t numPins = NUM_PWM_PINS(_type);
unsigned maxBri = (1<<_depth) - 1;
#ifdef ESP8266
unsigned pwmBri = (unsigned)(roundf(powf((float)_bri / 255.0f, 1.7f) * (float)maxBri)); // using gamma 1.7 to extrapolate PWM duty cycle
#else
unsigned pwmBri = cieLUT[_bri] >> (12 - _depth); // use CIE LUT
#endif
for (unsigned i = 0; i < numPins; i++) { for (unsigned i = 0; i < numPins; i++) {
uint8_t scaled = (_data[i] * _bri) / 255; unsigned scaled = (_data[i] * pwmBri) / 255;
if (_reversed) scaled = 255 - scaled; if (_reversed) scaled = maxBri - scaled;
#ifdef ESP8266 #ifdef ESP8266
analogWrite(_pins[i], scaled); analogWrite(_pins[i], scaled);
#else #else
@ -554,6 +598,10 @@ BusNetwork::BusNetwork(BusConfig &bc)
_rgbw = false; _rgbw = false;
_UDPtype = 2; _UDPtype = 2;
break; break;
case TYPE_NET_ARTNET_RGBW:
_rgbw = true;
_UDPtype = 2;
break;
case TYPE_NET_E131_RGB: case TYPE_NET_E131_RGB:
_rgbw = false; _rgbw = false;
_UDPtype = 1; _UDPtype = 1;
@ -571,7 +619,7 @@ BusNetwork::BusNetwork(BusConfig &bc)
void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) { void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) {
if (!_valid || pix >= _len) return; if (!_valid || pix >= _len) return;
if (_rgbw) c = autoWhiteCalc(c); if (_rgbw) c = autoWhiteCalc(c);
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT if (Bus::_cct >= 1900) c = colorBalanceFromKelvin(Bus::_cct, c); //color correction from CCT
uint16_t offset = pix * _UDPchannels; uint16_t offset = pix * _UDPchannels;
_data[offset] = R(c); _data[offset] = R(c);
_data[offset+1] = G(c); _data[offset+1] = G(c);
@ -611,25 +659,18 @@ uint32_t BusManager::memUsage(BusConfig &bc) {
if (bc.type == TYPE_ONOFF || IS_PWM(bc.type)) return 5; if (bc.type == TYPE_ONOFF || IS_PWM(bc.type)) return 5;
uint16_t len = bc.count + bc.skipAmount; uint16_t len = bc.count + bc.skipAmount;
uint16_t channels = 3; uint16_t channels = Bus::getNumberOfChannels(bc.type);
uint16_t multiplier = 1; uint16_t multiplier = 1;
if (IS_DIGITAL(bc.type)) { // digital types if (IS_DIGITAL(bc.type)) { // digital types
if (IS_16BIT(bc.type)) len *= 2; // 16-bit LEDs if (IS_16BIT(bc.type)) len *= 2; // 16-bit LEDs
#ifdef ESP8266 #ifdef ESP8266
if (bc.type > 28) channels = 4; //RGBW
if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem
multiplier = 5; multiplier = 5;
} }
#else //ESP32 RMT uses double buffer, I2S uses 5x buffer #else //ESP32 RMT uses double buffer, I2S uses 5x buffer
if (bc.type > 28) channels = 4; //RGBW
multiplier = 2; multiplier = 2;
#endif #endif
} }
if (IS_VIRTUAL(bc.type)) {
switch (bc.type) {
case TYPE_NET_DDP_RGBW: channels = 4; break;
}
}
return len * channels * multiplier; //RGB return len * channels * multiplier; //RGB
} }
@ -691,7 +732,7 @@ void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) {
if (cct >= 0) { if (cct >= 0) {
//if white balance correction allowed, save as kelvin value instead of 0-255 //if white balance correction allowed, save as kelvin value instead of 0-255
if (allowWBCorrection) cct = 1900 + (cct << 5); if (allowWBCorrection) cct = 1900 + (cct << 5);
} else cct = -1; } else cct = -1; // will use kelvin approximation from RGB
Bus::setCCT(cct); Bus::setCCT(cct);
} }

View File

@ -7,6 +7,9 @@
#include "const.h" #include "const.h"
//colors.cpp
uint16_t approximateKelvinFromRGB(uint32_t rgb);
#define GET_BIT(var,bit) (((var)>>(bit))&0x01) #define GET_BIT(var,bit) (((var)>>(bit))&0x01)
#define SET_BIT(var,bit) ((var)|=(uint16_t)(0x0001<<(bit))) #define SET_BIT(var,bit) ((var)|=(uint16_t)(0x0001<<(bit)))
#define UNSET_BIT(var,bit) ((var)&=(~(uint16_t)(0x0001<<(bit)))) #define UNSET_BIT(var,bit) ((var)&=(~(uint16_t)(0x0001<<(bit))))
@ -32,7 +35,7 @@ struct BusConfig {
uint8_t skipAmount; uint8_t skipAmount;
bool refreshReq; bool refreshReq;
uint8_t autoWhite; uint8_t autoWhite;
uint8_t pins[5] = {LEDPIN, 255, 255, 255, 255}; uint8_t pins[5] = {255, 255, 255, 255, 255};
uint16_t frequency; uint16_t frequency;
bool doubleBuffer; bool doubleBuffer;
uint8_t milliAmpsPerLed; uint8_t milliAmpsPerLed;
@ -53,9 +56,9 @@ struct BusConfig {
refreshReq = (bool) GET_BIT(busType,7); refreshReq = (bool) GET_BIT(busType,7);
type = busType & 0x7F; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh) type = busType & 0x7F; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh)
size_t nPins = 1; size_t nPins = 1;
if (type >= TYPE_NET_DDP_RGB && type < 96) nPins = 4; //virtual network bus. 4 "pins" store IP address if (IS_VIRTUAL(type)) nPins = 4; //virtual network bus. 4 "pins" store IP address
else if (type > 47) nPins = 2; else if (IS_2PIN(type)) nPins = 2;
else if (type > 40 && type < 46) nPins = NUM_PWM_PINS(type); else if (IS_PWM(type)) nPins = NUM_PWM_PINS(type);
for (size_t i = 0; i < nPins; i++) pins[i] = ppins[i]; for (size_t i = 0; i < nPins; i++) pins[i] = ppins[i];
} }
@ -138,6 +141,8 @@ class Bus {
virtual uint16_t getLEDCurrent() { return 0; } virtual uint16_t getLEDCurrent() { return 0; }
virtual uint16_t getUsedCurrent() { return 0; } virtual uint16_t getUsedCurrent() { return 0; }
virtual uint16_t getMaxCurrent() { return 0; } virtual uint16_t getMaxCurrent() { return 0; }
virtual uint8_t getNumberOfChannels() { return hasWhite(_type) + 3*hasRGB(_type) + hasCCT(_type); }
static inline uint8_t getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); }
inline void setReversed(bool reversed) { _reversed = reversed; } inline void setReversed(bool reversed) { _reversed = reversed; }
inline uint16_t getStart() { return _start; } inline uint16_t getStart() { return _start; }
inline void setStart(uint16_t start) { _start = start; } inline void setStart(uint16_t start) { _start = start; }
@ -154,18 +159,22 @@ class Bus {
} }
virtual bool hasWhite(void) { return Bus::hasWhite(_type); } virtual bool hasWhite(void) { return Bus::hasWhite(_type); }
static bool hasWhite(uint8_t type) { static bool hasWhite(uint8_t type) {
if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_SK6812_RGBW || type == TYPE_TM1814 || type == TYPE_UCS8904) return true; // digital types with white channel if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) ||
type == TYPE_SK6812_RGBW || type == TYPE_TM1814 || type == TYPE_UCS8904 ||
type == TYPE_FW1906 || type == TYPE_WS2805) return true; // digital types with white channel
if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true; // analog types with white channel if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true; // analog types with white channel
if (type == TYPE_NET_DDP_RGBW) return true; // network types with white channel if (type == TYPE_NET_DDP_RGBW || type == TYPE_NET_ARTNET_RGBW) return true; // network types with white channel
return false; return false;
} }
virtual bool hasCCT(void) { return Bus::hasCCT(_type); } virtual bool hasCCT(void) { return Bus::hasCCT(_type); }
static bool hasCCT(uint8_t type) { static bool hasCCT(uint8_t type) {
if (type == TYPE_WS2812_2CH_X3 || type == TYPE_WS2812_WWA || if (type == TYPE_WS2812_2CH_X3 || type == TYPE_WS2812_WWA ||
type == TYPE_ANALOG_2CH || type == TYPE_ANALOG_5CH) return true; type == TYPE_ANALOG_2CH || type == TYPE_ANALOG_5CH ||
type == TYPE_FW1906 || type == TYPE_WS2805 ) return true;
return false; return false;
} }
static void setCCT(uint16_t cct) { static int16_t getCCT() { return _cct; }
static void setCCT(int16_t cct) {
_cct = cct; _cct = cct;
} }
static void setCCTBlend(uint8_t b) { static void setCCTBlend(uint8_t b) {
@ -176,6 +185,26 @@ class Bus {
if (_cctBlend > WLED_MAX_CCT_BLEND) _cctBlend = WLED_MAX_CCT_BLEND; if (_cctBlend > WLED_MAX_CCT_BLEND) _cctBlend = WLED_MAX_CCT_BLEND;
#endif #endif
} }
static void calculateCCT(uint32_t c, uint8_t &ww, uint8_t &cw) {
uint8_t cct = 0; //0 - full warm white, 255 - full cold white
uint8_t w = byte(c >> 24);
if (_cct > -1) {
if (_cct >= 1900) cct = (_cct - 1900) >> 5;
else if (_cct < 256) cct = _cct;
} else {
cct = (approximateKelvinFromRGB(c) - 1900) >> 5;
}
//0 - linear (CCT 127 = 50% warm, 50% cold), 127 - additive CCT blending (CCT 127 = 100% warm, 100% cold)
if (cct < _cctBlend) ww = 255;
else ww = ((255-cct) * 255) / (255 - _cctBlend);
if ((255-cct) < _cctBlend) cw = 255;
else cw = (cct * 255) / (255 - _cctBlend);
ww = (w * ww) / 255; //brightness scaling
cw = (w * cw) / 255;
}
inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; } inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; }
inline uint8_t getAutoWhiteMode() { return _autoWhiteMode; } inline uint8_t getAutoWhiteMode() { return _autoWhiteMode; }
inline static void setGlobalAWMode(uint8_t m) { if (m < 5) _gAWM = m; else _gAWM = AW_GLOBAL_DISABLED; } inline static void setGlobalAWMode(uint8_t m) { if (m < 5) _gAWM = m; else _gAWM = AW_GLOBAL_DISABLED; }
@ -191,8 +220,17 @@ class Bus {
bool _needsRefresh; bool _needsRefresh;
uint8_t _autoWhiteMode; uint8_t _autoWhiteMode;
uint8_t *_data; uint8_t *_data;
// global Auto White Calculation override
static uint8_t _gAWM; static uint8_t _gAWM;
// _cct has the following menaings (see calculateCCT() & BusManager::setSegmentCCT()):
// -1 means to extract approximate CCT value in K from RGB (in calcualteCCT())
// [0,255] is the exact CCT value where 0 means warm and 255 cold
// [1900,10060] only for color correction expressed in K (colorBalanceFromKelvin())
static int16_t _cct; static int16_t _cct;
// _cctBlend determines WW/CW blending:
// 0 - linear (CCT 127 => 50% warm, 50% cold)
// 63 - semi additive/nonlinear (CCT 127 => 66% warm, 66% cold)
// 127 - additive CCT blending (CCT 127 => 100% warm, 100% cold)
static uint8_t _cctBlend; static uint8_t _cctBlend;
uint32_t autoWhiteCalc(uint32_t c); uint32_t autoWhiteCalc(uint32_t c);
@ -268,6 +306,7 @@ class BusPwm : public Bus {
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
uint8_t _ledcStart; uint8_t _ledcStart;
#endif #endif
uint8_t _depth;
uint16_t _frequency; uint16_t _frequency;
void deallocatePins(); void deallocatePins();
@ -333,9 +372,12 @@ class BusManager {
static void setStatusPixel(uint32_t c); static void setStatusPixel(uint32_t c);
static void setPixelColor(uint16_t pix, uint32_t c); static void setPixelColor(uint16_t pix, uint32_t c);
static void setBrightness(uint8_t b); 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 void setSegmentCCT(int16_t cct, bool allowWBCorrection = false);
static void setMilliampsMax(uint16_t max) { _milliAmpsMax = max;} static void setMilliampsMax(uint16_t max) { _milliAmpsMax = max;}
static uint32_t getPixelColor(uint16_t pix); static uint32_t getPixelColor(uint16_t pix);
static inline int16_t getSegmentCCT() { return Bus::getCCT(); }
static Bus* getBus(uint8_t busNr); static Bus* getBus(uint8_t busNr);

View File

@ -2,6 +2,7 @@
#define BusWrapper_h #define BusWrapper_h
#include "NeoPixelBusLg.h" #include "NeoPixelBusLg.h"
#include "bus_manager.h"
// temporary - these defines should actually be set in platformio.ini // temporary - these defines should actually be set in platformio.ini
// C3: I2S0 and I2S1 methods not supported (has one I2S bus) // C3: I2S0 and I2S1 methods not supported (has one I2S bus)
@ -63,52 +64,64 @@
#define I_8266_U1_UCS_4 54 #define I_8266_U1_UCS_4 54
#define I_8266_DM_UCS_4 55 #define I_8266_DM_UCS_4 55
#define I_8266_BB_UCS_4 56 #define I_8266_BB_UCS_4 56
//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
//ESP8266 APA106 //ESP8266 APA106
#define I_8266_U0_APA106_3 81 #define I_8266_U0_APA106_3 81
#define I_8266_U1_APA106_3 82 #define I_8266_U1_APA106_3 82
#define I_8266_DM_APA106_3 83 #define I_8266_DM_APA106_3 83
#define I_8266_BB_APA106_3 84 #define I_8266_BB_APA106_3 84
//WS2805
#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
/*** ESP32 Neopixel methods ***/ /*** ESP32 Neopixel methods ***/
//RGB //RGB
#define I_32_RN_NEO_3 21 #define I_32_RN_NEO_3 21
#define I_32_I0_NEO_3 22 #define I_32_I0_NEO_3 22
#define I_32_I1_NEO_3 23 #define I_32_I1_NEO_3 23
#define I_32_BB_NEO_3 24 // bitbanging on ESP32 not recommended
//RGBW //RGBW
#define I_32_RN_NEO_4 25 #define I_32_RN_NEO_4 25
#define I_32_I0_NEO_4 26 #define I_32_I0_NEO_4 26
#define I_32_I1_NEO_4 27 #define I_32_I1_NEO_4 27
#define I_32_BB_NEO_4 28 // bitbanging on ESP32 not recommended
//400Kbps //400Kbps
#define I_32_RN_400_3 29 #define I_32_RN_400_3 29
#define I_32_I0_400_3 30 #define I_32_I0_400_3 30
#define I_32_I1_400_3 31 #define I_32_I1_400_3 31
#define I_32_BB_400_3 32 // bitbanging on ESP32 not recommended
//TM1814 (RGBW) //TM1814 (RGBW)
#define I_32_RN_TM1_4 33 #define I_32_RN_TM1_4 33
#define I_32_I0_TM1_4 34 #define I_32_I0_TM1_4 34
#define I_32_I1_TM1_4 35 #define I_32_I1_TM1_4 35
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
//TM1829 (RGB) //TM1829 (RGB)
#define I_32_RN_TM2_3 36 #define I_32_RN_TM2_3 36
#define I_32_I0_TM2_3 37 #define I_32_I0_TM2_3 37
#define I_32_I1_TM2_3 38 #define I_32_I1_TM2_3 38
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
//UCS8903 (RGB) //UCS8903 (RGB)
#define I_32_RN_UCS_3 57 #define I_32_RN_UCS_3 57
#define I_32_I0_UCS_3 58 #define I_32_I0_UCS_3 58
#define I_32_I1_UCS_3 59 #define I_32_I1_UCS_3 59
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
//UCS8904 (RGBW) //UCS8904 (RGBW)
#define I_32_RN_UCS_4 60 #define I_32_RN_UCS_4 60
#define I_32_I0_UCS_4 61 #define I_32_I0_UCS_4 61
#define I_32_I1_UCS_4 62 #define I_32_I1_UCS_4 62
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S) //FW1906 GRBCW
#define I_32_RN_FW6_5 63
#define I_32_I0_FW6_5 64
#define I_32_I1_FW6_5 65
//APA106
#define I_32_RN_APA106_3 85 #define I_32_RN_APA106_3 85
#define I_32_I0_APA106_3 86 #define I_32_I0_APA106_3 86
#define I_32_I1_APA106_3 87 #define I_32_I1_APA106_3 87
#define I_32_BB_APA106_3 88 // bitbangging on ESP32 not recommended //WS2805
#define I_32_RN_2805_5 93
#define I_32_I0_2805_5 94
#define I_32_I1_2805_5 95
//APA102 //APA102
#define I_HS_DOT_3 39 //hardware SPI #define I_HS_DOT_3 39 //hardware SPI
@ -176,6 +189,16 @@
#define B_8266_U1_APA106_3 NeoPixelBusLg<NeoRbgFeature, NeoEsp8266Uart1Apa106Method, NeoGammaNullMethod> //3 chan, esp8266, gpio2 #define B_8266_U1_APA106_3 NeoPixelBusLg<NeoRbgFeature, NeoEsp8266Uart1Apa106Method, NeoGammaNullMethod> //3 chan, esp8266, gpio2
#define B_8266_DM_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266DmaApa106Method, NeoGammaNullMethod> //3 chan, esp8266, gpio3 #define B_8266_DM_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266DmaApa106Method, NeoGammaNullMethod> //3 chan, esp8266, gpio3
#define B_8266_BB_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266BitBangApa106Method, NeoGammaNullMethod> //3 chan, esp8266, bb (any pin but 16) #define B_8266_BB_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266BitBangApa106Method, NeoGammaNullMethod> //3 chan, esp8266, bb (any pin but 16)
//FW1906 GRBCW
#define B_8266_U0_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp8266Uart0Ws2813Method, NeoGammaNullMethod> //esp8266, gpio1
#define B_8266_U1_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod> //esp8266, gpio2
#define B_8266_DM_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp8266Dma800KbpsMethod, NeoGammaNullMethod> //esp8266, gpio3
#define B_8266_BB_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp8266BitBang800KbpsMethod, NeoGammaNullMethod> //esp8266, bb
//WS2805 GRBCW
#define B_8266_U0_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp8266Uart0Ws2805Method, NeoGammaNullMethod> //esp8266, gpio1
#define B_8266_U1_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp8266Uart1Ws2805Method, NeoGammaNullMethod> //esp8266, gpio2
#define B_8266_DM_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp8266DmaWs2805Method, NeoGammaNullMethod> //esp8266, gpio3
#define B_8266_BB_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp8266BitBangWs2805Method, NeoGammaNullMethod> //esp8266, bb
#endif #endif
/*** ESP32 Neopixel methods ***/ /*** ESP32 Neopixel methods ***/
@ -183,75 +206,102 @@
//RGB //RGB
#define B_32_RN_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod> #define B_32_RN_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod>
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
#define B_32_I0_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s0800KbpsMethod, NeoGammaNullMethod> #define B_32_I0_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s0Ws2812xMethod, NeoGammaNullMethod>
//#define B_32_I0_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s0X8Ws2812xMethod, NeoGammaNullMethod> // parallel I2S
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
#define B_32_I1_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1800KbpsMethod, NeoGammaNullMethod> #define B_32_I1_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1Ws2812xMethod, NeoGammaNullMethod>
//#define B_32_I1_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1X8Ws2812xMethod, NeoGammaNullMethod> // parallel I2S
#endif #endif
//#define B_32_BB_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32BitBang800KbpsMethod, NeoGammaNullMethod> // NeoEsp8266BitBang800KbpsMethod
//RGBW //RGBW
#define B_32_RN_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod> #define B_32_RN_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32RmtNSk6812Method, NeoGammaNullMethod>
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
#define B_32_I0_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32I2s0800KbpsMethod, NeoGammaNullMethod> #define B_32_I0_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32I2s0Sk6812Method, NeoGammaNullMethod>
//#define B_32_I0_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32I2s0X8Sk6812Method, NeoGammaNullMethod> // parallel I2S
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
#define B_32_I1_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32I2s1800KbpsMethod, NeoGammaNullMethod> #define B_32_I1_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32I2s1Sk6812Method, NeoGammaNullMethod>
//#define B_32_I1_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32I2s1X8Sk6812Method, NeoGammaNullMethod> // parallel I2S
#endif #endif
//#define B_32_BB_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32BitBang800KbpsMethod, NeoGammaNullMethod> // NeoEsp8266BitBang800KbpsMethod
//400Kbps //400Kbps
#define B_32_RN_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32RmtN400KbpsMethod, NeoGammaNullMethod> #define B_32_RN_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32RmtN400KbpsMethod, NeoGammaNullMethod>
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
#define B_32_I0_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s0400KbpsMethod, NeoGammaNullMethod> #define B_32_I0_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s0400KbpsMethod, NeoGammaNullMethod>
//#define B_32_I0_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s0X8400KbpsMethod, NeoGammaNullMethod> // parallel I2S
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
#define B_32_I1_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1400KbpsMethod, NeoGammaNullMethod> #define B_32_I1_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1400KbpsMethod, NeoGammaNullMethod>
//#define B_32_I1_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1X8400KbpsMethod, NeoGammaNullMethod> // parallel I2S
#endif #endif
//#define B_32_BB_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32BitBang400KbpsMethod, NeoGammaNullMethod> // NeoEsp8266BitBang400KbpsMethod
//TM1814 (RGBW) //TM1814 (RGBW)
#define B_32_RN_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp32RmtNTm1814Method, NeoGammaNullMethod> #define B_32_RN_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp32RmtNTm1814Method, NeoGammaNullMethod>
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
#define B_32_I0_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp32I2s0Tm1814Method, NeoGammaNullMethod> #define B_32_I0_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp32I2s0Tm1814Method, NeoGammaNullMethod>
//#define B_32_I0_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp32I2s0X8Tm1814Method, NeoGammaNullMethod> // parallel I2S
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
#define B_32_I1_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp32I2s1Tm1814Method, NeoGammaNullMethod> #define B_32_I1_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp32I2s1Tm1814Method, NeoGammaNullMethod>
//#define B_32_I1_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp32I2s1X8Tm1814Method, NeoGammaNullMethod> // parallel I2S
#endif #endif
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
//TM1829 (RGB) //TM1829 (RGB)
#define B_32_RN_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp32RmtNTm1829Method, NeoGammaNullMethod> #define B_32_RN_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp32RmtNTm1829Method, NeoGammaNullMethod>
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
#define B_32_I0_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp32I2s0Tm1829Method, NeoGammaNullMethod> #define B_32_I0_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp32I2s0Tm1829Method, NeoGammaNullMethod>
//#define B_32_I0_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp32I2s0X8Tm1829Method, NeoGammaNullMethod> // parallel I2S
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
#define B_32_I1_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp32I2s1Tm1829Method, NeoGammaNullMethod> #define B_32_I1_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp32I2s1Tm1829Method, NeoGammaNullMethod>
//#define B_32_I1_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp32I2s1X8Tm1829Method, NeoGammaNullMethod> // parallel I2S
#endif #endif
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
//UCS8903 //UCS8903
#define B_32_RN_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod> #define B_32_RN_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod>
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
#define B_32_I0_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp32I2s0800KbpsMethod, NeoGammaNullMethod> #define B_32_I0_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp32I2s0800KbpsMethod, NeoGammaNullMethod>
//#define B_32_I0_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp32I2s0X8800KbpsMethod, NeoGammaNullMethod> // parallel I2S
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
#define B_32_I1_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp32I2s1800KbpsMethod, NeoGammaNullMethod> #define B_32_I1_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp32I2s1800KbpsMethod, NeoGammaNullMethod>
//#define B_32_I1_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp32I2s1X8800KbpsMethod, NeoGammaNullMethod> // parallel I2S
#endif #endif
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
//UCS8904 //UCS8904
#define B_32_RN_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod> #define B_32_RN_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod>
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
#define B_32_I0_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32I2s0800KbpsMethod, NeoGammaNullMethod> #define B_32_I0_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32I2s0800KbpsMethod, NeoGammaNullMethod>
//#define B_32_I0_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32I2s0X8800KbpsMethod, NeoGammaNullMethod>// parallel I2S
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
#define B_32_I1_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32I2s1800KbpsMethod, NeoGammaNullMethod> #define B_32_I1_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32I2s1800KbpsMethod, NeoGammaNullMethod>
//#define B_32_I1_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32I2s1X8800KbpsMethod, NeoGammaNullMethod>// parallel I2S
#endif #endif
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
#define B_32_RN_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32RmtNApa106Method, NeoGammaNullMethod> #define B_32_RN_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32RmtNApa106Method, NeoGammaNullMethod>
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
#define B_32_I0_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s0Apa106Method, NeoGammaNullMethod> #define B_32_I0_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s0Apa106Method, NeoGammaNullMethod>
//#define B_32_I0_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s0X8Apa106Method, NeoGammaNullMethod> // parallel I2S
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
#define B_32_I1_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1Apa106Method, NeoGammaNullMethod> #define B_32_I1_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1Apa106Method, NeoGammaNullMethod>
//#define B_32_I1_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1X8Apa106Method, NeoGammaNullMethod> // parallel I2S
#endif
//FW1906 GRBCW
#define B_32_RN_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod>
#ifndef WLED_NO_I2S0_PIXELBUS
#define B_32_I0_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp32I2s0800KbpsMethod, NeoGammaNullMethod>
//#define B_32_I0_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp32I2s0X8800KbpsMethod, NeoGammaNullMethod> // parallel I2S
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
#define B_32_I1_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp32I2s1800KbpsMethod, NeoGammaNullMethod>
//#define B_32_I1_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp32I2s1X8800KbpsMethod, NeoGammaNullMethod> // parallel I2S
#endif
//WS2805 RGBWC
#define B_32_RN_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp32RmtNWs2805Method, NeoGammaNullMethod>
#ifndef WLED_NO_I2S0_PIXELBUS
#define B_32_I0_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp32I2s0Ws2805Method, NeoGammaNullMethod>
//#define B_32_I0_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp32I2s0X8Ws2805Method, NeoGammaNullMethod> // parallel I2S
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
#define B_32_I1_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp32I2s1Ws2805Method, NeoGammaNullMethod>
//#define B_32_I1_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp32I2s1X8Ws2805Method, NeoGammaNullMethod> // parallel I2S
#endif #endif
//#define B_32_BB_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266BitBangApa106Method, NeoGammaNullMethod> // NeoEsp8266BitBang800KbpsMethod
#endif #endif
//APA102 //APA102
@ -290,6 +340,7 @@
//handles pointer type conversion for all possible bus types //handles pointer type conversion for all possible bus types
class PolyBus { class PolyBus {
public: public:
// initialize SPI bus speed for DotStar methods // initialize SPI bus speed for DotStar methods
template <class T> template <class T>
static void beginDotStar(void* busPtr, int8_t sck, int8_t miso, int8_t mosi, int8_t ss, uint16_t clock_kHz = 0U) { static void beginDotStar(void* busPtr, int8_t sck, int8_t miso, int8_t mosi, int8_t ss, uint16_t clock_kHz = 0U) {
@ -353,6 +404,14 @@ class PolyBus {
case I_8266_U1_APA106_3: (static_cast<B_8266_U1_APA106_3*>(busPtr))->Begin(); break; case I_8266_U1_APA106_3: (static_cast<B_8266_U1_APA106_3*>(busPtr))->Begin(); break;
case I_8266_DM_APA106_3: (static_cast<B_8266_DM_APA106_3*>(busPtr))->Begin(); break; case I_8266_DM_APA106_3: (static_cast<B_8266_DM_APA106_3*>(busPtr))->Begin(); break;
case I_8266_BB_APA106_3: (static_cast<B_8266_BB_APA106_3*>(busPtr))->Begin(); break; case I_8266_BB_APA106_3: (static_cast<B_8266_BB_APA106_3*>(busPtr))->Begin(); break;
case I_8266_U0_FW6_5: (static_cast<B_8266_U0_FW6_5*>(busPtr))->Begin(); break;
case I_8266_U1_FW6_5: (static_cast<B_8266_U1_FW6_5*>(busPtr))->Begin(); break;
case I_8266_DM_FW6_5: (static_cast<B_8266_DM_FW6_5*>(busPtr))->Begin(); break;
case I_8266_BB_FW6_5: (static_cast<B_8266_BB_FW6_5*>(busPtr))->Begin(); break;
case I_8266_U0_2805_5: (static_cast<B_8266_U0_2805_5*>(busPtr))->Begin(); break;
case I_8266_U1_2805_5: (static_cast<B_8266_U1_2805_5*>(busPtr))->Begin(); break;
case I_8266_DM_2805_5: (static_cast<B_8266_DM_2805_5*>(busPtr))->Begin(); break;
case I_8266_BB_2805_5: (static_cast<B_8266_BB_2805_5*>(busPtr))->Begin(); break;
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Begin(); break; case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Begin(); break;
@ -362,7 +421,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->Begin(); break; case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->Begin(); break;
#endif #endif
// case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->Begin(); break;
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->Begin(); break; case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->Begin(); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->Begin(); break; case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->Begin(); break;
@ -370,7 +428,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->Begin(); break; case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->Begin(); break;
#endif #endif
// case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->Begin(); break;
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->Begin(); break; case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->Begin(); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->Begin(); break; case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->Begin(); break;
@ -378,7 +435,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->Begin(); break; case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->Begin(); break;
#endif #endif
// case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->Begin(); break;
case I_32_RN_TM1_4: beginTM1814<B_32_RN_TM1_4*>(busPtr); break; case I_32_RN_TM1_4: beginTM1814<B_32_RN_TM1_4*>(busPtr); break;
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->Begin(); break; case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->Begin(); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
@ -396,7 +452,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->Begin(); break; case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->Begin(); break;
#endif #endif
// case I_32_BB_UCS_3: (static_cast<B_32_BB_UCS_3*>(busPtr))->Begin(); break;
case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->Begin(); break; case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->Begin(); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->Begin(); break; case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->Begin(); break;
@ -404,7 +459,13 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->Begin(); break; case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->Begin(); break;
#endif #endif
// case I_32_BB_UCS_4: (static_cast<B_32_BB_UCS_4*>(busPtr))->Begin(); break; case I_32_RN_FW6_5: (static_cast<B_32_RN_FW6_5*>(busPtr))->Begin(); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_FW6_5: (static_cast<B_32_I0_FW6_5*>(busPtr))->Begin(); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_FW6_5: (static_cast<B_32_I1_FW6_5*>(busPtr))->Begin(); break;
#endif
case I_32_RN_APA106_3: (static_cast<B_32_RN_APA106_3*>(busPtr))->Begin(); break; case I_32_RN_APA106_3: (static_cast<B_32_RN_APA106_3*>(busPtr))->Begin(); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_APA106_3: (static_cast<B_32_I0_APA106_3*>(busPtr))->Begin(); break; case I_32_I0_APA106_3: (static_cast<B_32_I0_APA106_3*>(busPtr))->Begin(); break;
@ -412,7 +473,13 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_APA106_3: (static_cast<B_32_I1_APA106_3*>(busPtr))->Begin(); break; case I_32_I1_APA106_3: (static_cast<B_32_I1_APA106_3*>(busPtr))->Begin(); break;
#endif #endif
// case I_32_BB_APA106_3: (static_cast<B_32_BB_APA106_3*>(busPtr))->Begin(); break; case I_32_RN_2805_5: (static_cast<B_32_RN_2805_5*>(busPtr))->Begin(); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_2805_5: (static_cast<B_32_I0_2805_5*>(busPtr))->Begin(); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_2805_5: (static_cast<B_32_I1_2805_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() // 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; case I_HS_DOT_3: beginDotStar<B_HS_DOT_3*>(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break;
case I_HS_LPD_3: beginDotStar<B_HS_LPD_3*>(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break; case I_HS_LPD_3: beginDotStar<B_HS_LPD_3*>(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break;
@ -465,6 +532,14 @@ class PolyBus {
case I_8266_U1_APA106_3: busPtr = new B_8266_U1_APA106_3(len, pins[0]); break; case I_8266_U1_APA106_3: busPtr = new B_8266_U1_APA106_3(len, pins[0]); break;
case I_8266_DM_APA106_3: busPtr = new B_8266_DM_APA106_3(len, pins[0]); break; case I_8266_DM_APA106_3: busPtr = new B_8266_DM_APA106_3(len, pins[0]); break;
case I_8266_BB_APA106_3: busPtr = new B_8266_BB_APA106_3(len, pins[0]); break; case I_8266_BB_APA106_3: busPtr = new B_8266_BB_APA106_3(len, pins[0]); break;
case I_8266_U0_FW6_5: busPtr = new B_8266_U0_FW6_5(len, pins[0]); break;
case I_8266_U1_FW6_5: busPtr = new B_8266_U1_FW6_5(len, pins[0]); break;
case I_8266_DM_FW6_5: busPtr = new B_8266_DM_FW6_5(len, pins[0]); break;
case I_8266_BB_FW6_5: busPtr = new B_8266_BB_FW6_5(len, pins[0]); break;
case I_8266_U0_2805_5: busPtr = new B_8266_U0_2805_5(len, pins[0]); break;
case I_8266_U1_2805_5: busPtr = new B_8266_U1_2805_5(len, pins[0]); break;
case I_8266_DM_2805_5: busPtr = new B_8266_DM_2805_5(len, pins[0]); break;
case I_8266_BB_2805_5: busPtr = new B_8266_BB_2805_5(len, pins[0]); break;
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); break; case I_32_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); break;
@ -474,7 +549,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_3: busPtr = new B_32_I1_NEO_3(len, pins[0]); break; case I_32_I1_NEO_3: busPtr = new B_32_I1_NEO_3(len, pins[0]); break;
#endif #endif
// case I_32_BB_NEO_3: busPtr = new B_32_BB_NEO_3(len, pins[0], (NeoBusChannel)channel); break;
case I_32_RN_NEO_4: busPtr = new B_32_RN_NEO_4(len, pins[0], (NeoBusChannel)channel); break; case I_32_RN_NEO_4: busPtr = new B_32_RN_NEO_4(len, pins[0], (NeoBusChannel)channel); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_4: busPtr = new B_32_I0_NEO_4(len, pins[0]); break; case I_32_I0_NEO_4: busPtr = new B_32_I0_NEO_4(len, pins[0]); break;
@ -482,7 +556,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_4: busPtr = new B_32_I1_NEO_4(len, pins[0]); break; case I_32_I1_NEO_4: busPtr = new B_32_I1_NEO_4(len, pins[0]); break;
#endif #endif
// case I_32_BB_NEO_4: busPtr = new B_32_BB_NEO_4(len, pins[0], (NeoBusChannel)channel); break;
case I_32_RN_400_3: busPtr = new B_32_RN_400_3(len, pins[0], (NeoBusChannel)channel); break; case I_32_RN_400_3: busPtr = new B_32_RN_400_3(len, pins[0], (NeoBusChannel)channel); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_400_3: busPtr = new B_32_I0_400_3(len, pins[0]); break; case I_32_I0_400_3: busPtr = new B_32_I0_400_3(len, pins[0]); break;
@ -490,7 +563,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_400_3: busPtr = new B_32_I1_400_3(len, pins[0]); break; case I_32_I1_400_3: busPtr = new B_32_I1_400_3(len, pins[0]); break;
#endif #endif
// case I_32_BB_400_3: busPtr = new B_32_BB_400_3(len, pins[0], (NeoBusChannel)channel); break;
case I_32_RN_TM1_4: busPtr = new B_32_RN_TM1_4(len, pins[0], (NeoBusChannel)channel); break; case I_32_RN_TM1_4: busPtr = new B_32_RN_TM1_4(len, pins[0], (NeoBusChannel)channel); break;
case I_32_RN_TM2_3: busPtr = new B_32_RN_TM2_3(len, pins[0], (NeoBusChannel)channel); break; case I_32_RN_TM2_3: busPtr = new B_32_RN_TM2_3(len, pins[0], (NeoBusChannel)channel); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
@ -508,7 +580,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_3: busPtr = new B_32_I1_UCS_3(len, pins[0]); break; case I_32_I1_UCS_3: busPtr = new B_32_I1_UCS_3(len, pins[0]); break;
#endif #endif
// case I_32_BB_UCS_3: busPtr = new B_32_BB_UCS_3(len, pins[0], (NeoBusChannel)channel); break;
case I_32_RN_UCS_4: busPtr = new B_32_RN_UCS_4(len, pins[0], (NeoBusChannel)channel); break; case I_32_RN_UCS_4: busPtr = new B_32_RN_UCS_4(len, pins[0], (NeoBusChannel)channel); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_4: busPtr = new B_32_I0_UCS_4(len, pins[0]); break; case I_32_I0_UCS_4: busPtr = new B_32_I0_UCS_4(len, pins[0]); break;
@ -516,7 +587,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_4: busPtr = new B_32_I1_UCS_4(len, pins[0]); break; case I_32_I1_UCS_4: busPtr = new B_32_I1_UCS_4(len, pins[0]); break;
#endif #endif
// case I_32_BB_UCS_4: busPtr = new B_32_BB_UCS_4(len, pins[0], (NeoBusChannel)channel); break;
case I_32_RN_APA106_3: busPtr = new B_32_RN_APA106_3(len, pins[0], (NeoBusChannel)channel); break; case I_32_RN_APA106_3: busPtr = new B_32_RN_APA106_3(len, pins[0], (NeoBusChannel)channel); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_APA106_3: busPtr = new B_32_I0_APA106_3(len, pins[0]); break; case I_32_I0_APA106_3: busPtr = new B_32_I0_APA106_3(len, pins[0]); break;
@ -524,7 +594,20 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_APA106_3: busPtr = new B_32_I1_APA106_3(len, pins[0]); break; case I_32_I1_APA106_3: busPtr = new B_32_I1_APA106_3(len, pins[0]); break;
#endif #endif
// case I_32_BB_APA106_3: busPtr = new B_32_BB_APA106_3(len, pins[0], (NeoBusChannel)channel); break; case I_32_RN_FW6_5: busPtr = new B_32_RN_FW6_5(len, pins[0], (NeoBusChannel)channel); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_FW6_5: busPtr = new B_32_I0_FW6_5(len, pins[0]); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_FW6_5: busPtr = new B_32_I1_FW6_5(len, pins[0]); break;
#endif
case I_32_RN_2805_5: busPtr = new B_32_RN_2805_5(len, pins[0], (NeoBusChannel)channel); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_2805_5: busPtr = new B_32_I0_2805_5(len, pins[0]); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_2805_5: busPtr = new B_32_I1_2805_5(len, pins[0]); break;
#endif
#endif #endif
// for 2-wire: pins[1] is clk, pins[0] is dat. begin expects (len, clk, dat) // for 2-wire: pins[1] is clk, pins[0] is dat. begin expects (len, clk, dat)
case I_HS_DOT_3: busPtr = new B_HS_DOT_3(len, pins[1], pins[0]); break; case I_HS_DOT_3: busPtr = new B_HS_DOT_3(len, pins[1], pins[0]); break;
@ -578,6 +661,14 @@ class PolyBus {
case I_8266_U1_APA106_3: (static_cast<B_8266_U1_APA106_3*>(busPtr))->Show(consistent); break; case I_8266_U1_APA106_3: (static_cast<B_8266_U1_APA106_3*>(busPtr))->Show(consistent); break;
case I_8266_DM_APA106_3: (static_cast<B_8266_DM_APA106_3*>(busPtr))->Show(consistent); break; case I_8266_DM_APA106_3: (static_cast<B_8266_DM_APA106_3*>(busPtr))->Show(consistent); break;
case I_8266_BB_APA106_3: (static_cast<B_8266_BB_APA106_3*>(busPtr))->Show(consistent); break; case I_8266_BB_APA106_3: (static_cast<B_8266_BB_APA106_3*>(busPtr))->Show(consistent); break;
case I_8266_U0_FW6_5: (static_cast<B_8266_U0_FW6_5*>(busPtr))->Show(consistent); break;
case I_8266_U1_FW6_5: (static_cast<B_8266_U1_FW6_5*>(busPtr))->Show(consistent); break;
case I_8266_DM_FW6_5: (static_cast<B_8266_DM_FW6_5*>(busPtr))->Show(consistent); break;
case I_8266_BB_FW6_5: (static_cast<B_8266_BB_FW6_5*>(busPtr))->Show(consistent); break;
case I_8266_U0_2805_5: (static_cast<B_8266_U0_2805_5*>(busPtr))->Show(consistent); break;
case I_8266_U1_2805_5: (static_cast<B_8266_U1_2805_5*>(busPtr))->Show(consistent); break;
case I_8266_DM_2805_5: (static_cast<B_8266_DM_2805_5*>(busPtr))->Show(consistent); break;
case I_8266_BB_2805_5: (static_cast<B_8266_BB_2805_5*>(busPtr))->Show(consistent); break;
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Show(consistent); break; case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Show(consistent); break;
@ -587,7 +678,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->Show(consistent); break; case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->Show(consistent); break;
#endif #endif
// case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->Show(consistent); break;
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->Show(consistent); break; case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->Show(consistent); break; case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->Show(consistent); break;
@ -595,7 +685,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->Show(consistent); break; case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->Show(consistent); break;
#endif #endif
// case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->Show(consistent); break;
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->Show(consistent); break; case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->Show(consistent); break; case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->Show(consistent); break;
@ -603,7 +692,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->Show(consistent); break; case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->Show(consistent); break;
#endif #endif
// case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->Show(consistent); break;
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->Show(consistent); break; case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->Show(consistent); break;
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->Show(consistent); break; case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
@ -621,7 +709,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->Show(consistent); break; case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->Show(consistent); break;
#endif #endif
// case I_32_BB_UCS_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->Show(consistent); break;
case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->Show(consistent); break; case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->Show(consistent); break; case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->Show(consistent); break;
@ -629,7 +716,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->Show(consistent); break; case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->Show(consistent); break;
#endif #endif
// case I_32_BB_UCS_4: (static_cast<B_32_BB_UCS_4*>(busPtr))->Show(consistent); break;
case I_32_RN_APA106_3: (static_cast<B_32_RN_APA106_3*>(busPtr))->Show(consistent); break; case I_32_RN_APA106_3: (static_cast<B_32_RN_APA106_3*>(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_APA106_3: (static_cast<B_32_I0_APA106_3*>(busPtr))->Show(consistent); break; case I_32_I0_APA106_3: (static_cast<B_32_I0_APA106_3*>(busPtr))->Show(consistent); break;
@ -637,7 +723,20 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_APA106_3: (static_cast<B_32_I1_APA106_3*>(busPtr))->Show(consistent); break; case I_32_I1_APA106_3: (static_cast<B_32_I1_APA106_3*>(busPtr))->Show(consistent); break;
#endif #endif
// case I_32_BB_APA106_3: (static_cast<B_32_BB_APA106_3*>(busPtr))->Show(consistent); break; case I_32_RN_FW6_5: (static_cast<B_32_RN_FW6_5*>(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_FW6_5: (static_cast<B_32_I0_FW6_5*>(busPtr))->Show(consistent); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_FW6_5: (static_cast<B_32_I1_FW6_5*>(busPtr))->Show(consistent); break;
#endif
case I_32_RN_2805_5: (static_cast<B_32_RN_2805_5*>(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_2805_5: (static_cast<B_32_I0_2805_5*>(busPtr))->Show(consistent); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_2805_5: (static_cast<B_32_I1_2805_5*>(busPtr))->Show(consistent); break;
#endif
#endif #endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->Show(consistent); break; case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->Show(consistent); break;
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->Show(consistent); break; case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->Show(consistent); break;
@ -687,6 +786,14 @@ class PolyBus {
case I_8266_U1_APA106_3: return (static_cast<B_8266_U1_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; case I_8266_DM_APA106_3: return (static_cast<B_8266_DM_APA106_3*>(busPtr))->CanShow(); break;
case I_8266_BB_APA106_3: return (static_cast<B_8266_BB_APA106_3*>(busPtr))->CanShow(); break; case I_8266_BB_APA106_3: return (static_cast<B_8266_BB_APA106_3*>(busPtr))->CanShow(); break;
case I_8266_U0_FW6_5: return (static_cast<B_8266_U0_FW6_5*>(busPtr))->CanShow(); break;
case I_8266_U1_FW6_5: return (static_cast<B_8266_U1_FW6_5*>(busPtr))->CanShow(); break;
case I_8266_DM_FW6_5: return (static_cast<B_8266_DM_FW6_5*>(busPtr))->CanShow(); break;
case I_8266_BB_FW6_5: return (static_cast<B_8266_BB_FW6_5*>(busPtr))->CanShow(); break;
case I_8266_U0_2805_5: return (static_cast<B_8266_U0_2805_5*>(busPtr))->CanShow(); break;
case I_8266_U1_2805_5: return (static_cast<B_8266_U1_2805_5*>(busPtr))->CanShow(); break;
case I_8266_DM_2805_5: return (static_cast<B_8266_DM_2805_5*>(busPtr))->CanShow(); break;
case I_8266_BB_2805_5: return (static_cast<B_8266_BB_2805_5*>(busPtr))->CanShow(); break;
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: return (static_cast<B_32_RN_NEO_3*>(busPtr))->CanShow(); break; case I_32_RN_NEO_3: return (static_cast<B_32_RN_NEO_3*>(busPtr))->CanShow(); break;
@ -696,7 +803,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_3: return (static_cast<B_32_I1_NEO_3*>(busPtr))->CanShow(); break; case I_32_I1_NEO_3: return (static_cast<B_32_I1_NEO_3*>(busPtr))->CanShow(); break;
#endif #endif
// case I_32_BB_NEO_3: return (static_cast<B_32_BB_NEO_3*>(busPtr))->CanShow(); break;
case I_32_RN_NEO_4: return (static_cast<B_32_RN_NEO_4*>(busPtr))->CanShow(); break; case I_32_RN_NEO_4: return (static_cast<B_32_RN_NEO_4*>(busPtr))->CanShow(); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_4: return (static_cast<B_32_I0_NEO_4*>(busPtr))->CanShow(); break; case I_32_I0_NEO_4: return (static_cast<B_32_I0_NEO_4*>(busPtr))->CanShow(); break;
@ -704,7 +810,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_4: return (static_cast<B_32_I1_NEO_4*>(busPtr))->CanShow(); break; case I_32_I1_NEO_4: return (static_cast<B_32_I1_NEO_4*>(busPtr))->CanShow(); break;
#endif #endif
// case I_32_BB_NEO_4: return (static_cast<B_32_BB_NEO_4*>(busPtr))->CanShow(); break;
case I_32_RN_400_3: return (static_cast<B_32_RN_400_3*>(busPtr))->CanShow(); break; case I_32_RN_400_3: return (static_cast<B_32_RN_400_3*>(busPtr))->CanShow(); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_400_3: return (static_cast<B_32_I0_400_3*>(busPtr))->CanShow(); break; case I_32_I0_400_3: return (static_cast<B_32_I0_400_3*>(busPtr))->CanShow(); break;
@ -712,7 +817,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_400_3: return (static_cast<B_32_I1_400_3*>(busPtr))->CanShow(); break; case I_32_I1_400_3: return (static_cast<B_32_I1_400_3*>(busPtr))->CanShow(); break;
#endif #endif
// case I_32_BB_400_3: return (static_cast<B_32_BB_400_3*>(busPtr))->CanShow(); break;
case I_32_RN_TM1_4: return (static_cast<B_32_RN_TM1_4*>(busPtr))->CanShow(); break; case I_32_RN_TM1_4: return (static_cast<B_32_RN_TM1_4*>(busPtr))->CanShow(); break;
case I_32_RN_TM2_3: return (static_cast<B_32_RN_TM2_3*>(busPtr))->CanShow(); break; case I_32_RN_TM2_3: return (static_cast<B_32_RN_TM2_3*>(busPtr))->CanShow(); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
@ -730,7 +834,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_3: return (static_cast<B_32_I1_UCS_3*>(busPtr))->CanShow(); break; case I_32_I1_UCS_3: return (static_cast<B_32_I1_UCS_3*>(busPtr))->CanShow(); break;
#endif #endif
// case I_32_BB_UCS_3: return (static_cast<B_32_BB_UCS_3*>(busPtr))->CanShow(); break;
case I_32_RN_UCS_4: return (static_cast<B_32_RN_UCS_4*>(busPtr))->CanShow(); break; case I_32_RN_UCS_4: return (static_cast<B_32_RN_UCS_4*>(busPtr))->CanShow(); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_4: return (static_cast<B_32_I0_UCS_4*>(busPtr))->CanShow(); break; case I_32_I0_UCS_4: return (static_cast<B_32_I0_UCS_4*>(busPtr))->CanShow(); break;
@ -738,7 +841,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_4: return (static_cast<B_32_I1_UCS_4*>(busPtr))->CanShow(); break; case I_32_I1_UCS_4: return (static_cast<B_32_I1_UCS_4*>(busPtr))->CanShow(); break;
#endif #endif
// case I_32_BB_UCS_4: return (static_cast<B_32_BB_UCS_4*>(busPtr))->CanShow(); break;
case I_32_RN_APA106_3: return (static_cast<B_32_RN_APA106_3*>(busPtr))->CanShow(); break; case I_32_RN_APA106_3: return (static_cast<B_32_RN_APA106_3*>(busPtr))->CanShow(); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_APA106_3: return (static_cast<B_32_I0_APA106_3*>(busPtr))->CanShow(); break; case I_32_I0_APA106_3: return (static_cast<B_32_I0_APA106_3*>(busPtr))->CanShow(); break;
@ -746,7 +848,20 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_APA106_3: return (static_cast<B_32_I1_APA106_3*>(busPtr))->CanShow(); break; case I_32_I1_APA106_3: return (static_cast<B_32_I1_APA106_3*>(busPtr))->CanShow(); break;
#endif #endif
// case I_32_BB_APA106_3: return (static_cast<B_32_BB_APA106_3*>(busPtr))->CanShow(); break; case I_32_RN_FW6_5: return (static_cast<B_32_RN_FW6_5*>(busPtr))->CanShow(); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_FW6_5: return (static_cast<B_32_I0_FW6_5*>(busPtr))->CanShow(); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_FW6_5: return (static_cast<B_32_I1_FW6_5*>(busPtr))->CanShow(); break;
#endif
case I_32_RN_2805_5: return (static_cast<B_32_RN_2805_5*>(busPtr))->CanShow(); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_2805_5: return (static_cast<B_32_I0_2805_5*>(busPtr))->CanShow(); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_2805_5: return (static_cast<B_32_I1_2805_5*>(busPtr))->CanShow(); break;
#endif
#endif #endif
case I_HS_DOT_3: return (static_cast<B_HS_DOT_3*>(busPtr))->CanShow(); break; case I_HS_DOT_3: return (static_cast<B_HS_DOT_3*>(busPtr))->CanShow(); break;
case I_SS_DOT_3: return (static_cast<B_SS_DOT_3*>(busPtr))->CanShow(); break; case I_SS_DOT_3: return (static_cast<B_SS_DOT_3*>(busPtr))->CanShow(); break;
@ -762,12 +877,13 @@ class PolyBus {
return true; return true;
} }
static void setPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint32_t c, uint8_t co) { 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 r = c >> 16;
uint8_t g = c >> 8; uint8_t g = c >> 8;
uint8_t b = c >> 0; uint8_t b = c >> 0;
uint8_t w = c >> 24; uint8_t w = c >> 24;
RgbwColor col; RgbwColor col;
uint8_t cctWW = wwcw & 0xFF, cctCW = (wwcw>>8) & 0xFF;
// reorder channels to selected order // reorder channels to selected order
switch (co & 0x0F) { switch (co & 0x0F) {
@ -821,6 +937,14 @@ class PolyBus {
case I_8266_U1_APA106_3: (static_cast<B_8266_U1_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_8266_U1_APA106_3: (static_cast<B_8266_U1_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_DM_APA106_3: (static_cast<B_8266_DM_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_8266_DM_APA106_3: (static_cast<B_8266_DM_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_BB_APA106_3: (static_cast<B_8266_BB_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_8266_BB_APA106_3: (static_cast<B_8266_BB_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_U0_FW6_5: (static_cast<B_8266_U0_FW6_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
case I_8266_U1_FW6_5: (static_cast<B_8266_U1_FW6_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
case I_8266_DM_FW6_5: (static_cast<B_8266_DM_FW6_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
case I_8266_BB_FW6_5: (static_cast<B_8266_BB_FW6_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
case I_8266_U0_2805_5: (static_cast<B_8266_U0_2805_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
case I_8266_U1_2805_5: (static_cast<B_8266_U1_2805_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
case I_8266_DM_2805_5: (static_cast<B_8266_DM_2805_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
case I_8266_BB_2805_5: (static_cast<B_8266_BB_2805_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
@ -830,7 +954,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
#endif #endif
// case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetPixelColor(pix, col); break; case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetPixelColor(pix, col); break; case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
@ -838,7 +961,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetPixelColor(pix, col); break; case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
#endif #endif
// case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
@ -846,7 +968,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
#endif #endif
// case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(colB)); break;
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetPixelColor(pix, col); break; case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
@ -864,7 +985,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break; case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break;
#endif #endif
// case I_32_BB_UCS_3: (static_cast<B_32_BB_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break;
case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break; case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break; case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break;
@ -872,7 +992,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break; case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break;
#endif #endif
// case I_32_BB_UCS_4: (static_cast<B_32_BB_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break;
case I_32_RN_APA106_3: (static_cast<B_32_RN_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_32_RN_APA106_3: (static_cast<B_32_RN_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_APA106_3: (static_cast<B_32_I0_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_32_I0_APA106_3: (static_cast<B_32_I0_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
@ -880,7 +999,20 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_APA106_3: (static_cast<B_32_I1_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_32_I1_APA106_3: (static_cast<B_32_I1_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
#endif #endif
// case I_32_BB_APA106_3: (static_cast<B_32_BB_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_32_RN_FW6_5: (static_cast<B_32_RN_FW6_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
#ifndef WLED_NO_I2S0_PIXELBUS
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;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_FW6_5: (static_cast<B_32_I1_FW6_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
#endif
case I_32_RN_2805_5: (static_cast<B_32_RN_2805_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
#ifndef WLED_NO_I2S0_PIXELBUS
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;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_2805_5: (static_cast<B_32_I1_2805_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
#endif
#endif #endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
@ -931,6 +1063,14 @@ class PolyBus {
case I_8266_U1_APA106_3: (static_cast<B_8266_U1_APA106_3*>(busPtr))->SetLuminance(b); break; case I_8266_U1_APA106_3: (static_cast<B_8266_U1_APA106_3*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_APA106_3: (static_cast<B_8266_DM_APA106_3*>(busPtr))->SetLuminance(b); break; case I_8266_DM_APA106_3: (static_cast<B_8266_DM_APA106_3*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_APA106_3: (static_cast<B_8266_BB_APA106_3*>(busPtr))->SetLuminance(b); break; case I_8266_BB_APA106_3: (static_cast<B_8266_BB_APA106_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_FW6_5: (static_cast<B_8266_U0_FW6_5*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_FW6_5: (static_cast<B_8266_U1_FW6_5*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_FW6_5: (static_cast<B_8266_DM_FW6_5*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_FW6_5: (static_cast<B_8266_BB_FW6_5*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_2805_5: (static_cast<B_8266_U0_2805_5*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_2805_5: (static_cast<B_8266_U1_2805_5*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_2805_5: (static_cast<B_8266_DM_2805_5*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_2805_5: (static_cast<B_8266_BB_2805_5*>(busPtr))->SetLuminance(b); break;
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetLuminance(b); break; case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetLuminance(b); break;
@ -940,7 +1080,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->SetLuminance(b); break; case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->SetLuminance(b); break;
#endif #endif
// case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->SetLuminance(b); break;
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetLuminance(b); break; case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetLuminance(b); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetLuminance(b); break; case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetLuminance(b); break;
@ -948,7 +1087,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetLuminance(b); break; case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetLuminance(b); break;
#endif #endif
// case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->SetLuminance(b); break;
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetLuminance(b); break; case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetLuminance(b); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetLuminance(b); break; case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetLuminance(b); break;
@ -956,7 +1094,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetLuminance(b); break; case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetLuminance(b); break;
#endif #endif
// case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->SetLuminance(b); break;
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetLuminance(b); break; case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->SetLuminance(b); break; case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->SetLuminance(b); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
@ -974,7 +1111,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->SetLuminance(b); break; case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->SetLuminance(b); break;
#endif #endif
// case I_32_BB_UCS_3: (static_cast<B_32_BB_UCS_3*>(busPtr))->SetLuminance(b); break;
case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->SetLuminance(b); break; case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->SetLuminance(b); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->SetLuminance(b); break; case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->SetLuminance(b); break;
@ -982,7 +1118,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->SetLuminance(b); break; case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->SetLuminance(b); break;
#endif #endif
// case I_32_BB_UCS_4: (static_cast<B_32_BB_UCS_4*>(busPtr))->SetLuminance(b); break;
case I_32_RN_APA106_3: (static_cast<B_32_RN_APA106_3*>(busPtr))->SetLuminance(b); break; case I_32_RN_APA106_3: (static_cast<B_32_RN_APA106_3*>(busPtr))->SetLuminance(b); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_APA106_3: (static_cast<B_32_I0_APA106_3*>(busPtr))->SetLuminance(b); break; case I_32_I0_APA106_3: (static_cast<B_32_I0_APA106_3*>(busPtr))->SetLuminance(b); break;
@ -990,7 +1125,20 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_APA106_3: (static_cast<B_32_I1_APA106_3*>(busPtr))->SetLuminance(b); break; case I_32_I1_APA106_3: (static_cast<B_32_I1_APA106_3*>(busPtr))->SetLuminance(b); break;
#endif #endif
// case I_32_BB_APA106_3: (static_cast<B_32_BB_APA106_3*>(busPtr))->SetLuminance(b); break; case I_32_RN_FW6_5: (static_cast<B_32_RN_FW6_5*>(busPtr))->SetLuminance(b); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_FW6_5: (static_cast<B_32_I0_FW6_5*>(busPtr))->SetLuminance(b); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_FW6_5: (static_cast<B_32_I1_FW6_5*>(busPtr))->SetLuminance(b); break;
#endif
case I_32_RN_2805_5: (static_cast<B_32_RN_2805_5*>(busPtr))->SetLuminance(b); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_2805_5: (static_cast<B_32_I0_2805_5*>(busPtr))->SetLuminance(b); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_2805_5: (static_cast<B_32_I1_2805_5*>(busPtr))->SetLuminance(b); break;
#endif
#endif #endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetLuminance(b); break; case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetLuminance(b); break;
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetLuminance(b); break; case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetLuminance(b); break;
@ -1042,6 +1190,14 @@ class PolyBus {
case I_8266_U1_APA106_3: col = (static_cast<B_8266_U1_APA106_3*>(busPtr))->GetPixelColor(pix); break; case I_8266_U1_APA106_3: col = (static_cast<B_8266_U1_APA106_3*>(busPtr))->GetPixelColor(pix); break;
case I_8266_DM_APA106_3: col = (static_cast<B_8266_DM_APA106_3*>(busPtr))->GetPixelColor(pix); break; case I_8266_DM_APA106_3: col = (static_cast<B_8266_DM_APA106_3*>(busPtr))->GetPixelColor(pix); break;
case I_8266_BB_APA106_3: col = (static_cast<B_8266_BB_APA106_3*>(busPtr))->GetPixelColor(pix); break; case I_8266_BB_APA106_3: col = (static_cast<B_8266_BB_APA106_3*>(busPtr))->GetPixelColor(pix); break;
case I_8266_U0_FW6_5: { RgbwwColor c = (static_cast<B_8266_U0_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_8266_U1_FW6_5: { RgbwwColor c = (static_cast<B_8266_U1_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_8266_DM_FW6_5: { RgbwwColor c = (static_cast<B_8266_DM_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_8266_BB_FW6_5: { RgbwwColor c = (static_cast<B_8266_BB_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_8266_U0_2805_5: { RgbwwColor c = (static_cast<B_8266_U0_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_8266_U1_2805_5: { RgbwwColor c = (static_cast<B_8266_U1_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_8266_DM_2805_5: { RgbwwColor c = (static_cast<B_8266_DM_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_8266_BB_2805_5: { RgbwwColor c = (static_cast<B_8266_BB_2805_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: col = (static_cast<B_32_RN_NEO_3*>(busPtr))->GetPixelColor(pix); break; case I_32_RN_NEO_3: col = (static_cast<B_32_RN_NEO_3*>(busPtr))->GetPixelColor(pix); break;
@ -1051,7 +1207,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_3: col = (static_cast<B_32_I1_NEO_3*>(busPtr))->GetPixelColor(pix); break; case I_32_I1_NEO_3: col = (static_cast<B_32_I1_NEO_3*>(busPtr))->GetPixelColor(pix); break;
#endif #endif
// case I_32_BB_NEO_3: col = (static_cast<B_32_BB_NEO_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_RN_NEO_4: col = (static_cast<B_32_RN_NEO_4*>(busPtr))->GetPixelColor(pix); break; case I_32_RN_NEO_4: col = (static_cast<B_32_RN_NEO_4*>(busPtr))->GetPixelColor(pix); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_4: col = (static_cast<B_32_I0_NEO_4*>(busPtr))->GetPixelColor(pix); break; case I_32_I0_NEO_4: col = (static_cast<B_32_I0_NEO_4*>(busPtr))->GetPixelColor(pix); break;
@ -1059,7 +1214,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_4: col = (static_cast<B_32_I1_NEO_4*>(busPtr))->GetPixelColor(pix); break; case I_32_I1_NEO_4: col = (static_cast<B_32_I1_NEO_4*>(busPtr))->GetPixelColor(pix); break;
#endif #endif
// case I_32_BB_NEO_4: col = (static_cast<B_32_BB_NEO_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_RN_400_3: col = (static_cast<B_32_RN_400_3*>(busPtr))->GetPixelColor(pix); break; case I_32_RN_400_3: col = (static_cast<B_32_RN_400_3*>(busPtr))->GetPixelColor(pix); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_400_3: col = (static_cast<B_32_I0_400_3*>(busPtr))->GetPixelColor(pix); break; case I_32_I0_400_3: col = (static_cast<B_32_I0_400_3*>(busPtr))->GetPixelColor(pix); break;
@ -1067,7 +1221,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_400_3: col = (static_cast<B_32_I1_400_3*>(busPtr))->GetPixelColor(pix); break; case I_32_I1_400_3: col = (static_cast<B_32_I1_400_3*>(busPtr))->GetPixelColor(pix); break;
#endif #endif
// case I_32_BB_400_3: col = (static_cast<B_32_BB_400_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_RN_TM1_4: col = (static_cast<B_32_RN_TM1_4*>(busPtr))->GetPixelColor(pix); break; case I_32_RN_TM1_4: col = (static_cast<B_32_RN_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_RN_TM2_3: col = (static_cast<B_32_RN_TM2_3*>(busPtr))->GetPixelColor(pix); break; case I_32_RN_TM2_3: col = (static_cast<B_32_RN_TM2_3*>(busPtr))->GetPixelColor(pix); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
@ -1085,7 +1238,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_3: { Rgb48Color c = (static_cast<B_32_I1_UCS_3*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,0); } break; case I_32_I1_UCS_3: { Rgb48Color c = (static_cast<B_32_I1_UCS_3*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,0); } break;
#endif #endif
// case I_32_BB_UCS_3: col = (static_cast<B_32_BB_UCS_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_RN_UCS_4: { Rgbw64Color c = (static_cast<B_32_RN_UCS_4*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.W>>8); } break; case I_32_RN_UCS_4: { Rgbw64Color c = (static_cast<B_32_RN_UCS_4*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.W>>8); } break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_4: { Rgbw64Color c = (static_cast<B_32_I0_UCS_4*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.W>>8); } break; case I_32_I0_UCS_4: { Rgbw64Color c = (static_cast<B_32_I0_UCS_4*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.W>>8); } break;
@ -1093,7 +1245,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_4: { Rgbw64Color c = (static_cast<B_32_I1_UCS_4*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.W>>8); } break; case I_32_I1_UCS_4: { Rgbw64Color c = (static_cast<B_32_I1_UCS_4*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.W>>8); } break;
#endif #endif
// case I_32_BB_UCS_4: col = (static_cast<B_32_BB_UCS_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_RN_APA106_3: col = (static_cast<B_32_RN_APA106_3*>(busPtr))->GetPixelColor(pix); break; case I_32_RN_APA106_3: col = (static_cast<B_32_RN_APA106_3*>(busPtr))->GetPixelColor(pix); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_APA106_3: col = (static_cast<B_32_I0_APA106_3*>(busPtr))->GetPixelColor(pix); break; case I_32_I0_APA106_3: col = (static_cast<B_32_I0_APA106_3*>(busPtr))->GetPixelColor(pix); break;
@ -1101,7 +1252,20 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_APA106_3: col = (static_cast<B_32_I1_APA106_3*>(busPtr))->GetPixelColor(pix); break; case I_32_I1_APA106_3: col = (static_cast<B_32_I1_APA106_3*>(busPtr))->GetPixelColor(pix); break;
#endif #endif
// case I_32_BB_APA106_3: col = (static_cast<B_32_BB_APA106_3*>(busPtr))->GetPixelColor(pix); break; case I_32_RN_FW6_5: { RgbwwColor c = (static_cast<B_32_RN_FW6_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
#ifndef WLED_NO_I2S0_PIXELBUS
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
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_FW6_5: { RgbwwColor c = (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
#endif
case I_32_RN_2805_5: { RgbwwColor c = (static_cast<B_32_RN_2805_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
#ifndef WLED_NO_I2S0_PIXELBUS
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
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_2805_5: { RgbwwColor c = (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
#endif
#endif #endif
case I_HS_DOT_3: col = (static_cast<B_HS_DOT_3*>(busPtr))->GetPixelColor(pix); break; case I_HS_DOT_3: col = (static_cast<B_HS_DOT_3*>(busPtr))->GetPixelColor(pix); break;
case I_SS_DOT_3: col = (static_cast<B_SS_DOT_3*>(busPtr))->GetPixelColor(pix); break; case I_SS_DOT_3: col = (static_cast<B_SS_DOT_3*>(busPtr))->GetPixelColor(pix); break;
@ -1171,6 +1335,14 @@ class PolyBus {
case I_8266_U1_APA106_3: delete (static_cast<B_8266_U1_APA106_3*>(busPtr)); break; case I_8266_U1_APA106_3: delete (static_cast<B_8266_U1_APA106_3*>(busPtr)); break;
case I_8266_DM_APA106_3: delete (static_cast<B_8266_DM_APA106_3*>(busPtr)); break; case I_8266_DM_APA106_3: delete (static_cast<B_8266_DM_APA106_3*>(busPtr)); break;
case I_8266_BB_APA106_3: delete (static_cast<B_8266_BB_APA106_3*>(busPtr)); break; case I_8266_BB_APA106_3: delete (static_cast<B_8266_BB_APA106_3*>(busPtr)); break;
case I_8266_U0_FW6_5: delete (static_cast<B_8266_U0_FW6_5*>(busPtr)); break;
case I_8266_U1_FW6_5: delete (static_cast<B_8266_U1_FW6_5*>(busPtr)); break;
case I_8266_DM_FW6_5: delete (static_cast<B_8266_DM_FW6_5*>(busPtr)); break;
case I_8266_BB_FW6_5: delete (static_cast<B_8266_BB_FW6_5*>(busPtr)); break;
case I_8266_U0_2805_5: delete (static_cast<B_8266_U0_2805_5*>(busPtr)); break;
case I_8266_U1_2805_5: delete (static_cast<B_8266_U1_2805_5*>(busPtr)); break;
case I_8266_DM_2805_5: delete (static_cast<B_8266_DM_2805_5*>(busPtr)); break;
case I_8266_BB_2805_5: delete (static_cast<B_8266_BB_2805_5*>(busPtr)); break;
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: delete (static_cast<B_32_RN_NEO_3*>(busPtr)); break; case I_32_RN_NEO_3: delete (static_cast<B_32_RN_NEO_3*>(busPtr)); break;
@ -1180,7 +1352,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_3: delete (static_cast<B_32_I1_NEO_3*>(busPtr)); break; case I_32_I1_NEO_3: delete (static_cast<B_32_I1_NEO_3*>(busPtr)); break;
#endif #endif
// case I_32_BB_NEO_3: delete (static_cast<B_32_BB_NEO_3*>(busPtr)); break;
case I_32_RN_NEO_4: delete (static_cast<B_32_RN_NEO_4*>(busPtr)); break; case I_32_RN_NEO_4: delete (static_cast<B_32_RN_NEO_4*>(busPtr)); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_4: delete (static_cast<B_32_I0_NEO_4*>(busPtr)); break; case I_32_I0_NEO_4: delete (static_cast<B_32_I0_NEO_4*>(busPtr)); break;
@ -1188,7 +1359,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_4: delete (static_cast<B_32_I1_NEO_4*>(busPtr)); break; case I_32_I1_NEO_4: delete (static_cast<B_32_I1_NEO_4*>(busPtr)); break;
#endif #endif
// case I_32_BB_NEO_4: delete (static_cast<B_32_BB_NEO_4*>(busPtr)); break;
case I_32_RN_400_3: delete (static_cast<B_32_RN_400_3*>(busPtr)); break; case I_32_RN_400_3: delete (static_cast<B_32_RN_400_3*>(busPtr)); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_400_3: delete (static_cast<B_32_I0_400_3*>(busPtr)); break; case I_32_I0_400_3: delete (static_cast<B_32_I0_400_3*>(busPtr)); break;
@ -1196,7 +1366,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_400_3: delete (static_cast<B_32_I1_400_3*>(busPtr)); break; case I_32_I1_400_3: delete (static_cast<B_32_I1_400_3*>(busPtr)); break;
#endif #endif
// case I_32_BB_400_3: delete (static_cast<B_32_BB_400_3*>(busPtr)); break;
case I_32_RN_TM1_4: delete (static_cast<B_32_RN_TM1_4*>(busPtr)); break; case I_32_RN_TM1_4: delete (static_cast<B_32_RN_TM1_4*>(busPtr)); break;
case I_32_RN_TM2_3: delete (static_cast<B_32_RN_TM2_3*>(busPtr)); break; case I_32_RN_TM2_3: delete (static_cast<B_32_RN_TM2_3*>(busPtr)); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
@ -1214,7 +1383,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_3: delete (static_cast<B_32_I1_UCS_3*>(busPtr)); break; case I_32_I1_UCS_3: delete (static_cast<B_32_I1_UCS_3*>(busPtr)); break;
#endif #endif
// case I_32_BB_UCS_3: delete (static_cast<B_32_BB_UCS_3*>(busPtr)); break;
case I_32_RN_UCS_4: delete (static_cast<B_32_RN_UCS_4*>(busPtr)); break; case I_32_RN_UCS_4: delete (static_cast<B_32_RN_UCS_4*>(busPtr)); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_4: delete (static_cast<B_32_I0_UCS_4*>(busPtr)); break; case I_32_I0_UCS_4: delete (static_cast<B_32_I0_UCS_4*>(busPtr)); break;
@ -1222,7 +1390,6 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_4: delete (static_cast<B_32_I1_UCS_4*>(busPtr)); break; case I_32_I1_UCS_4: delete (static_cast<B_32_I1_UCS_4*>(busPtr)); break;
#endif #endif
// case I_32_BB_UCS_4: delete (static_cast<B_32_BB_UCS_4*>(busPtr)); break;
case I_32_RN_APA106_3: delete (static_cast<B_32_RN_APA106_3*>(busPtr)); break; case I_32_RN_APA106_3: delete (static_cast<B_32_RN_APA106_3*>(busPtr)); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_APA106_3: delete (static_cast<B_32_I0_APA106_3*>(busPtr)); break; case I_32_I0_APA106_3: delete (static_cast<B_32_I0_APA106_3*>(busPtr)); break;
@ -1230,7 +1397,20 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_APA106_3: delete (static_cast<B_32_I1_APA106_3*>(busPtr)); break; case I_32_I1_APA106_3: delete (static_cast<B_32_I1_APA106_3*>(busPtr)); break;
#endif #endif
// case I_32_BB_APA106_3: delete (static_cast<B_32_BB_APA106_3*>(busPtr)); break; case I_32_RN_FW6_5: delete (static_cast<B_32_RN_FW6_5*>(busPtr)); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_FW6_5: delete (static_cast<B_32_I0_FW6_5*>(busPtr)); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_FW6_5: delete (static_cast<B_32_I1_FW6_5*>(busPtr)); break;
#endif
case I_32_RN_2805_5: delete (static_cast<B_32_RN_2805_5*>(busPtr)); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_2805_5: delete (static_cast<B_32_I0_2805_5*>(busPtr)); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_2805_5: delete (static_cast<B_32_I1_2805_5*>(busPtr)); break;
#endif
#endif #endif
case I_HS_DOT_3: delete (static_cast<B_HS_DOT_3*>(busPtr)); break; case I_HS_DOT_3: delete (static_cast<B_HS_DOT_3*>(busPtr)); break;
case I_SS_DOT_3: delete (static_cast<B_SS_DOT_3*>(busPtr)); break; case I_SS_DOT_3: delete (static_cast<B_SS_DOT_3*>(busPtr)); break;
@ -1292,13 +1472,17 @@ class PolyBus {
return I_8266_U0_UCS_4 + offset; return I_8266_U0_UCS_4 + offset;
case TYPE_APA106: case TYPE_APA106:
return I_8266_U0_APA106_3 + offset; return I_8266_U0_APA106_3 + offset;
case TYPE_FW1906:
return I_8266_U0_FW6_5 + offset;
case TYPE_WS2805:
return I_8266_U0_2805_5 + offset;
} }
#else //ESP32 #else //ESP32
uint8_t offset = 0; //0 = RMT (num 0-7) 8 = I2S0 9 = I2S1 uint8_t offset = 0; // 0 = RMT (num 1-8), 1 = I2S0 (used by Audioreactive), 2 = I2S1
#if defined(CONFIG_IDF_TARGET_ESP32S2) #if defined(CONFIG_IDF_TARGET_ESP32S2)
// ESP32-S2 only has 4 RMT channels // ESP32-S2 only has 4 RMT channels
if (num > 4) return I_NONE; if (num > 4) return I_NONE;
if (num > 3) offset = 1; // only one I2S if (num > 3) offset = 1; // only one I2S (use last to allow Audioreactive)
#elif defined(CONFIG_IDF_TARGET_ESP32C3) #elif defined(CONFIG_IDF_TARGET_ESP32C3)
// On ESP32-C3 only the first 2 RMT channels are usable for transmitting // On ESP32-C3 only the first 2 RMT channels are usable for transmitting
if (num > 1) return I_NONE; if (num > 1) return I_NONE;
@ -1310,7 +1494,8 @@ class PolyBus {
#else #else
// standard ESP32 has 8 RMT and 2 I2S channels // standard ESP32 has 8 RMT and 2 I2S channels
if (num > 9) return I_NONE; if (num > 9) return I_NONE;
if (num > 7) offset = num -7; if (num > 8) offset = 1;
if (num == 0) offset = 2; // prefer I2S1 for 1st bus (less flickering but more RAM needed)
#endif #endif
switch (busType) { switch (busType) {
case TYPE_WS2812_1CH_X3: case TYPE_WS2812_1CH_X3:
@ -1332,11 +1517,14 @@ class PolyBus {
return I_32_RN_UCS_4 + offset; return I_32_RN_UCS_4 + offset;
case TYPE_APA106: case TYPE_APA106:
return I_32_RN_APA106_3 + offset; return I_32_RN_APA106_3 + offset;
case TYPE_FW1906:
return I_32_RN_FW6_5 + offset;
case TYPE_WS2805:
return I_32_RN_2805_5 + offset;
} }
#endif #endif
} }
return I_NONE; return I_NONE;
} }
}; };
#endif #endif

View File

@ -21,7 +21,6 @@ void shortPressAction(uint8_t b)
case 1: ++effectCurrent %= strip.getModeCount(); stateChanged = true; colorUpdated(CALL_MODE_BUTTON); break; case 1: ++effectCurrent %= strip.getModeCount(); stateChanged = true; colorUpdated(CALL_MODE_BUTTON); break;
} }
} else { } else {
unloadPlaylist(); // applying a preset unloads the playlist
applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET); applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET);
} }
@ -43,7 +42,6 @@ void longPressAction(uint8_t b)
case 1: bri += 8; stateUpdated(CALL_MODE_BUTTON); buttonPressedTime[b] = millis(); break; // repeatable action case 1: bri += 8; stateUpdated(CALL_MODE_BUTTON); buttonPressedTime[b] = millis(); break; // repeatable action
} }
} else { } else {
unloadPlaylist(); // applying a preset unloads the playlist
applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET); applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET);
} }
@ -65,7 +63,6 @@ void doublePressAction(uint8_t b)
case 1: ++effectPalette %= strip.getPaletteCount(); colorUpdated(CALL_MODE_BUTTON); break; case 1: ++effectPalette %= strip.getPaletteCount(); colorUpdated(CALL_MODE_BUTTON); break;
} }
} else { } else {
unloadPlaylist(); // applying a preset unloads the playlist
applyPreset(macroDoublePress[b], CALL_MODE_BUTTON_PRESET); applyPreset(macroDoublePress[b], CALL_MODE_BUTTON_PRESET);
} }
@ -99,9 +96,13 @@ bool isButtonPressed(uint8_t i)
case BTN_TYPE_TOUCH: case BTN_TYPE_TOUCH:
case BTN_TYPE_TOUCH_SWITCH: case BTN_TYPE_TOUCH_SWITCH:
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) #if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
if (digitalPinToTouchChannel(btnPin[i]) >= 0 && touchRead(pin) <= touchThreshold) return true; #ifdef SOC_TOUCH_VERSION_2 //ESP32 S2 and S3 provide a function to check touch state (state is updated in interrupt)
if (touchInterruptGetLastStatus(pin)) return true;
#else
if (digitalPinToTouchChannel(btnPin[i]) >= 0 && touchRead(pin) <= touchThreshold) return true;
#endif
#endif #endif
break; break;
} }
return false; return false;
} }
@ -406,3 +407,8 @@ void handleIO()
offMode = true; offMode = true;
} }
} }
void IRAM_ATTR touchButtonISR()
{
// used for ESP32 S2 and S3: nothing to do, ISR is just used to update registers of HAL driver
}

65
wled00/cfg.cpp Executable file → Normal file
View File

@ -110,6 +110,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
Bus::setGlobalAWMode(hw_led[F("rgbwm")] | AW_GLOBAL_DISABLED); Bus::setGlobalAWMode(hw_led[F("rgbwm")] | AW_GLOBAL_DISABLED);
CJSON(correctWB, hw_led["cct"]); CJSON(correctWB, hw_led["cct"]);
CJSON(cctFromRgb, hw_led[F("cr")]); CJSON(cctFromRgb, hw_led[F("cr")]);
CJSON(cctICused, hw_led[F("ic")]);
CJSON(strip.cctBlending, hw_led[F("cb")]); CJSON(strip.cctBlending, hw_led[F("cb")]);
Bus::setCCTBlend(strip.cctBlending); Bus::setCCTBlend(strip.cctBlending);
strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS
@ -180,12 +181,12 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
uint8_t ledType = elm["type"] | TYPE_WS2812_RGB; uint8_t ledType = elm["type"] | TYPE_WS2812_RGB;
bool reversed = elm["rev"]; bool reversed = elm["rev"];
bool refresh = elm["ref"] | false; bool refresh = elm["ref"] | false;
uint16_t freqkHz = elm[F("freq")] | 0; // will be in kHz for DotStar and Hz for PWM (not yet implemented fully) uint16_t freqkHz = elm[F("freq")] | 0; // will be in kHz for DotStar and Hz for PWM
uint8_t AWmode = elm[F("rgbwm")] | RGBW_MODE_MANUAL_ONLY; uint8_t AWmode = elm[F("rgbwm")] | RGBW_MODE_MANUAL_ONLY;
uint8_t maPerLed = elm[F("ledma")] | 55; uint8_t maPerLed = elm[F("ledma")] | 55;
uint16_t maMax = elm[F("maxpwr")] | (ablMilliampsMax * length) / total; // rough (incorrect?) per strip ABL calculation when no config exists uint16_t maMax = elm[F("maxpwr")] | (ablMilliampsMax * length) / total; // rough (incorrect?) per strip ABL calculation when no config exists
// To disable brightness limiter we either set output max current to 0 or single LED current to 0 (we choose output max current) // To disable brightness limiter we either set output max current to 0 or single LED current to 0 (we choose output max current)
if ((ledType > TYPE_TM1814 && ledType < TYPE_WS2801) || ledType >= TYPE_NET_DDP_RGB) { // analog and virtual if (IS_PWM(ledType) || IS_ONOFF(ledType) || IS_VIRTUAL(ledType)) { // analog and virtual
maPerLed = 0; maPerLed = 0;
maMax = 0; maMax = 0;
} }
@ -228,6 +229,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
// read multiple button configuration // read multiple button configuration
JsonObject btn_obj = hw["btn"]; JsonObject btn_obj = hw["btn"];
CJSON(touchThreshold, btn_obj[F("tt")]);
bool pull = btn_obj[F("pull")] | (!disablePullUp); // if true, pullup is enabled bool pull = btn_obj[F("pull")] | (!disablePullUp); // if true, pullup is enabled
disablePullUp = !pull; disablePullUp = !pull;
JsonArray hw_btn_ins = btn_obj["ins"]; JsonArray hw_btn_ins = btn_obj["ins"];
@ -252,6 +254,13 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
btnPin[s] = -1; btnPin[s] = -1;
pinManager.deallocatePin(pin,PinOwner::Button); pinManager.deallocatePin(pin,PinOwner::Button);
} }
//if touch pin, enable the touch interrupt on ESP32 S2 & S3
#ifdef SOC_TOUCH_VERSION_2 // ESP32 S2 and S3 have a fucntion to check touch state but need to attach an interrupt to do so
if ((buttonType[s] == BTN_TYPE_TOUCH || buttonType[s] == BTN_TYPE_TOUCH_SWITCH))
{
touchAttachInterrupt(btnPin[s], touchButtonISR, 256 + (touchThreshold << 4)); // threshold on Touch V2 is much higher (1500 is a value given by Espressif example, I measured changes of over 5000)
}
#endif
else else
#endif #endif
{ {
@ -286,22 +295,32 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
// new install/missing configuration (button 0 has defaults) // new install/missing configuration (button 0 has defaults)
if (fromFS) { if (fromFS) {
// relies upon only being called once with fromFS == true, which is currently true. // relies upon only being called once with fromFS == true, which is currently true.
uint8_t s = 0; for (size_t s = 0; s < WLED_MAX_BUTTONS; s++) {
if (pinManager.allocatePin(btnPin[0], false, PinOwner::Button)) { // initialized to #define value BTNPIN, or zero if not defined(!) if (buttonType[s] == BTN_TYPE_NONE || btnPin[s] < 0 || !pinManager.allocatePin(btnPin[s], false, PinOwner::Button)) {
++s; // do not clear default button if allocated successfully btnPin[s] = -1;
} buttonType[s] = BTN_TYPE_NONE;
for (; s<WLED_MAX_BUTTONS; s++) { }
btnPin[s] = -1; if (btnPin[s] >= 0) {
buttonType[s] = BTN_TYPE_NONE; if (disablePullUp) {
pinMode(btnPin[s], INPUT);
} else {
#ifdef ESP32
pinMode(btnPin[s], buttonType[s]==BTN_TYPE_PUSH_ACT_HIGH ? INPUT_PULLDOWN : INPUT_PULLUP);
#else
pinMode(btnPin[s], INPUT_PULLUP);
#endif
}
}
macroButton[s] = 0; macroButton[s] = 0;
macroLongPress[s] = 0; macroLongPress[s] = 0;
macroDoublePress[s] = 0; macroDoublePress[s] = 0;
} }
} }
} }
CJSON(touchThreshold,btn_obj[F("tt")]);
CJSON(buttonPublishMqtt,btn_obj["mqtt"]); CJSON(buttonPublishMqtt,btn_obj["mqtt"]);
#ifndef WLED_DISABLE_INFRARED
int hw_ir_pin = hw["ir"]["pin"] | -2; // 4 int hw_ir_pin = hw["ir"]["pin"] | -2; // 4
if (hw_ir_pin > -2) { if (hw_ir_pin > -2) {
pinManager.deallocatePin(irPin, PinOwner::IR); pinManager.deallocatePin(irPin, PinOwner::IR);
@ -312,6 +331,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
} }
} }
CJSON(irEnabled, hw["ir"]["type"]); CJSON(irEnabled, hw["ir"]["type"]);
#endif
CJSON(irApplyToAllSelected, hw["ir"]["sel"]); CJSON(irApplyToAllSelected, hw["ir"]["sel"]);
JsonObject relay = hw[F("relay")]; JsonObject relay = hw[F("relay")];
@ -440,7 +460,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
if (if_sync_send[F("twice")]) udpNumRetries = 1; // import setting from 0.13 and earlier if (if_sync_send[F("twice")]) udpNumRetries = 1; // import setting from 0.13 and earlier
CJSON(udpNumRetries, if_sync_send["ret"]); CJSON(udpNumRetries, if_sync_send["ret"]);
JsonObject if_nodes = interfaces[F("nodes")]; JsonObject if_nodes = interfaces["nodes"];
CJSON(nodeListEnabled, if_nodes[F("list")]); CJSON(nodeListEnabled, if_nodes[F("list")]);
CJSON(nodeBroadcastEnabled, if_nodes[F("bcast")]); CJSON(nodeBroadcastEnabled, if_nodes[F("bcast")]);
@ -616,20 +636,23 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
return (doc["sv"] | true); return (doc["sv"] | true);
} }
static const char s_cfg_json[] PROGMEM = "/cfg.json";
void deserializeConfigFromFS() { void deserializeConfigFromFS() {
bool success = deserializeConfigSec(); bool success = deserializeConfigSec();
#ifdef WLED_ADD_EEPROM_SUPPORT
if (!success) { //if file does not exist, try reading from EEPROM if (!success) { //if file does not exist, try reading from EEPROM
#ifdef WLED_ADD_EEPROM_SUPPORT
deEEPSettings(); deEEPSettings();
return; return;
#endif
} }
#endif
if (!requestJSONBufferLock(1)) return; if (!requestJSONBufferLock(1)) return;
DEBUG_PRINTLN(F("Reading settings from /cfg.json...")); DEBUG_PRINTLN(F("Reading settings from /cfg.json..."));
success = readObjectFromFile("/cfg.json", nullptr, pDoc); success = readObjectFromFile(s_cfg_json, nullptr, pDoc);
if (!success) { // if file does not exist, optionally try reading from EEPROM and then save defaults to FS if (!success) { // if file does not exist, optionally try reading from EEPROM and then save defaults to FS
releaseJSONBufferLock(); releaseJSONBufferLock();
#ifdef WLED_ADD_EEPROM_SUPPORT #ifdef WLED_ADD_EEPROM_SUPPORT
@ -753,6 +776,7 @@ void serializeConfig() {
hw_led[F("ledma")] = 0; // no longer used hw_led[F("ledma")] = 0; // no longer used
hw_led["cct"] = correctWB; hw_led["cct"] = correctWB;
hw_led[F("cr")] = cctFromRgb; hw_led[F("cr")] = cctFromRgb;
hw_led[F("ic")] = cctICused;
hw_led[F("cb")] = strip.cctBlending; hw_led[F("cb")] = strip.cctBlending;
hw_led["fps"] = strip.getTargetFps(); hw_led["fps"] = strip.getTargetFps();
hw_led[F("rgbwm")] = Bus::getGlobalAWMode(); // global auto white mode override hw_led[F("rgbwm")] = Bus::getGlobalAWMode(); // global auto white mode override
@ -835,8 +859,10 @@ void serializeConfig() {
hw_btn["mqtt"] = buttonPublishMqtt; hw_btn["mqtt"] = buttonPublishMqtt;
JsonObject hw_ir = hw.createNestedObject("ir"); JsonObject hw_ir = hw.createNestedObject("ir");
#ifndef WLED_DISABLE_INFRARED
hw_ir["pin"] = irPin; hw_ir["pin"] = irPin;
hw_ir["type"] = irEnabled; // the byte 'irEnabled' does contain the IR-Remote Type ( 0=disabled ) hw_ir["type"] = irEnabled; // the byte 'irEnabled' does contain the IR-Remote Type ( 0=disabled )
#endif
hw_ir["sel"] = irApplyToAllSelected; hw_ir["sel"] = irApplyToAllSelected;
JsonObject hw_relay = hw.createNestedObject(F("relay")); JsonObject hw_relay = hw.createNestedObject(F("relay"));
@ -913,7 +939,7 @@ void serializeConfig() {
if_sync_send["grp"] = syncGroups; if_sync_send["grp"] = syncGroups;
if_sync_send["ret"] = udpNumRetries; if_sync_send["ret"] = udpNumRetries;
JsonObject if_nodes = interfaces.createNestedObject(F("nodes")); JsonObject if_nodes = interfaces.createNestedObject("nodes");
if_nodes[F("list")] = nodeListEnabled; if_nodes[F("list")] = nodeListEnabled;
if_nodes[F("bcast")] = nodeBroadcastEnabled; if_nodes[F("bcast")] = nodeBroadcastEnabled;
@ -1052,7 +1078,7 @@ void serializeConfig() {
JsonObject usermods_settings = root.createNestedObject("um"); JsonObject usermods_settings = root.createNestedObject("um");
usermods.addToConfig(usermods_settings); usermods.addToConfig(usermods_settings);
File f = WLED_FS.open("/cfg.json", "w"); File f = WLED_FS.open(FPSTR(s_cfg_json), "w");
if (f) serializeJson(root, f); if (f) serializeJson(root, f);
f.close(); f.close();
releaseJSONBufferLock(); releaseJSONBufferLock();
@ -1060,13 +1086,16 @@ void serializeConfig() {
doSerializeConfig = false; doSerializeConfig = false;
} }
static const char s_wsec_json[] PROGMEM = "/wsec.json";
//settings in /wsec.json, not accessible via webserver, for passwords and tokens //settings in /wsec.json, not accessible via webserver, for passwords and tokens
bool deserializeConfigSec() { bool deserializeConfigSec() {
DEBUG_PRINTLN(F("Reading settings from /wsec.json...")); DEBUG_PRINTLN(F("Reading settings from /wsec.json..."));
if (!requestJSONBufferLock(3)) return false; if (!requestJSONBufferLock(3)) return false;
bool success = readObjectFromFile("/wsec.json", nullptr, pDoc); bool success = readObjectFromFile(s_wsec_json, nullptr, pDoc);
if (!success) { if (!success) {
releaseJSONBufferLock(); releaseJSONBufferLock();
return false; return false;
@ -1149,7 +1178,7 @@ void serializeConfigSec() {
ota[F("lock-wifi")] = wifiLock; ota[F("lock-wifi")] = wifiLock;
ota[F("aota")] = aOtaEnabled; ota[F("aota")] = aOtaEnabled;
File f = WLED_FS.open("/wsec.json", "w"); File f = WLED_FS.open(FPSTR(s_wsec_json), "w");
if (f) serializeJson(root, f); if (f) serializeJson(root, f);
f.close(); f.close();
releaseJSONBufferLock(); releaseJSONBufferLock();

View File

@ -53,24 +53,16 @@
#define WLED_MAX_BUSSES 3 // will allow 2 digital & 1 analog (or the other way around) #define WLED_MAX_BUSSES 3 // will allow 2 digital & 1 analog (or the other way around)
#define WLED_MIN_VIRTUAL_BUSSES 3 #define WLED_MIN_VIRTUAL_BUSSES 3
#elif defined(CONFIG_IDF_TARGET_ESP32S2) // 4 RMT, 8 LEDC, only has 1 I2S bus, supported in NPB #elif defined(CONFIG_IDF_TARGET_ESP32S2) // 4 RMT, 8 LEDC, only has 1 I2S bus, supported in NPB
#if defined(USERMOD_AUDIOREACTIVE) // requested by @softhack007 https://github.com/blazoncek/WLED/issues/33 // the 5th bus (I2S) will prevent Audioreactive usermod from functioning (it is last used though)
#define WLED_MAX_BUSSES 6 // will allow 4 digital & 2 analog #define WLED_MAX_BUSSES 7 // will allow 5 digital & 2 analog
#define WLED_MIN_VIRTUAL_BUSSES 4 #define WLED_MIN_VIRTUAL_BUSSES 3
#else
#define WLED_MAX_BUSSES 7 // will allow 5 digital & 2 analog
#define WLED_MIN_VIRTUAL_BUSSES 3
#endif
#elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, 8 LEDC, has 2 I2S but NPB does not support them ATM #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 #define WLED_MAX_BUSSES 6 // will allow 4 digital & 2 analog
#define WLED_MIN_VIRTUAL_BUSSES 4 #define WLED_MIN_VIRTUAL_BUSSES 4
#else #else
#if defined(USERMOD_AUDIOREACTIVE) // requested by @softhack007 https://github.com/blazoncek/WLED/issues/33 // the 10th digital bus (I2S0) will prevent Audioreactive usermod from functioning (it is last used though)
#define WLED_MAX_BUSSES 8 #define WLED_MAX_BUSSES 10
#define WLED_MIN_VIRTUAL_BUSSES 2 #define WLED_MIN_VIRTUAL_BUSSES 0
#else
#define WLED_MAX_BUSSES 10
#define WLED_MIN_VIRTUAL_BUSSES 0
#endif
#endif #endif
#endif #endif
#else #else
@ -93,6 +85,11 @@
#else #else
#define WLED_MAX_BUTTONS 4 #define WLED_MAX_BUTTONS 4
#endif #endif
#else
#if WLED_MAX_BUTTONS < 2
#undef WLED_MAX_BUTTONS
#define WLED_MAX_BUTTONS 2
#endif
#endif #endif
#ifdef ESP8266 #ifdef ESP8266
@ -175,6 +172,8 @@
#define USERMOD_ID_STAIRWAY_WIPE 44 //Usermod "stairway-wipe-usermod-v2.h" #define USERMOD_ID_STAIRWAY_WIPE 44 //Usermod "stairway-wipe-usermod-v2.h"
#define USERMOD_ID_ANIMARTRIX 45 //Usermod "usermod_v2_animartrix.h" #define USERMOD_ID_ANIMARTRIX 45 //Usermod "usermod_v2_animartrix.h"
#define USERMOD_ID_HTTP_PULL_LIGHT_CONTROL 46 //usermod "usermod_v2_HttpPullLightControl.h" #define USERMOD_ID_HTTP_PULL_LIGHT_CONTROL 46 //usermod "usermod_v2_HttpPullLightControl.h"
#define USERMOD_ID_TETRISAI 47 //Usermod "usermod_v2_tetris.h"
#define USERMOD_ID_MAX17048 48 //Usermod "usermod_max17048.h"
//Access point behavior //Access point behavior
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot #define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
@ -264,9 +263,11 @@
#define TYPE_TM1829 25 #define TYPE_TM1829 25
#define TYPE_UCS8903 26 #define TYPE_UCS8903 26
#define TYPE_APA106 27 #define TYPE_APA106 27
#define TYPE_FW1906 28 //RGB + CW + WW + unused channel (6 channels per IC)
#define TYPE_UCS8904 29 //first RGBW digital type (hardcoded in busmanager.cpp, memUsage()) #define TYPE_UCS8904 29 //first RGBW digital type (hardcoded in busmanager.cpp, memUsage())
#define TYPE_SK6812_RGBW 30 #define TYPE_SK6812_RGBW 30
#define TYPE_TM1814 31 #define TYPE_TM1814 31
#define TYPE_WS2805 32 //RGB + WW + CW
//"Analog" types (40-47) //"Analog" types (40-47)
#define TYPE_ONOFF 40 //binary output (relays etc.; NOT PWM) #define TYPE_ONOFF 40 //binary output (relays etc.; NOT PWM)
#define TYPE_ANALOG_1CH 41 //single channel PWM. Uses value of brightest RGBW channel #define TYPE_ANALOG_1CH 41 //single channel PWM. Uses value of brightest RGBW channel
@ -285,11 +286,13 @@
#define TYPE_NET_E131_RGB 81 //network E131 RGB bus (master broadcast bus, unused) #define TYPE_NET_E131_RGB 81 //network E131 RGB bus (master broadcast bus, unused)
#define TYPE_NET_ARTNET_RGB 82 //network ArtNet RGB bus (master broadcast bus, unused) #define TYPE_NET_ARTNET_RGB 82 //network ArtNet RGB bus (master broadcast bus, unused)
#define TYPE_NET_DDP_RGBW 88 //network DDP RGBW bus (master broadcast bus) #define TYPE_NET_DDP_RGBW 88 //network DDP RGBW bus (master broadcast bus)
#define TYPE_NET_ARTNET_RGBW 89 //network ArtNet RGB bus (master broadcast bus, unused)
#define IS_TYPE_VALID(t) ((t) > 15 && (t) < 128) #define IS_TYPE_VALID(t) ((t) > 15 && (t) < 128)
#define IS_DIGITAL(t) (((t) > 15 && (t) < 40) || ((t) > 47 && (t) < 64)) //digital are 16-39 and 48-63 #define IS_DIGITAL(t) (((t) > 15 && (t) < 40) || ((t) > 47 && (t) < 64)) //digital are 16-39 and 48-63
#define IS_2PIN(t) ((t) > 47 && (t) < 64) #define IS_2PIN(t) ((t) > 47 && (t) < 64)
#define IS_16BIT(t) ((t) == TYPE_UCS8903 || (t) == TYPE_UCS8904) #define IS_16BIT(t) ((t) == TYPE_UCS8903 || (t) == TYPE_UCS8904)
#define IS_ONOFF(t) ((t) == 40)
#define IS_PWM(t) ((t) > 40 && (t) < 46) //does not include on/Off type #define IS_PWM(t) ((t) > 40 && (t) < 46) //does not include on/Off type
#define NUM_PWM_PINS(t) ((t) - 40) //for analog PWM 41-45 only #define NUM_PWM_PINS(t) ((t) - 40) //for analog PWM 41-45 only
#define IS_VIRTUAL(t) ((t) >= 80 && (t) < 96) //this was a poor choice a better would be 96-111 #define IS_VIRTUAL(t) ((t) >= 80 && (t) < 96) //this was a poor choice a better would be 96-111
@ -367,6 +370,7 @@
//Playlist option byte //Playlist option byte
#define PL_OPTION_SHUFFLE 0x01 #define PL_OPTION_SHUFFLE 0x01
#define PL_OPTION_RESTORE 0x02
// Segment capability byte // Segment capability byte
#define SEG_CAPABILITY_RGB 0x01 #define SEG_CAPABILITY_RGB 0x01
@ -504,10 +508,10 @@
//this is merely a default now and can be changed at runtime //this is merely a default now and can be changed at runtime
#ifndef LEDPIN #ifndef LEDPIN
#if defined(ESP8266) || (defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32_PICO) #if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) //|| (defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM)) || defined(ARDUINO_ESP32_PICO)
#define LEDPIN 2 // GPIO2 (D4) on Wemos D1 mini compatible boards, and on boards where GPIO16 is not available #define LEDPIN 2 // GPIO2 (D4) on Wemos D1 mini compatible boards, safe to use on any board
#else #else
#define LEDPIN 16 // aligns with GPIO2 (D4) on Wemos D1 mini32 compatible boards #define LEDPIN 16 // aligns with GPIO2 (D4) on Wemos D1 mini32 compatible boards (if it is unusable it will be reassigned in WS2812FX::finalizeInit())
#endif #endif
#endif #endif

View File

@ -1290,6 +1290,7 @@ TD .checkmark, TD .radiomark {
margin: 0 auto 12px; margin: 0 auto 12px;
min-height: 40px; min-height: 40px;
border: 1px solid var(--c-2); border: 1px solid var(--c-2);
width: 100%;
} }
/* Simplify segments */ /* Simplify segments */

View File

@ -309,7 +309,7 @@
<button class="btn infobtn" id="resetbtn" onclick="cnfReset()">Reboot WLED</button> <button class="btn infobtn" id="resetbtn" onclick="cnfReset()">Reboot WLED</button>
</div> </div>
<br> <br>
<span class="h">Made with <span id="heart">&#10084;&#xFE0E;</span> by <a href="https://github.com/Aircoookie/" target="_blank">Aircoookie</a> and the <a href="https://wled.discourse.group/" target="_blank">WLED community</a></span> <span class="h">Made with&#32;<span id="heart">&#10084;&#xFE0E;</span>&#32;by&#32;<a href="https://github.com/Aircoookie/" target="_blank">Aircoookie</a>&#32;and the&#32;<a href="https://wled.discourse.group/" target="_blank">WLED community</a></span>
</div> </div>
<div id="nodes" class="modal"> <div id="nodes" class="modal">

View File

@ -618,7 +618,7 @@ function populatePresets(fromls)
cn += `<div class="pres lstI" id="p${i}o">`; cn += `<div class="pres lstI" id="p${i}o">`;
if (cfg.comp.pid) cn += `<div class="pid">${i}</div>`; if (cfg.comp.pid) cn += `<div class="pid">${i}</div>`;
cn += `<div class="pname lstIname" onclick="setPreset(${i})">${isPlaylist(i)?"<i class='icons btn-icon'>&#xe139;</i>":""}${pName(i)} cn += `<div class="pname lstIname" onclick="setPreset(${i})">${i==lastinfo.leds.bootps?"<i class='icons btn-icon'>&#xe410;</i>":""}${isPlaylist(i)?"<i class='icons btn-icon'>&#xe139;</i>":""}${pName(i)}
<i class="icons edit-icon flr" id="p${i}nedit" onclick="tglSegn(${i+100})">&#xe2c6;</i></div> <i class="icons edit-icon flr" id="p${i}nedit" onclick="tglSegn(${i+100})">&#xe2c6;</i></div>
<i class="icons e-icon flr" id="sege${i+100}" onclick="expand(${i+100})">&#xe395;</i> <i class="icons e-icon flr" id="sege${i+100}" onclick="expand(${i+100})">&#xe395;</i>
<div class="presin lstIcontent" id="seg${i+100}"></div> <div class="presin lstIcontent" id="seg${i+100}"></div>
@ -694,8 +694,6 @@ function parseInfo(i) {
function populateInfo(i) function populateInfo(i)
{ {
var cn=""; var cn="";
var heap = i.freeheap/1024;
heap = heap.toFixed(1);
var pwr = i.leds.pwr; var pwr = i.leds.pwr;
var pwru = "Not calculated"; var pwru = "Not calculated";
if (pwr > 1000) {pwr /= 1000; pwr = pwr.toFixed((pwr > 10) ? 0 : 1); pwru = pwr + " A";} if (pwr > 1000) {pwr /= 1000; pwr = pwr.toFixed((pwr > 10) ? 0 : 1); pwru = pwr + " A";}
@ -720,11 +718,13 @@ ${inforow("Build",i.vid)}
${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")} ${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")}
${inforow("Uptime",getRuntimeStr(i.uptime))} ${inforow("Uptime",getRuntimeStr(i.uptime))}
${inforow("Time",i.time)} ${inforow("Time",i.time)}
${inforow("Free heap",heap," kB")} ${inforow("Free heap",(i.freeheap/1024).toFixed(1)," kB")}
${i.psram?inforow("Free PSRAM",(i.psram/1024).toFixed(1)," kB"):""} ${i.psram?inforow("Free PSRAM",(i.psram/1024).toFixed(1)," kB"):""}
${inforow("Estimated current",pwru)} ${inforow("Estimated current",pwru)}
${inforow("Average FPS",i.leds.fps)} ${inforow("Average FPS",i.leds.fps)}
${inforow("MAC address",i.mac)} ${inforow("MAC address",i.mac)}
${inforow("CPU clock",i.clock," MHz")}
${inforow("Flash size",i.flash," MB")}
${inforow("Filesystem",i.fs.u + "/" + i.fs.t + " kB (" +Math.round(i.fs.u*100/i.fs.t) + "%)")} ${inforow("Filesystem",i.fs.u + "/" + i.fs.t + " kB (" +Math.round(i.fs.u*100/i.fs.t) + "%)")}
${inforow("Environment",i.arch + " " + i.core + " (" + i.lwip + ")")} ${inforow("Environment",i.arch + " " + i.core + " (" + i.lwip + ")")}
</table>`; </table>`;
@ -869,10 +869,11 @@ function populateSegments(s)
updateLen(i); updateLen(i);
updateTrail(gId(`seg${i}bri`)); updateTrail(gId(`seg${i}bri`));
gId(`segr${i}`).classList.add("hide"); gId(`segr${i}`).classList.add("hide");
//if (i<lSeg) gId(`segd${i}`).classList.add("hide"); // hide delete button for all but last
if (!gId(`seg${i}sel`).checked && gId('selall')) gId('selall').checked = false; // uncheck if at least one is unselected. if (!gId(`seg${i}sel`).checked && gId('selall')) gId('selall').checked = false; // uncheck if at least one is unselected.
} }
if (segCount < 2) { if (segCount < 2) {
gId(`segd${lSeg}`).classList.add("hide"); gId(`segd${lSeg}`).classList.add("hide"); // hide delete if only one segment
if (parseInt(gId("seg0bri").value)==255) gId(`segp0`).classList.add("hide"); if (parseInt(gId("seg0bri").value)==255) gId(`segp0`).classList.add("hide");
// hide segment controls if there is only one segment in simplified UI // hide segment controls if there is only one segment in simplified UI
if (simplifiedUI) gId("segcont").classList.add("hide"); if (simplifiedUI) gId("segcont").classList.add("hide");
@ -1440,7 +1441,7 @@ function readState(s,command=false)
if (s.seg[i].sel) { if (s.seg[i].sel) {
if (sellvl < 2) selc = i; // get first selected segment if (sellvl < 2) selc = i; // get first selected segment
sellvl = 2; sellvl = 2;
var lc = lastinfo.leds.seglc[s.seg[i].id]; var lc = lastinfo.leds.seglc[i];
hasRGB |= !!(lc & 0x01); hasRGB |= !!(lc & 0x01);
hasWhite |= !!(lc & 0x02); hasWhite |= !!(lc & 0x02);
hasCCT |= !!(lc & 0x04); hasCCT |= !!(lc & 0x04);
@ -1450,7 +1451,7 @@ function readState(s,command=false)
} }
var i=s.seg[selc]; var i=s.seg[selc];
if (sellvl == 1) { if (sellvl == 1) {
var lc = lastinfo.leds.seglc[i.id]; var lc = lastinfo.leds.seglc[selc];
hasRGB = !!(lc & 0x01); hasRGB = !!(lc & 0x01);
hasWhite = !!(lc & 0x02); hasWhite = !!(lc & 0x02);
hasCCT = !!(lc & 0x04); hasCCT = !!(lc & 0x04);
@ -1819,17 +1820,16 @@ function toggleNodes()
function makeSeg() function makeSeg()
{ {
var ns = 0, ct = 0; var ns = 0, ct = isM ? mw : ledCount;
var lu = lowestUnused; var lu = lowestUnused;
let li = lastinfo; let li = lastinfo;
if (lu > 0) { if (lu > 0) {
let xend = parseInt(gId(`seg${lu -1}e`).value,10) + (cfg.comp.seglen?parseInt(gId(`seg${lu -1}s`).value,10):0); let xend = parseInt(gId(`seg${lu -1}e`).value,10) + (cfg.comp.seglen?parseInt(gId(`seg${lu -1}s`).value,10):0);
if (isM) { if (isM) {
ns = 0; ns = 0;
ct = mw;
} else { } else {
if (xend < ledCount) ns = xend; if (xend < ledCount) ns = xend;
ct = ledCount-(cfg.comp.seglen?ns:0) ct -= cfg.comp.seglen?ns:0;
} }
} }
gId('segutil').scrollIntoView({ gId('segutil').scrollIntoView({
@ -1959,6 +1959,7 @@ function plR(p)
function makeP(i,pl) function makeP(i,pl)
{ {
var content = ""; var content = "";
const bps = lastinfo.leds.bootps;
if (pl) { if (pl) {
if (i===0) plJson[0] = { if (i===0) plJson[0] = {
ps: [1], ps: [1],
@ -1983,7 +1984,7 @@ function makeP(i,pl)
<div class="sel">End preset:<br> <div class="sel">End preset:<br>
<div class="sel-p"><select class="sel-ple" id="pl${i}selEnd" onchange="plR(${i})" data-val=${plJson[i].end?plJson[i].end:0}> <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="0">None</option>
<option value="255">Restore preset</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(plJson[i].end?plJson[i].end:0, true)}
</select></div></div> </select></div></div>
</div> </div>
@ -2024,6 +2025,11 @@ ${makePlSel(plJson[i].end?plJson[i].end:0, true)}
</div> </div>
<div class="po2" id="p${i}o2">API command<br><textarea class="apitxt" id="p${i}api"></textarea></div> <div class="po2" id="p${i}o2">API command<br><textarea class="apitxt" id="p${i}api"></textarea></div>
<div class="po1" id="p${i}o1">${content}</div> <div class="po1" id="p${i}o1">${content}</div>
<label class="check revchkl">
<span class="lstIname">Apply at boot</span>
<input type="checkbox" id="p${i}bps" ${i==bps?"checked":""}>
<span class="checkmark"></span>
</label>
<div class="c m6">Save to ID <input id="p${i}id" type="number" oninput="checkUsed(${i})" max=250 min=1 value=${(i>0)?i:getLowestUnusedP()}></div> <div class="c m6">Save to ID <input id="p${i}id" type="number" oninput="checkUsed(${i})" max=250 min=1 value=${(i>0)?i:getLowestUnusedP()}></div>
<div class="c"> <div class="c">
<button class="btn btn-p" onclick="saveP(${i},${pl})"><i class="icons btn-icon">&#xe390;</i>Save</button> <button class="btn btn-p" onclick="saveP(${i},${pl})"><i class="icons btn-icon">&#xe390;</i>Save</button>
@ -2445,8 +2451,9 @@ function saveP(i,pl)
if (gId(`p${i}lmp`) && gId(`p${i}lmp`).value!=="") obj.ledmap = parseInt(gId(`p${i}lmp`).value); if (gId(`p${i}lmp`) && gId(`p${i}lmp`).value!=="") obj.ledmap = parseInt(gId(`p${i}lmp`).value);
} }
} }
if (gId(`p${i}bps`).checked) obj.bootps = pI;
obj.psave = pI; obj.n = pN; obj.psave = pI;
obj.n = pN;
var pQN = gId(`p${i}ql`).value; var pQN = gId(`p${i}ql`).value;
if (pQN.length > 0) obj.ql = pQN; if (pQN.length > 0) obj.ql = pQN;
@ -2794,7 +2801,12 @@ function search(field, listId = null) {
if (!listId) return; if (!listId) return;
const search = field.value !== ''; const search = field.value !== '';
const presets = listId === 'pcont';
// restore default preset sorting if no search term is entered
if (listId === 'pcont' && !search) {
populatePresets();
return;
}
// clear filter if searching in fxlist // clear filter if searching in fxlist
if (listId === 'fxlist' && search) { if (listId === 'fxlist' && search) {
@ -2806,7 +2818,7 @@ function search(field, listId = null) {
const listItems = gId(listId).querySelectorAll('.lstI'); const listItems = gId(listId).querySelectorAll('.lstI');
// filter list items but leave (Default & Solid) always visible // filter list items but leave (Default & Solid) always visible
for (i = (presets ? 0 : 1); i < listItems.length; i++) { for (i = (listId === 'pcont' ? 0 : 1); i < listItems.length; i++) {
const listItem = listItems[i]; const listItem = listItems[i];
const listItemName = listItem.querySelector('.lstIname').innerText.toUpperCase(); const listItemName = listItem.querySelector('.lstIname').innerText.toUpperCase();
const searchIndex = listItemName.indexOf(field.value.toUpperCase()); const searchIndex = listItemName.indexOf(field.value.toUpperCase());
@ -2814,30 +2826,28 @@ function search(field, listId = null) {
listItem.dataset.searchIndex = searchIndex; listItem.dataset.searchIndex = searchIndex;
} }
if (!presets) { // sort list items by search index and name
// sort list items by search index and name const sortedListItems = Array.from(listItems).sort((a, b) => {
const sortedListItems = Array.from(listItems).sort((a, b) => { const aSearchIndex = parseInt(a.dataset.searchIndex);
const aSearchIndex = parseInt(a.dataset.searchIndex); const bSearchIndex = parseInt(b.dataset.searchIndex);
const bSearchIndex = parseInt(b.dataset.searchIndex);
if (aSearchIndex !== bSearchIndex) { if (aSearchIndex !== bSearchIndex) {
return aSearchIndex - bSearchIndex; return aSearchIndex - bSearchIndex;
}
const aName = a.querySelector('.lstIname').innerText.toUpperCase();
const bName = b.querySelector('.lstIname').innerText.toUpperCase();
return aName.localeCompare(bName);
});
sortedListItems.forEach(item => {
gId(listId).append(item);
});
// scroll to first search result
const firstVisibleItem = sortedListItems.find(item => item.style.display !== 'none' && !item.classList.contains('sticky') && !item.classList.contains('selected'));
if (firstVisibleItem && search) {
firstVisibleItem.scrollIntoView({ behavior: "instant", block: "center" });
} }
const aName = a.querySelector('.lstIname').innerText.toUpperCase();
const bName = b.querySelector('.lstIname').innerText.toUpperCase();
return aName.localeCompare(bName);
});
sortedListItems.forEach(item => {
gId(listId).append(item);
});
// scroll to first search result
const firstVisibleItem = sortedListItems.find(item => item.style.display !== 'none' && !item.classList.contains('sticky') && !item.classList.contains('selected'));
if (firstVisibleItem && search) {
firstVisibleItem.scrollIntoView({ behavior: "instant", block: "center" });
} }
} }

View File

@ -23,6 +23,8 @@
function isD2P(t) { return t > 47 && t < 64; } // is digital 2 pin type function isD2P(t) { return t > 47 && t < 64; } // is digital 2 pin type
function is16b(t) { return t == 26 || t == 29 } // is digital 16 bit type function is16b(t) { return t == 26 || t == 29 } // is digital 16 bit type
function isVir(t) { return t >= 80 && t < 96; } // is virtual type function isVir(t) { return t >= 80 && t < 96; } // is virtual type
function hasW(t) { return (t >= 18 && t <= 21) || (t >= 28 && t <= 32) || (t >= 44 && t <= 45) || (t >= 88 && t <= 89); }
function hasCCT(t) { return t == 20 || t == 21 || t == 42 || t == 45 || t == 28 || t == 32; }
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript // https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) { function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script"); let scE = d.createElement("script");
@ -123,10 +125,10 @@
gId('abl').style.display = (en) ? 'inline':'none'; gId('abl').style.display = (en) ? 'inline':'none';
gId('psu2').style.display = (en) ? 'inline':'none'; gId('psu2').style.display = (en) ? 'inline':'none';
if (!en) d.Sf.PPL.checked = false; if (!en) d.Sf.PPL.checked = false;
enPPL();
UI(); UI();
} }
function enPPL() // enable per port limiter and calculate current
function enPPL(sDI=0)
{ {
const abl = d.Sf.ABL.checked; const abl = d.Sf.ABL.checked;
const ppl = d.Sf.PPL.checked; const ppl = d.Sf.PPL.checked;
@ -139,9 +141,11 @@
d.Sf.querySelectorAll("#mLC input[name^=MA]").forEach((i,n)=>{ d.Sf.querySelectorAll("#mLC input[name^=MA]").forEach((i,n)=>{
gId("PSU"+n).style.display = ppl ? "inline" : "none"; gId("PSU"+n).style.display = ppl ? "inline" : "none";
const t = parseInt(d.Sf["LT"+n].value); // LED type SELECT const t = parseInt(d.Sf["LT"+n].value); // LED type SELECT
const c = parseInt(d.Sf["LC"+n].value); //get LED count
i.min = ppl && !(isVir(t) || isAna(t)) ? 250 : 0; i.min = ppl && !(isVir(t) || isAna(t)) ? 250 : 0;
if (!abl) i.value = 0; if (!abl || isVir(t) || isAna(t)) i.value = 0;
else if (ppl) sumMA += parseInt(i.value,10); else if (ppl) sumMA += parseInt(i.value,10);
else if (sDI) i.value = Math.round(parseInt(d.Sf.MA.value,10)*c/sDI);
}); });
if (ppl) d.Sf.MA.value = sumMA; // populate UI ABL value if PPL used if (ppl) d.Sf.MA.value = sumMA; // populate UI ABL value if PPL used
} }
@ -186,6 +190,7 @@
if (isDig(t)) { if (isDig(t)) {
if (is16b(t)) len *= 2; // 16 bit LEDs if (is16b(t)) len *= 2; // 16 bit LEDs
if (t > 28 && t < 40) ch = 4; //RGBW if (t > 28 && t < 40) ch = 4; //RGBW
if (t == 28) ch = 5; //GRBCW
if (maxM < 10000 && d.getElementsByName("L0"+n)[0].value == 3) { //8266 DMA uses 5x the mem if (maxM < 10000 && d.getElementsByName("L0"+n)[0].value == 3) { //8266 DMA uses 5x the mem
mul = 5; mul = 5;
} }
@ -200,11 +205,10 @@
function UI(change=false) function UI(change=false)
{ {
let isRGBW = false, gRGBW = false, memu = 0; let gRGBW = false, memu = 0;
let sumMA = 0, busMA = 0; let busMA = 0;
let sLC = 0, sPC = 0, sDI = 0, maxLC = 0; let sLC = 0, sPC = 0, sDI = 0, maxLC = 0;
const ablEN = d.Sf.ABL.checked; const ablEN = d.Sf.ABL.checked;
const pplEN = d.Sf.PPL.checked;
// enable/disable LED fields // enable/disable LED fields
d.Sf.querySelectorAll("#mLC select[name^=LT]").forEach((s)=>{ d.Sf.querySelectorAll("#mLC select[name^=LT]").forEach((s)=>{
@ -241,21 +245,18 @@
d.Sf["MA"+n].min = (isVir(t) || isAna(t)) ? 0 : 250; d.Sf["MA"+n].min = (isVir(t) || isAna(t)) ? 0 : 250;
} }
gId("rf"+n).onclick = (t == 31) ? (()=>{return false}) : (()=>{}); // prevent change for TM1814 gId("rf"+n).onclick = (t == 31) ? (()=>{return false}) : (()=>{}); // prevent change for TM1814
gRGBW |= isRGBW = ((t > 17 && t < 22) || (t > 28 && t < 32) || (t > 40 && t < 46 && t != 43) || t == 88); // RGBW checkbox, TYPE_xxxx values from const.h gRGBW |= hasW(t); // RGBW checkbox, TYPE_xxxx values from const.h
gId("co"+n).style.display = (isVir(t) || isAna(t)) ? "none":"inline"; // hide color order for PWM gId("co"+n).style.display = (isVir(t) || isAna(t)) ? "none":"inline"; // hide color order for PWM
gId("dig"+n+"w").style.display = (isDig(t) && isRGBW) ? "inline":"none"; // show swap channels dropdown gId("dig"+n+"w").style.display = (isDig(t) && hasW(t)) ? "inline":"none"; // show swap channels dropdown
if (!(isDig(t) && isRGBW)) d.Sf["WO"+n].value = 0; // reset swapping if (!(isDig(t) && hasW(t))) d.Sf["WO"+n].value = 0; // reset swapping
gId("dig"+n+"c").style.display = (isAna(t)) ? "none":"inline"; // hide count for analog gId("dig"+n+"c").style.display = (isAna(t)) ? "none":"inline"; // hide count for analog
gId("dig"+n+"r").style.display = (isVir(t)) ? "none":"inline"; // hide reversed for virtual gId("dig"+n+"r").style.display = (isVir(t)) ? "none":"inline"; // hide reversed for virtual
gId("dig"+n+"s").style.display = (isVir(t) || isAna(t)) ? "none":"inline"; // hide skip 1st for virtual & analog gId("dig"+n+"s").style.display = (isVir(t) || isAna(t)) ? "none":"inline"; // hide skip 1st for virtual & analog
gId("dig"+n+"f").style.display = (isDig(t)) ? "inline":"none"; // hide refresh gId("dig"+n+"f").style.display = (isDig(t)) ? "inline":"none"; // hide refresh
gId("dig"+n+"a").style.display = (isRGBW) ? "inline":"none"; // auto calculate white gId("dig"+n+"a").style.display = (hasW(t)) ? "inline":"none"; // auto calculate white
gId("dig"+n+"l").style.display = (isD2P(t) || isPWM(t)) ? "inline":"none"; // bus clock speed / PWM speed (relative) (not On/Off) gId("dig"+n+"l").style.display = (isD2P(t) || isPWM(t)) ? "inline":"none"; // bus clock speed / PWM speed (relative) (not On/Off)
gId("rev"+n).innerHTML = isAna(t) ? "Inverted output":"Reversed (rotated 180°)"; // change reverse text for analog gId("rev"+n).innerHTML = isAna(t) ? "Inverted output":"Reversed (rotated 180°)"; // change reverse text for analog
//gId("psd"+n).innerHTML = isAna(t) ? "Index:":"Start:"; // change analog start description //gId("psd"+n).innerHTML = isAna(t) ? "Index:":"Start:"; // change analog start description
if (ablEN && pplEN && !(isVir(t) || isAna(t))) {
sumMA += parseInt(d.Sf["MA"+n].value); // summarize PPL ABL limit (fields)
}
}); });
// display global white channel overrides // display global white channel overrides
gId("wc").style.display = (gRGBW) ? 'inline':'none'; gId("wc").style.display = (gRGBW) ? 'inline':'none';
@ -279,7 +280,6 @@
if (s+c > sLC) sLC = s+c; //update total count if (s+c > sLC) sLC = s+c; //update total count
if (c > maxLC) maxLC = c; //max per output if (c > maxLC) maxLC = c; //max per output
if (!isVir(t)) sPC += c; //virtual out busses do not count towards physical LEDs if (!isVir(t)) sPC += c; //virtual out busses do not count towards physical LEDs
//if (!(isVir(t) || isPWM(t))) sDI += c;
if (!(isVir(t) || isAna(t))) { if (!(isVir(t) || isAna(t))) {
sDI += c; // summarize digital LED count sDI += c; // summarize digital LED count
let maPL = parseInt(d.Sf["LA"+n].value); let maPL = parseInt(d.Sf["LA"+n].value);
@ -327,18 +327,9 @@
else LC.style.color = d.ro_gpio.some((e)=>e==parseInt(LC.value)) ? "orange" : "#fff"; else LC.style.color = d.ro_gpio.some((e)=>e==parseInt(LC.value)) ? "orange" : "#fff";
} }
}); });
// distribute ABL current if not using PPL, otherwise sumMA contains summarized ABL limit // distribute ABL current if not using PPL
d.Sf.querySelectorAll("#mLC input[name^=LC]").forEach((s,n)=>{ enPPL(sDI);
let c = parseInt(s.value,10); //get LED count
let t = parseInt(d.Sf["LT"+n].value); //get LED type
if (!ablEN || isVir(t) || isAna(t)) {
// virtual and analog LEDs have no limiter
d.Sf["MA"+n].value = 0;
return;
}
if (!pplEN) d.Sf["MA"+n].value = Math.round(parseInt(d.Sf.MA.value,10)*c/sDI);
});
if (pplEN) d.Sf.MA.value = sumMA; // update global ABL if using PPL
// update total led count // update total led count
gId("lc").textContent = sLC; gId("lc").textContent = sLC;
gId("pc").textContent = (sLC == sPC) ? "":"(" + sPC + " physical)"; gId("pc").textContent = (sLC == sPC) ? "":"(" + sPC + " physical)";
@ -395,7 +386,9 @@ ${i+1}:
<option value="25">TM1829</option>\ <option value="25">TM1829</option>\
<option value="26">UCS8903</option>\ <option value="26">UCS8903</option>\
<option value="27">APA106/PL9823</option>\ <option value="27">APA106/PL9823</option>\
<option value="28">FW1906 GRBCW</option>\
<option value="29">UCS8904 RGBW</option>\ <option value="29">UCS8904 RGBW</option>\
<option value="32">WS2805 RGBCW</option>\
<option value="50">WS2801</option>\ <option value="50">WS2801</option>\
<option value="51">APA102</option>\ <option value="51">APA102</option>\
<option value="52">LPD8806</option>\ <option value="52">LPD8806</option>\
@ -413,6 +406,7 @@ ${i+1}:
<!--option value="81">E1.31 RGB (network)</option--> <!--option value="81">E1.31 RGB (network)</option-->
<option value="82">Art-Net RGB (network)</option> <option value="82">Art-Net RGB (network)</option>
<option value="88">DDP RGBW (network)</option> <option value="88">DDP RGBW (network)</option>
<option value="89">Art-Net RGBW (network)</option>
</select><br> </select><br>
<div id="abl${i}"> <div id="abl${i}">
mA/LED: <select name="LAsel${i}" onchange="enLA(this,${i});UI();"> mA/LED: <select name="LAsel${i}" onchange="enLA(this,${i});UI();">
@ -461,7 +455,6 @@ mA/LED: <select name="LAsel${i}" onchange="enLA(this,${i});UI();">
gId("-").style.display = (i>0) ? "inline":"none"; gId("-").style.display = (i>0) ? "inline":"none";
if (!init) { if (!init) {
enPPL();
UI(); UI();
} }
} }
@ -776,7 +769,7 @@ Swap: <select id="xw${i}" name="XW${i}">
Keep at &lt;1A if poweing LEDs directly from the ESP 5V pin!<br> Keep at &lt;1A if poweing LEDs directly from the ESP 5V pin!<br>
Analog (PWM) and virtual LEDs cannot use automatic brightness limiter.<br></i> Analog (PWM) and virtual LEDs cannot use automatic brightness limiter.<br></i>
<div id="psuMA">Maximum PSU Current: <input name="MA" type="number" class="xl" min="250" max="65000" oninput="UI()" required> mA<br></div> <div id="psuMA">Maximum PSU Current: <input name="MA" type="number" class="xl" min="250" max="65000" oninput="UI()" required> mA<br></div>
Use per-output limiter: <input type="checkbox" name="PPL" onchange="enPPL()"><br> Use per-output limiter: <input type="checkbox" name="PPL" onchange="UI()"><br>
<div id="ppldis" style="display:none;"> <div id="ppldis" style="display:none;">
<i>Make sure you enter correct values in each LED output.<br> <i>Make sure you enter correct values in each LED output.<br>
If using multiple outputs with only one PSU, distribute its power proportionally amongst ouputs.</i><br> If using multiple outputs with only one PSU, distribute its power proportionally amongst ouputs.</i><br>
@ -873,6 +866,7 @@ Swap: <select id="xw${i}" name="XW${i}">
</select> </select>
<br> <br>
Calculate CCT from RGB: <input type="checkbox" name="CR"><br> Calculate CCT from RGB: <input type="checkbox" name="CR"><br>
CCT IC used (Athom 15W): <input type="checkbox" name="IC"><br>
CCT additive blending: <input type="number" class="s" min="0" max="100" name="CB" required> % CCT additive blending: <input type="number" class="s" min="0" max="100" name="CB" required> %
</div> </div>
<h3>Advanced</h3> <h3>Advanced</h3>

View File

@ -124,20 +124,20 @@
Enable ArduinoOTA: <input type="checkbox" name="AO"> Enable ArduinoOTA: <input type="checkbox" name="AO">
<hr> <hr>
<h3>Backup & Restore</h3> <h3>Backup & Restore</h3>
<div class="warn">&#9888; Restoring presets/configuration will OVERWRITE your current presets/configuration.<br>
Incorrect upload or configuration may require a factory reset or re-flashing of your ESP.</div>
For security reasons, passwords are not backed up.
<a class="btn lnk" id="bckcfg" href="/presets.json" download="presets">Backup presets</a><br> <a class="btn lnk" id="bckcfg" href="/presets.json" download="presets">Backup presets</a><br>
<div>Restore presets<br><input type="file" name="data" accept=".json"> <button type="button" onclick="uploadFile(d.Sf.data,'/presets.json');">Upload</button><br></div><br> <div>Restore presets<br><input type="file" name="data" accept=".json"> <button type="button" onclick="uploadFile(d.Sf.data,'/presets.json');">Upload</button><br></div><br>
<a class="btn lnk" id="bckpresets" href="/cfg.json" download="cfg">Backup configuration</a><br> <a class="btn lnk" id="bckpresets" href="/cfg.json" download="cfg">Backup configuration</a><br>
<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> <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>
<div class="warn">&#9888; Restoring presets/configuration will OVERWRITE your current presets/configuration.<br>
Incorrect configuration may require a factory reset or re-flashing of your ESP.</div>
For security reasons, passwords are not backed up.
<hr> <hr>
<h3>About</h3> <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/" target="_blank">WLED</a>&#32;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/Aircoookie/WLED/wiki/Contributors-and-credits" target="_blank">Contributors, dependencies and special thanks</a><br>
A huge thank you to everyone who helped me create WLED!<br><br> A huge thank you to everyone who helped me create WLED!<br><br>
(c) 2016-2023 Christian Schwinne <br> (c) 2016-2024 Christian Schwinne <br>
<i>Licensed under the <a href="https://github.com/Aircoookie/WLED/blob/master/LICENSE" target="_blank">MIT license</a></i><br><br> <i>Licensed under the&#32;<a href="https://github.com/Aircoookie/WLED/blob/master/LICENSE" target="_blank">MIT license</a></i><br><br>
Server message: <span class="sip"> Response error! </span><hr> Server message: <span class="sip"> Response error! </span><hr>
<div id="toast"></div> <div id="toast"></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button> <button type="button" onclick="B()">Back</button><button type="submit">Save</button>

View File

@ -84,7 +84,7 @@
option.textContent = "Other network..."; option.textContent = "Other network...";
select.appendChild(option); select.appendChild(option);
if (input.value === "" || found) input.replaceWith(select); if (input.value === "" || input.value === "Your_Network" || found) input.replaceWith(select);
else select.remove(); else select.remove();
} }

View File

@ -11,6 +11,7 @@
//DDP protocol support, called by handleE131Packet //DDP protocol support, called by handleE131Packet
//handles RGB data only //handles RGB data only
void handleDDPPacket(e131_packet_t* p) { void handleDDPPacket(e131_packet_t* p) {
static bool ddpSeenPush = false; // have we seen a push yet?
int lastPushSeq = e131LastSequenceNumber[0]; int lastPushSeq = e131LastSequenceNumber[0];
//reject late packets belonging to previous frame (assuming 4 packets max. before push) //reject late packets belonging to previous frame (assuming 4 packets max. before push)
@ -34,6 +35,7 @@ void handleDDPPacket(e131_packet_t* p) {
uint16_t c = 0; uint16_t c = 0;
if (p->flags & DDP_TIMECODE_FLAG) c = 4; //packet has timecode flag, we do not support it, but data starts 4 bytes later if (p->flags & DDP_TIMECODE_FLAG) c = 4; //packet has timecode flag, we do not support it, but data starts 4 bytes later
if (realtimeMode != REALTIME_MODE_DDP) ddpSeenPush = false; // just starting, no push yet
realtimeLock(realtimeTimeoutMs, REALTIME_MODE_DDP); realtimeLock(realtimeTimeoutMs, REALTIME_MODE_DDP);
if (!realtimeOverride || (realtimeMode && useMainSegmentOnly)) { if (!realtimeOverride || (realtimeMode && useMainSegmentOnly)) {
@ -44,7 +46,8 @@ void handleDDPPacket(e131_packet_t* p) {
} }
bool push = p->flags & DDP_PUSH_FLAG; bool push = p->flags & DDP_PUSH_FLAG;
if (push) { ddpSeenPush |= push;
if (!ddpSeenPush || push) { // if we've never seen a push, or this is one, render display
e131NewData = true; e131NewData = true;
byte sn = p->sequenceNum & 0xF; byte sn = p->sequenceNum & 0xF;
if (sn) e131LastSequenceNumber[0] = sn; if (sn) e131LastSequenceNumber[0] = sn;
@ -184,7 +187,6 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
// only apply preset if not in playlist, or playlist changed // only apply preset if not in playlist, or playlist changed
(currentPlaylist < 0 || dmxValPreset != currentPlaylist)) { (currentPlaylist < 0 || dmxValPreset != currentPlaylist)) {
presetCycCurr = dmxValPreset; presetCycCurr = dmxValPreset;
unloadPlaylist(); // applying a preset unloads the playlist
applyPreset(dmxValPreset, CALL_MODE_NOTIFICATION); applyPreset(dmxValPreset, CALL_MODE_NOTIFICATION);
} }
@ -225,11 +227,16 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
if (e131_data[dataOffset+3] != seg.intensity) seg.intensity = e131_data[dataOffset+3]; if (e131_data[dataOffset+3] != seg.intensity) seg.intensity = e131_data[dataOffset+3];
if (e131_data[dataOffset+4] != seg.palette) seg.setPalette(e131_data[dataOffset+4]); if (e131_data[dataOffset+4] != seg.palette) seg.setPalette(e131_data[dataOffset+4]);
uint8_t segOption = (uint8_t)floor(e131_data[dataOffset+5]/64.0); if ((e131_data[dataOffset+5] & 0b00000010) != seg.reverse_y) { seg.setOption(SEG_OPTION_REVERSED_Y, e131_data[dataOffset+5] & 0b00000010); }
if (segOption == 0 && (seg.mirror || seg.reverse )) {seg.setOption(SEG_OPTION_MIRROR, false); seg.setOption(SEG_OPTION_REVERSED, false);} if ((e131_data[dataOffset+5] & 0b00000100) != seg.mirror_y) { seg.setOption(SEG_OPTION_MIRROR_Y, e131_data[dataOffset+5] & 0b00000100); }
if (segOption == 1 && (seg.mirror || !seg.reverse)) {seg.setOption(SEG_OPTION_MIRROR, false); seg.setOption(SEG_OPTION_REVERSED, true);} if ((e131_data[dataOffset+5] & 0b00001000) != seg.transpose) { seg.setOption(SEG_OPTION_TRANSPOSED, e131_data[dataOffset+5] & 0b00001000); }
if (segOption == 2 && (!seg.mirror || seg.reverse )) {seg.setOption(SEG_OPTION_MIRROR, true); seg.setOption(SEG_OPTION_REVERSED, false);} if ((e131_data[dataOffset+5] & 0b00110000) / 8 != seg.map1D2D) {
if (segOption == 3 && (!seg.mirror || !seg.reverse)) {seg.setOption(SEG_OPTION_MIRROR, true); seg.setOption(SEG_OPTION_REVERSED, true);} seg.map1D2D = (e131_data[dataOffset+5] & 0b00110000) / 8;
}
// To maintain backwards compatibility with prior e1.31 values, reverse is fixed to mask 0x01000000
if ((e131_data[dataOffset+5] & 0b01000000) != seg.reverse) { seg.setOption(SEG_OPTION_REVERSED, e131_data[dataOffset+5] & 0b01000000); }
// To maintain backwards compatibility with prior e1.31 values, mirror is fixed to mask 0x10000000
if ((e131_data[dataOffset+5] & 0b10000000) != seg.mirror) { seg.setOption(SEG_OPTION_MIRROR, e131_data[dataOffset+5] & 0b10000000); }
uint32_t colors[3]; uint32_t colors[3];
byte whites[3] = {0,0,0}; byte whites[3] = {0,0,0};

View File

@ -20,6 +20,7 @@ void doublePressAction(uint8_t b=0);
bool isButtonPressed(uint8_t b=0); bool isButtonPressed(uint8_t b=0);
void handleButton(); void handleButton();
void handleIO(); void handleIO();
void IRAM_ATTR touchButtonISR();
//cfg.cpp //cfg.cpp
bool deserializeConfig(JsonObject doc, bool fromFS = false); bool deserializeConfig(JsonObject doc, bool fromFS = false);
@ -112,6 +113,10 @@ bool readObjectFromFileUsingId(const char* file, uint16_t id, JsonDocument* dest
bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest); bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest);
void updateFSInfo(); void updateFSInfo();
void closeFile(); void closeFile();
inline bool writeObjectToFileUsingId(const String &file, uint16_t id, JsonDocument* content) { return writeObjectToFileUsingId(file.c_str(), id, content); };
inline bool writeObjectToFile(const String &file, const char* key, 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); };
//hue.cpp //hue.cpp
void handleHue(); void handleHue();
@ -225,9 +230,11 @@ void handlePlaylist();
void serializePlaylist(JsonObject obj); void serializePlaylist(JsonObject obj);
//presets.cpp //presets.cpp
const char *getPresetsFileName(bool persistent = true);
void initPresetsFile(); void initPresetsFile();
void handlePresets(); void handlePresets();
bool applyPreset(byte index, byte callMode = CALL_MODE_DIRECT_CHANGE); bool applyPreset(byte index, byte callMode = CALL_MODE_DIRECT_CHANGE);
bool applyPresetFromPlaylist(byte index);
void applyPresetWithFallback(uint8_t presetID, uint8_t callMode, uint8_t effectID = 0, uint8_t paletteID = 0); void applyPresetWithFallback(uint8_t presetID, uint8_t callMode, uint8_t effectID = 0, uint8_t paletteID = 0);
inline bool applyTemporaryPreset() {return applyPreset(255);}; inline bool applyTemporaryPreset() {return applyPreset(255);};
void savePreset(byte index, const char* pname = nullptr, JsonObject saveobj = JsonObject()); void savePreset(byte index, const char* pname = nullptr, JsonObject saveobj = JsonObject());

View File

@ -272,8 +272,8 @@ bool writeObjectToFile(const char* file, const char* key, JsonDocument* content)
#endif #endif
size_t pos = 0; size_t pos = 0;
f = WLED_FS.open(file, "r+"); char fileName[129]; strncpy_P(fileName, file, 128); fileName[128] = 0; //use PROGMEM safe copy as FS.open() does not
if (!f && !WLED_FS.exists(file)) f = WLED_FS.open(file, "w+"); f = WLED_FS.open(fileName, WLED_FS.exists(fileName) ? "r+" : "w+");
if (!f) { if (!f) {
DEBUGFS_PRINTLN(F("Failed to open!")); DEBUGFS_PRINTLN(F("Failed to open!"));
return false; return false;
@ -340,7 +340,8 @@ bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest)
DEBUGFS_PRINTF("Read from %s with key %s >>>\n", file, (key==nullptr)?"nullptr":key); DEBUGFS_PRINTF("Read from %s with key %s >>>\n", file, (key==nullptr)?"nullptr":key);
uint32_t s = millis(); uint32_t s = millis();
#endif #endif
f = WLED_FS.open(file, "r"); char fileName[129]; strncpy_P(fileName, file, 128); fileName[128] = 0; //use PROGMEM safe copy as FS.open() does not
f = WLED_FS.open(fileName, "r");
if (!f) return false; if (!f) return false;
if (key != nullptr && !bufferedFind(key)) //key does not exist in file if (key != nullptr && !bufferedFind(key)) //key does not exist in file
@ -375,39 +376,15 @@ void updateFSInfo() {
} }
//Un-comment any file types you need #ifdef ARDUINO_ARCH_ESP32
static String getContentType(AsyncWebServerRequest* request, String filename){
if(request->hasArg(F("download"))) return SET_F("application/octet-stream");
else if(filename.endsWith(F(".htm"))) return SET_F("text/html");
else if(filename.endsWith(F(".html"))) return SET_F("text/html");
else if(filename.endsWith(F(".css"))) return SET_F("text/css");
else if(filename.endsWith(F(".js"))) return SET_F("application/javascript");
else if(filename.endsWith(F(".json"))) return SET_F("application/json");
else if(filename.endsWith(F(".png"))) return SET_F("image/png");
else if(filename.endsWith(F(".gif"))) return SET_F("image/gif");
else if(filename.endsWith(F(".jpg"))) return SET_F("image/jpeg");
else if(filename.endsWith(F(".ico"))) return SET_F("image/x-icon");
// else if(filename.endsWith(F(".xml"))) return SET_F("text/xml");
// else if(filename.endsWith(F(".pdf"))) return SET_F("application/x-pdf");
// else if(filename.endsWith(F(".zip"))) return SET_F("application/x-zip");
// else if(filename.endsWith(F(".gz"))) return SET_F("application/x-gzip");
return "text/plain";
}
#if defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM)
// caching presets in PSRAM may prevent occasional flashes seen when HomeAssitant polls WLED // caching presets in PSRAM may prevent occasional flashes seen when HomeAssitant polls WLED
// original idea by @akaricchi (https://github.com/Akaricchi) // original idea by @akaricchi (https://github.com/Akaricchi)
// returns a pointer to the PSRAM buffer updates size parameter // returns a pointer to the PSRAM buffer, updates size parameter
static const uint8_t *getPresetCache(size_t &size) { static const uint8_t *getPresetCache(size_t &size) {
static unsigned long presetsCachedTime; static unsigned long presetsCachedTime;
static uint8_t *presetsCached; static uint8_t *presetsCached;
static size_t presetsCachedSize; static size_t presetsCachedSize;
if (!psramFound()) {
size = 0;
return nullptr;
}
if (presetsModifiedTime != presetsCachedTime) { if (presetsModifiedTime != presetsCachedTime) {
if (presetsCached) { if (presetsCached) {
free(presetsCached); free(presetsCached);
@ -416,7 +393,7 @@ static const uint8_t *getPresetCache(size_t &size) {
} }
if (!presetsCached) { if (!presetsCached) {
File file = WLED_FS.open("/presets.json", "r"); File file = WLED_FS.open(FPSTR(getPresetsFileName()), "r");
if (file) { if (file) {
presetsCachedTime = presetsModifiedTime; presetsCachedTime = presetsModifiedTime;
presetsCachedSize = 0; presetsCachedSize = 0;
@ -439,25 +416,19 @@ bool handleFileRead(AsyncWebServerRequest* request, String path){
DEBUG_PRINT(F("WS FileRead: ")); DEBUG_PRINTLN(path); DEBUG_PRINT(F("WS FileRead: ")); DEBUG_PRINTLN(path);
if(path.endsWith("/")) path += "index.htm"; if(path.endsWith("/")) path += "index.htm";
if(path.indexOf(F("sec")) > -1) return false; if(path.indexOf(F("sec")) > -1) return false;
String contentType = getContentType(request, path); #ifdef ARDUINO_ARCH_ESP32
/*String pathWithGz = path + ".gz"; if (psramSafe && psramFound() && path.endsWith(FPSTR(getPresetsFileName()))) {
if(WLED_FS.exists(pathWithGz)){
request->send(WLED_FS, pathWithGz, contentType);
return true;
}*/
#if defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM)
if (path.endsWith("/presets.json")) {
size_t psize; size_t psize;
const uint8_t *presets = getPresetCache(psize); const uint8_t *presets = getPresetCache(psize);
if (presets) { if (presets) {
AsyncWebServerResponse *response = request->beginResponse_P(200, contentType, presets, psize); AsyncWebServerResponse *response = request->beginResponse_P(200, FPSTR(CONTENT_TYPE_JSON), presets, psize);
request->send(response); request->send(response);
return true; return true;
} }
} }
#endif #endif
if(WLED_FS.exists(path)) { if(WLED_FS.exists(path) || WLED_FS.exists(path + ".gz")) {
request->send(WLED_FS, path, contentType); request->send(WLED_FS, path, String(), request->hasArg(F("download")));
return true; return true;
} }
return false; return false;

View File

@ -210,7 +210,7 @@ void sendImprovInfoResponse() {
//Use serverDescription if it has been changed from the default "WLED", else mDNS name //Use serverDescription if it has been changed from the default "WLED", else mDNS name
bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0); bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0);
char vString[20]; char vString[20];
sprintf_P(vString, PSTR("0.15.0-b1/%i"), VERSION); sprintf_P(vString, PSTR("0.15.0-b2/%i"), VERSION);
const char *str[4] = {"WLED", vString, bString, useMdnsName ? cmDNS : serverDescription}; const char *str[4] = {"WLED", vString, bString, useMdnsName ? cmDNS : serverDescription};
sendImprovRPCResult(ImprovRPCType::Request_Info, 4, str); sendImprovRPCResult(ImprovRPCType::Request_Info, 4, str);

View File

@ -631,6 +631,7 @@ Sample:
void decodeIRJson(uint32_t code) void decodeIRJson(uint32_t code)
{ {
char objKey[10]; char objKey[10];
char fileName[16];
String cmdStr; String cmdStr;
JsonObject fdo; JsonObject fdo;
JsonObject jsonCmdObj; JsonObject jsonCmdObj;
@ -638,17 +639,18 @@ void decodeIRJson(uint32_t code)
if (!requestJSONBufferLock(13)) return; if (!requestJSONBufferLock(13)) return;
sprintf_P(objKey, PSTR("\"0x%lX\":"), (unsigned long)code); sprintf_P(objKey, PSTR("\"0x%lX\":"), (unsigned long)code);
strcpy_P(fileName, PSTR("/ir.json")); // for FS.exists()
// attempt to read command from ir.json // attempt to read command from ir.json
// this may fail for two reasons: ir.json does not exist or IR code not found // this may fail for two reasons: ir.json does not exist or IR code not found
// if the IR code is not found readObjectFromFile() will clean() doc JSON document // if the IR code is not found readObjectFromFile() will clean() doc JSON document
// so we can differentiate between the two // so we can differentiate between the two
readObjectFromFile("/ir.json", objKey, pDoc); readObjectFromFile(fileName, objKey, pDoc);
fdo = pDoc->as<JsonObject>(); fdo = pDoc->as<JsonObject>();
lastValidCode = 0; lastValidCode = 0;
if (fdo.isNull()) { if (fdo.isNull()) {
//the received code does not exist //the received code does not exist
if (!WLED_FS.exists("/ir.json")) errorFlag = ERR_FS_IRLOAD; //warn if IR file itself doesn't exist if (!WLED_FS.exists(fileName)) errorFlag = ERR_FS_IRLOAD; //warn if IR file itself doesn't exist
releaseJSONBufferLock(); releaseJSONBufferLock();
return; return;
} }

View File

@ -142,28 +142,42 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
{ {
if (seg.getLightCapabilities() & 3) { if (seg.getLightCapabilities() & 3) {
// segment has RGB or White // segment has RGB or White
for (size_t i = 0; i < 3; i++) for (size_t i = 0; i < NUM_COLORS; i++) {
{ // JSON "col" array can contain the following values for each of segment's colors (primary, background, custom):
// "col":[int|string|object|array, int|string|object|array, int|string|object|array]
// int = Kelvin temperature or 0 for black
// string = hex representation of [WW]RRGGBB
// object = individual channel control {"r":0,"g":127,"b":255,"w":255}, each being optional (valid to send {})
// array = direct channel values [r,g,b,w] (w element being optional)
int rgbw[] = {0,0,0,0}; int rgbw[] = {0,0,0,0};
bool colValid = false; bool colValid = false;
JsonArray colX = colarr[i]; JsonArray colX = colarr[i];
if (colX.isNull()) { if (colX.isNull()) {
byte brgbw[] = {0,0,0,0}; JsonObject oCol = colarr[i];
const char* hexCol = colarr[i]; if (!oCol.isNull()) {
if (hexCol == nullptr) { //Kelvin color temperature (or invalid), e.g 2400 // we have a JSON object for color {"w":123,"r":123,...}; allows individual channel control
int kelvin = colarr[i] | -1; rgbw[0] = oCol["r"] | R(seg.colors[i]);
if (kelvin < 0) continue; rgbw[1] = oCol["g"] | G(seg.colors[i]);
if (kelvin == 0) seg.setColor(i, 0); rgbw[2] = oCol["b"] | B(seg.colors[i]);
if (kelvin > 0) colorKtoRGB(kelvin, brgbw); rgbw[3] = oCol["w"] | W(seg.colors[i]);
colValid = true; colValid = true;
} else { //HEX string, e.g. "FFAA00" } else {
colValid = colorFromHexString(brgbw, hexCol); byte brgbw[] = {0,0,0,0};
const char* hexCol = colarr[i];
if (hexCol == nullptr) { //Kelvin color temperature (or invalid), e.g 2400
int kelvin = colarr[i] | -1;
if (kelvin < 0) continue;
if (kelvin == 0) seg.setColor(i, 0);
if (kelvin > 0) colorKtoRGB(kelvin, brgbw);
colValid = true;
} else { //HEX string, e.g. "FFAA00"
colValid = colorFromHexString(brgbw, hexCol);
}
for (size_t c = 0; c < 4; c++) rgbw[c] = brgbw[c];
} }
for (size_t c = 0; c < 4; c++) rgbw[c] = brgbw[c];
} else { //Array of ints (RGB or RGBW color), e.g. [255,160,0] } else { //Array of ints (RGB or RGBW color), e.g. [255,160,0]
byte sz = colX.size(); byte sz = colX.size();
if (sz == 0) continue; //do nothing on empty array if (sz == 0) continue; //do nothing on empty array
copyArray(colX, rgbw, 4); copyArray(colX, rgbw, 4);
colValid = true; colValid = true;
} }
@ -226,14 +240,19 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
getVal(elem["ix"], &seg.intensity); getVal(elem["ix"], &seg.intensity);
uint8_t pal = seg.palette; uint8_t pal = seg.palette;
last = strip.getPaletteCount();
if (!elem["pal"].isNull() && elem["pal"].is<const char*>()) {
const char *tmp = elem["pal"].as<const char *>();
if (strlen(tmp) > 3 && (strchr(tmp,'r') || strchr(tmp,'~') != strrchr(tmp,'~'))) last = 0; // we have "X~Y(r|[w]~[-])" form
}
if (seg.getLightCapabilities() & 1) { // ignore palette for White and On/Off segments if (seg.getLightCapabilities() & 1) { // ignore palette for White and On/Off segments
if (getVal(elem["pal"], &pal)) seg.setPalette(pal); if (getVal(elem["pal"], &pal, 0, last)) seg.setPalette(pal);
} }
getVal(elem["c1"], &seg.custom1); getVal(elem["c1"], &seg.custom1);
getVal(elem["c2"], &seg.custom2); getVal(elem["c2"], &seg.custom2);
uint8_t cust3 = seg.custom3; uint8_t cust3 = seg.custom3;
getVal(elem["c3"], &cust3); // we can't pass reference to bitfield getVal(elem["c3"], &cust3, 0, 31); // we can't pass reference to bitfield
seg.custom3 = constrain(cust3, 0, 31); seg.custom3 = constrain(cust3, 0, 31);
seg.check1 = getBoolVal(elem["o1"], seg.check1); seg.check1 = getBoolVal(elem["o1"], seg.check1);
@ -298,7 +317,7 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
return true; return true;
} }
// deserializes WLED state (fileDoc points to doc object if called from web server) // deserializes WLED state
// presetId is non-0 if called from handlePreset() // presetId is non-0 if called from handlePreset()
bool deserializeState(JsonObject root, byte callMode, byte presetId) bool deserializeState(JsonObject root, byte callMode, byte presetId)
{ {
@ -442,13 +461,11 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
currentPreset = root[F("pd")] | currentPreset; currentPreset = root[F("pd")] | currentPreset;
if (root["win"].isNull()) presetCycCurr = currentPreset; // otherwise it was set in handleSet() [set.cpp] if (root["win"].isNull()) presetCycCurr = currentPreset; // otherwise it was set in handleSet() [set.cpp]
presetToRestore = currentPreset; // stateUpdated() will clear the preset, so we need to restore it after presetToRestore = currentPreset; // stateUpdated() will clear the preset, so we need to restore it after
//unloadPlaylist(); // applying a preset unloads the playlist, may be needed here too?
} else if (!root["ps"].isNull()) { } else if (!root["ps"].isNull()) {
ps = presetCycCurr; ps = presetCycCurr;
if (root["win"].isNull() && getVal(root["ps"], &ps, 0, 0) && ps > 0 && ps < 251 && ps != currentPreset) { if (root["win"].isNull() && getVal(root["ps"], &ps, 0, 0) && ps > 0 && ps < 251 && ps != currentPreset) {
// b) preset ID only or preset that does not change state (use embedded cycling limits if they exist in getVal()) // b) preset ID only or preset that does not change state (use embedded cycling limits if they exist in getVal())
presetCycCurr = ps; presetCycCurr = ps;
unloadPlaylist(); // applying a preset unloads the playlist
applyPreset(ps, callMode); // async load from file system (only preset ID was specified) applyPreset(ps, callMode); // async load from file system (only preset ID was specified)
return stateResponse; return stateResponse;
} }
@ -628,6 +645,7 @@ void serializeInfo(JsonObject root)
leds[F("maxseg")] = strip.getMaxSegments(); leds[F("maxseg")] = strip.getMaxSegments();
//leds[F("actseg")] = strip.getActiveSegmentsNum(); //leds[F("actseg")] = strip.getActiveSegmentsNum();
//leds[F("seglock")] = false; //might be used in the future to prevent modifications to segment config //leds[F("seglock")] = false; //might be used in the future to prevent modifications to segment config
leds[F("bootps")] = bootPreset;
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
if (strip.isMatrix) { if (strip.isMatrix) {
@ -731,6 +749,8 @@ void serializeInfo(JsonObject root)
root[F("arch")] = ESP.getChipModel(); root[F("arch")] = ESP.getChipModel();
#endif #endif
root[F("core")] = ESP.getSdkVersion(); root[F("core")] = ESP.getSdkVersion();
root[F("clock")] = ESP.getCpuFreqMHz();
root[F("flash")] = (ESP.getFlashChipSize()/1024)/1024;
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
root[F("maxalloc")] = ESP.getMaxAllocHeap(); root[F("maxalloc")] = ESP.getMaxAllocHeap();
root[F("resetReason0")] = (int)rtc_get_reset_reason(0); root[F("resetReason0")] = (int)rtc_get_reset_reason(0);
@ -740,6 +760,8 @@ void serializeInfo(JsonObject root)
#else #else
root[F("arch")] = "esp8266"; root[F("arch")] = "esp8266";
root[F("core")] = ESP.getCoreVersion(); root[F("core")] = ESP.getCoreVersion();
root[F("clock")] = ESP.getCpuFreqMHz();
root[F("flash")] = (ESP.getFlashChipSize()/1024)/1024;
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
root[F("maxalloc")] = ESP.getMaxFreeBlockSize(); root[F("maxalloc")] = ESP.getMaxFreeBlockSize();
root[F("resetReason")] = (int)ESP.getResetInfoPtr()->reason; root[F("resetReason")] = (int)ESP.getResetInfoPtr()->reason;
@ -748,8 +770,8 @@ void serializeInfo(JsonObject root)
#endif #endif
root[F("freeheap")] = ESP.getFreeHeap(); root[F("freeheap")] = ESP.getFreeHeap();
#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) #if defined(ARDUINO_ARCH_ESP32)
if (psramFound()) root[F("psram")] = ESP.getFreePsram(); if (psramSafe && psramFound()) root[F("psram")] = ESP.getFreePsram();
#endif #endif
root[F("uptime")] = millis()/1000 + rolloverMillis*4294967; root[F("uptime")] = millis()/1000 + rolloverMillis*4294967;
@ -851,8 +873,8 @@ void serializePalettes(JsonObject root, int page)
int itemPerPage = 8; int itemPerPage = 8;
#endif #endif
int palettesCount = strip.getPaletteCount();
int customPalettes = strip.customPalettes.size(); int customPalettes = strip.customPalettes.size();
int palettesCount = strip.getPaletteCount() - customPalettes;
int maxPage = (palettesCount + customPalettes -1) / itemPerPage; int maxPage = (palettesCount + customPalettes -1) / itemPerPage;
if (page > maxPage) page = maxPage; if (page > maxPage) page = maxPage;
@ -974,7 +996,7 @@ void serializeNetworks(JsonObject root)
void serializeNodes(JsonObject root) void serializeNodes(JsonObject root)
{ {
JsonArray nodes = root.createNestedArray(F("nodes")); JsonArray nodes = root.createNestedArray("nodes");
for (NodesMap::iterator it = Nodes.begin(); it != Nodes.end(); ++it) for (NodesMap::iterator it = Nodes.begin(); it != Nodes.end(); ++it)
{ {
@ -1023,39 +1045,51 @@ void serializeModeNames(JsonArray arr)
// Global buffer locking response helper class (to make sure lock is released when AsyncJsonResponse is destroyed) // Global buffer locking response helper class (to make sure lock is released when AsyncJsonResponse is destroyed)
class LockedJsonResponse: public AsyncJsonResponse { class LockedJsonResponse: public AsyncJsonResponse {
bool _holding_lock;
public: public:
// WARNING: constructor assumes requestJSONBufferLock() was successfully acquired externally/prior to constructing the instance // WARNING: constructor assumes requestJSONBufferLock() was successfully acquired externally/prior to constructing the instance
// Not a good practice with C++. Unfortunately AsyncJsonResponse only has 2 constructors - for dynamic buffer or existing buffer, // Not a good practice with C++. Unfortunately AsyncJsonResponse only has 2 constructors - for dynamic buffer or existing buffer,
// with existing buffer it clears its content during construction // with existing buffer it clears its content during construction
// if the lock was not acquired (using JSONBufferGuard class) previous implementation still cleared existing buffer // if the lock was not acquired (using JSONBufferGuard class) previous implementation still cleared existing buffer
inline LockedJsonResponse(JsonDocument *doc, bool isArray) : AsyncJsonResponse(doc, isArray) {}; inline LockedJsonResponse(JsonDocument* doc, bool isArray) : AsyncJsonResponse(doc, isArray), _holding_lock(true) {};
virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) {
size_t result = AsyncJsonResponse::_fillBuffer(buf, maxLen);
// Release lock as soon as we're done filling content
if (((result + _sentLength) >= (_contentLength)) && _holding_lock) {
releaseJSONBufferLock();
_holding_lock = false;
}
return result;
}
// destructor will remove JSON buffer lock when response is destroyed in AsyncWebServer // destructor will remove JSON buffer lock when response is destroyed in AsyncWebServer
virtual ~LockedJsonResponse() { releaseJSONBufferLock(); }; virtual ~LockedJsonResponse() { if (_holding_lock) releaseJSONBufferLock(); };
}; };
void serveJson(AsyncWebServerRequest* request) void serveJson(AsyncWebServerRequest* request)
{ {
byte subJson = 0; byte subJson = 0;
const String& url = request->url(); const String& url = request->url();
if (url.indexOf("state") > 0) subJson = JSON_PATH_STATE; if (url.indexOf("state") > 0) subJson = JSON_PATH_STATE;
else if (url.indexOf("info") > 0) subJson = JSON_PATH_INFO; 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("si") > 0) subJson = JSON_PATH_STATE_INFO;
else if (url.indexOf("nodes") > 0) subJson = JSON_PATH_NODES; else if (url.indexOf(F("nodes")) > 0) subJson = JSON_PATH_NODES;
else if (url.indexOf("eff") > 0) subJson = JSON_PATH_EFFECTS; else if (url.indexOf(F("eff")) > 0) subJson = JSON_PATH_EFFECTS;
else if (url.indexOf("palx") > 0) subJson = JSON_PATH_PALETTES; else if (url.indexOf(F("palx")) > 0) subJson = JSON_PATH_PALETTES;
else if (url.indexOf("fxda") > 0) subJson = JSON_PATH_FXDATA; else if (url.indexOf(F("fxda")) > 0) subJson = JSON_PATH_FXDATA;
else if (url.indexOf("net") > 0) subJson = JSON_PATH_NETWORKS; else if (url.indexOf(F("net")) > 0) subJson = JSON_PATH_NETWORKS;
#ifdef WLED_ENABLE_JSONLIVE #ifdef WLED_ENABLE_JSONLIVE
else if (url.indexOf("live") > 0) { else if (url.indexOf("live") > 0) {
serveLiveLeds(request); serveLiveLeds(request);
return; return;
} }
#endif #endif
else if (url.indexOf("pal") > 0) { else if (url.indexOf("pal") > 0) {
request->send_P(200, "application/json", JSON_palette_names); request->send_P(200, FPSTR(CONTENT_TYPE_JSON), JSON_palette_names);
return; return;
} }
else if (url.indexOf("cfg") > 0 && handleFileRead(request, "/cfg.json")) { else if (url.indexOf(F("cfg")) > 0 && handleFileRead(request, F("/cfg.json"))) {
return; return;
} }
else if (url.length() > 6) { //not just /json else if (url.length() > 6) { //not just /json
@ -1082,7 +1116,7 @@ void serveJson(AsyncWebServerRequest* request)
case JSON_PATH_NODES: case JSON_PATH_NODES:
serializeNodes(lDoc); break; serializeNodes(lDoc); break;
case JSON_PATH_PALETTES: case JSON_PATH_PALETTES:
serializePalettes(lDoc, request->hasParam("page") ? request->getParam("page")->value().toInt() : 0); break; serializePalettes(lDoc, request->hasParam(F("page")) ? request->getParam(F("page"))->value().toInt() : 0); break;
case JSON_PATH_EFFECTS: case JSON_PATH_EFFECTS:
serializeModeNames(lDoc); break; serializeModeNames(lDoc); break;
case JSON_PATH_FXDATA: case JSON_PATH_FXDATA:
@ -1139,10 +1173,10 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
} }
#endif #endif
char buffer[2048]; // shoud be enough for 256 LEDs [RRGGBB] + all other text (9+25) DynamicBuffer buffer(9 + (9*(1+(used/n))) + 7 + 5 + 6 + 5 + 6 + 5 + 2);
strcpy_P(buffer, PSTR("{\"leds\":[")); char* buf = buffer.data(); // assign buffer for oappnd() functions
obuf = buffer; // assign buffer for oappnd() functions strncpy_P(buffer.data(), PSTR("{\"leds\":["), buffer.size());
olen = 9; buf += 9; // sizeof(PSTR()) from last line
for (size_t i = 0; i < used; i += n) for (size_t i = 0; i < used; i += n)
{ {
@ -1157,29 +1191,27 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
r = scale8(qadd8(w, r), strip.getBrightness()); //R, add white channel to RGB channels as a simple RGBW -> RGB map r = scale8(qadd8(w, r), strip.getBrightness()); //R, add white channel to RGB channels as a simple RGBW -> RGB map
g = scale8(qadd8(w, g), strip.getBrightness()); //G g = scale8(qadd8(w, g), strip.getBrightness()); //G
b = scale8(qadd8(w, b), strip.getBrightness()); //B b = scale8(qadd8(w, b), strip.getBrightness()); //B
olen += sprintf_P(obuf + olen, PSTR("\"%06X\","), RGBW32(r,g,b,0)); buf += sprintf_P(buf, PSTR("\"%06X\","), RGBW32(r,g,b,0));
} }
olen -= 1; buf--; // remove last comma
oappend((const char*)F("],\"n\":")); buf += sprintf_P(buf, PSTR("],\"n\":%d"), n);
oappendi(n);
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
if (strip.isMatrix) { if (strip.isMatrix) {
oappend((const char*)F(",\"w\":")); buf += sprintf_P(buf, PSTR(",\"w\":%d"), Segment::maxWidth/n);
oappendi(Segment::maxWidth/n); buf += sprintf_P(buf, PSTR(",\"h\":%d"), Segment::maxHeight/n);
oappend((const char*)F(",\"h\":"));
oappendi(Segment::maxHeight/n);
} }
#endif #endif
oappend("}"); (*buf++) = '}';
(*buf++) = 0;
if (request) { if (request) {
request->send(200, "application/json", buffer); request->send(200, FPSTR(CONTENT_TYPE_JSON), toString(std::move(buffer)));
} }
#ifdef WLED_ENABLE_WEBSOCKETS #ifdef WLED_ENABLE_WEBSOCKETS
else { else {
wsc->text(obuf, olen); wsc->text(toString(std::move(buffer)));
} }
#endif #endif
obuf = nullptr;
return true; return true;
} }
#endif #endif

View File

@ -172,7 +172,9 @@ void updateInterfaces(uint8_t callMode)
espalexaDevice->setColor(col[0], col[1], col[2]); espalexaDevice->setColor(col[0], col[1], col[2]);
} }
#endif #endif
doPublishMqtt = true; #ifndef WLED_DISABLE_MQTT
publishMqtt();
#endif
} }
@ -180,9 +182,6 @@ void handleTransitions()
{ {
//handle still pending interface update //handle still pending interface update
updateInterfaces(interfaceUpdateCallMode); updateInterfaces(interfaceUpdateCallMode);
#ifndef WLED_DISABLE_MQTT
if (doPublishMqtt) publishMqtt();
#endif
if (transitionActive && strip.getTransition() > 0) { if (transitionActive && strip.getTransition() > 0) {
float tper = (millis() - transitionStartTime)/(float)strip.getTransition(); float tper = (millis() - transitionStartTime)/(float)strip.getTransition();

View File

@ -47,8 +47,8 @@ void onMqttConnect(bool sessionPresent)
usermods.onMqttConnect(sessionPresent); usermods.onMqttConnect(sessionPresent);
doPublishMqtt = true;
DEBUG_PRINTLN(F("MQTT ready")); DEBUG_PRINTLN(F("MQTT ready"));
publishMqtt();
} }
@ -76,7 +76,7 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties
if (index + len >= total) { // at end if (index + len >= total) { // at end
payloadStr[total] = '\0'; // terminate c style string payloadStr[total] = '\0'; // terminate c style string
} else { } else {
DEBUG_PRINTLN(F("Partial packet received.")); DEBUG_PRINTLN(F("MQTT partial packet received."));
return; // process next packet return; // process next packet
} }
DEBUG_PRINTLN(payloadStr); DEBUG_PRINTLN(payloadStr);
@ -131,13 +131,12 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties
void publishMqtt() void publishMqtt()
{ {
doPublishMqtt = false;
if (!WLED_MQTT_CONNECTED) return; if (!WLED_MQTT_CONNECTED) return;
DEBUG_PRINTLN(F("Publish MQTT")); DEBUG_PRINTLN(F("Publish MQTT"));
#ifndef USERMOD_SMARTNEST #ifndef USERMOD_SMARTNEST
char s[10]; char s[10];
char subuf[38]; char subuf[48];
sprintf_P(s, PSTR("%u"), bri); sprintf_P(s, PSTR("%u"), bri);
strlcpy(subuf, mqttDeviceTopic, 33); strlcpy(subuf, mqttDeviceTopic, 33);

View File

@ -48,129 +48,118 @@ Timezone* tz;
#define TZ_ANCHORAGE 20 #define TZ_ANCHORAGE 20
#define TZ_MX_CENTRAL 21 #define TZ_MX_CENTRAL 21
#define TZ_PAKISTAN 22 #define TZ_PAKISTAN 22
#define TZ_COUNT 23
#define TZ_INIT 255 #define TZ_INIT 255
byte tzCurrent = TZ_INIT; //uninitialized byte tzCurrent = TZ_INIT; //uninitialized
/* C++11 form -- static std::array<std::pair<TimeChangeRule, TimeChangeRule>, TZ_COUNT> TZ_TABLE PROGMEM = {{ */
static const std::pair<TimeChangeRule, TimeChangeRule> TZ_TABLE[] PROGMEM = {
/* TZ_UTC */ {
{Last, Sun, Mar, 1, 0}, // UTC
{Last, Sun, Mar, 1, 0} // Same
},
/* TZ_UK */ {
{Last, Sun, Mar, 1, 60}, //British Summer Time
{Last, Sun, Oct, 2, 0} //Standard Time
},
/* TZ_EUROPE_CENTRAL */ {
{Last, Sun, Mar, 2, 120}, //Central European Summer Time
{Last, Sun, Oct, 3, 60} //Central European Standard Time
},
/* TZ_EUROPE_EASTERN */ {
{Last, Sun, Mar, 3, 180}, //East European Summer Time
{Last, Sun, Oct, 4, 120} //East European Standard Time
},
/* TZ_US_EASTERN */ {
{Second, Sun, Mar, 2, -240}, //EDT = UTC - 4 hours
{First, Sun, Nov, 2, -300} //EST = UTC - 5 hours
},
/* TZ_US_CENTRAL */ {
{Second, Sun, Mar, 2, -300}, //CDT = UTC - 5 hours
{First, Sun, Nov, 2, -360} //CST = UTC - 6 hours
},
/* TZ_US_MOUNTAIN */ {
{Second, Sun, Mar, 2, -360}, //MDT = UTC - 6 hours
{First, Sun, Nov, 2, -420} //MST = UTC - 7 hours
},
/* TZ_US_ARIZONA */ {
{First, Sun, Nov, 2, -420}, //MST = UTC - 7 hours
{First, Sun, Nov, 2, -420} //MST = UTC - 7 hours
},
/* TZ_US_PACIFIC */ {
{Second, Sun, Mar, 2, -420}, //PDT = UTC - 7 hours
{First, Sun, Nov, 2, -480} //PST = UTC - 8 hours
},
/* TZ_CHINA */ {
{Last, Sun, Mar, 1, 480}, //CST = UTC + 8 hours
{Last, Sun, Mar, 1, 480}
},
/* TZ_JAPAN */ {
{Last, Sun, Mar, 1, 540}, //JST = UTC + 9 hours
{Last, Sun, Mar, 1, 540}
},
/* TZ_AUSTRALIA_EASTERN */ {
{First, Sun, Oct, 2, 660}, //AEDT = UTC + 11 hours
{First, Sun, Apr, 3, 600} //AEST = UTC + 10 hours
},
/* TZ_NEW_ZEALAND */ {
{Last, Sun, Sep, 2, 780}, //NZDT = UTC + 13 hours
{First, Sun, Apr, 3, 720} //NZST = UTC + 12 hours
},
/* TZ_NORTH_KOREA */ {
{Last, Sun, Mar, 1, 510}, //Pyongyang Time = UTC + 8.5 hours
{Last, Sun, Mar, 1, 510}
},
/* TZ_INDIA */ {
{Last, Sun, Mar, 1, 330}, //India Standard Time = UTC + 5.5 hours
{Last, Sun, Mar, 1, 330}
},
/* TZ_SASKACHEWAN */ {
{First, Sun, Nov, 2, -360}, //CST = UTC - 6 hours
{First, Sun, Nov, 2, -360}
},
/* TZ_AUSTRALIA_NORTHERN */ {
{First, Sun, Apr, 3, 570}, //ACST = UTC + 9.5 hours
{First, Sun, Apr, 3, 570}
},
/* TZ_AUSTRALIA_SOUTHERN */ {
{First, Sun, Oct, 2, 630}, //ACDT = UTC + 10.5 hours
{First, Sun, Apr, 3, 570} //ACST = UTC + 9.5 hours
},
/* TZ_HAWAII */ {
{Last, Sun, Mar, 1, -600}, //HST = UTC - 10 hours
{Last, Sun, Mar, 1, -600}
},
/* TZ_NOVOSIBIRSK */ {
{Last, Sun, Mar, 1, 420}, //CST = UTC + 7 hours
{Last, Sun, Mar, 1, 420}
},
/* TZ_ANCHORAGE */ {
{Second, Sun, Mar, 2, -480}, //AKDT = UTC - 8 hours
{First, Sun, Nov, 2, -540} //AKST = UTC - 9 hours
},
/* TZ_MX_CENTRAL */ {
{First, Sun, Apr, 2, -360}, //CST = UTC - 6 hours
{First, Sun, Apr, 2, -360}
},
/* TZ_PAKISTAN */ {
{Last, Sun, Mar, 1, 300}, //Pakistan Standard Time = UTC + 5 hours
{Last, Sun, Mar, 1, 300}
}
};
void updateTimezone() { void updateTimezone() {
delete tz; delete tz;
TimeChangeRule tcrDaylight = {Last, Sun, Mar, 1, 0}; //UTC TimeChangeRule tcrDaylight, tcrStandard;
TimeChangeRule tcrStandard = tcrDaylight; //UTC auto tz_table_entry = currentTimezone;
if (tz_table_entry >= TZ_COUNT) {
switch (currentTimezone) { tz_table_entry = 0;
case TZ_UK : {
tcrDaylight = {Last, Sun, Mar, 1, 60}; //British Summer Time
tcrStandard = {Last, Sun, Oct, 2, 0}; //Standard Time
break;
}
case TZ_EUROPE_CENTRAL : {
tcrDaylight = {Last, Sun, Mar, 2, 120}; //Central European Summer Time
tcrStandard = {Last, Sun, Oct, 3, 60}; //Central European Standard Time
break;
}
case TZ_EUROPE_EASTERN : {
tcrDaylight = {Last, Sun, Mar, 3, 180}; //East European Summer Time
tcrStandard = {Last, Sun, Oct, 4, 120}; //East European Standard Time
break;
}
case TZ_US_EASTERN : {
tcrDaylight = {Second, Sun, Mar, 2, -240}; //EDT = UTC - 4 hours
tcrStandard = {First, Sun, Nov, 2, -300}; //EST = UTC - 5 hours
break;
}
case TZ_US_CENTRAL : {
tcrDaylight = {Second, Sun, Mar, 2, -300}; //CDT = UTC - 5 hours
tcrStandard = {First, Sun, Nov, 2, -360}; //CST = UTC - 6 hours
break;
}
case TZ_US_MOUNTAIN : {
tcrDaylight = {Second, Sun, Mar, 2, -360}; //MDT = UTC - 6 hours
tcrStandard = {First, Sun, Nov, 2, -420}; //MST = UTC - 7 hours
break;
}
case TZ_US_ARIZONA : {
tcrDaylight = {First, Sun, Nov, 2, -420}; //MST = UTC - 7 hours
tcrStandard = {First, Sun, Nov, 2, -420}; //MST = UTC - 7 hours
break;
}
case TZ_US_PACIFIC : {
tcrDaylight = {Second, Sun, Mar, 2, -420}; //PDT = UTC - 7 hours
tcrStandard = {First, Sun, Nov, 2, -480}; //PST = UTC - 8 hours
break;
}
case TZ_CHINA : {
tcrDaylight = {Last, Sun, Mar, 1, 480}; //CST = UTC + 8 hours
tcrStandard = tcrDaylight;
break;
}
case TZ_JAPAN : {
tcrDaylight = {Last, Sun, Mar, 1, 540}; //JST = UTC + 9 hours
tcrStandard = tcrDaylight;
break;
}
case TZ_AUSTRALIA_EASTERN : {
tcrDaylight = {First, Sun, Oct, 2, 660}; //AEDT = UTC + 11 hours
tcrStandard = {First, Sun, Apr, 3, 600}; //AEST = UTC + 10 hours
break;
}
case TZ_NEW_ZEALAND : {
tcrDaylight = {Last, Sun, Sep, 2, 780}; //NZDT = UTC + 13 hours
tcrStandard = {First, Sun, Apr, 3, 720}; //NZST = UTC + 12 hours
break;
}
case TZ_NORTH_KOREA : {
tcrDaylight = {Last, Sun, Mar, 1, 510}; //Pyongyang Time = UTC + 8.5 hours
tcrStandard = tcrDaylight;
break;
}
case TZ_INDIA : {
tcrDaylight = {Last, Sun, Mar, 1, 330}; //India Standard Time = UTC + 5.5 hours
tcrStandard = tcrDaylight;
break;
}
case TZ_SASKACHEWAN : {
tcrDaylight = {First, Sun, Nov, 2, -360}; //CST = UTC - 6 hours
tcrStandard = tcrDaylight;
break;
}
case TZ_AUSTRALIA_NORTHERN : {
tcrDaylight = {First, Sun, Apr, 3, 570}; //ACST = UTC + 9.5 hours
tcrStandard = tcrDaylight;
break;
}
case TZ_AUSTRALIA_SOUTHERN : {
tcrDaylight = {First, Sun, Oct, 2, 630}; //ACDT = UTC + 10.5 hours
tcrStandard = {First, Sun, Apr, 3, 570}; //ACST = UTC + 9.5 hours
break;
}
case TZ_HAWAII : {
tcrDaylight = {Last, Sun, Mar, 1, -600}; //HST = UTC - 10 hours
tcrStandard = tcrDaylight;
break;
}
case TZ_NOVOSIBIRSK : {
tcrDaylight = {Last, Sun, Mar, 1, 420}; //CST = UTC + 7 hours
tcrStandard = tcrDaylight;
break;
}
case TZ_ANCHORAGE : {
tcrDaylight = {Second, Sun, Mar, 2, -480}; //AKDT = UTC - 8 hours
tcrStandard = {First, Sun, Nov, 2, -540}; //AKST = UTC - 9 hours
break;
}
case TZ_MX_CENTRAL : {
tcrDaylight = {First, Sun, Apr, 2, -360}; //CST = UTC - 6 hours
tcrStandard = tcrDaylight;
break;
}
case TZ_PAKISTAN : {
tcrDaylight = {Last, Sun, Mar, 1, 300}; //Pakistan Standard Time = UTC + 5 hours
tcrStandard = tcrDaylight;
break;
}
} }
tzCurrent = currentTimezone; tzCurrent = currentTimezone;
memcpy_P(&tcrDaylight, &TZ_TABLE[tz_table_entry].first, sizeof(tcrDaylight));
memcpy_P(&tcrStandard, &TZ_TABLE[tz_table_entry].second, sizeof(tcrStandard));
tz = new Timezone(tcrDaylight, tcrStandard); tz = new Timezone(tcrDaylight, tcrStandard);
} }
@ -328,7 +317,8 @@ void getTimeString(char* out)
sprintf_P(out,PSTR("%i-%i-%i, %02d:%02d:%02d"),year(localTime), month(localTime), day(localTime), hr, minute(localTime), second(localTime)); sprintf_P(out,PSTR("%i-%i-%i, %02d:%02d:%02d"),year(localTime), month(localTime), day(localTime), hr, minute(localTime), second(localTime));
if (useAMPM) if (useAMPM)
{ {
strcat(out,(hour(localTime) > 11)? " PM":" AM"); strcat_P(out,PSTR(" "));
strcat(out,(hour(localTime) > 11)? "PM":"AM");
} }
} }
@ -409,7 +399,6 @@ void checkTimers()
&& isTodayInDateRange(((timerMonth[i] >> 4) & 0x0F), timerDay[i], timerMonth[i] & 0x0F, timerDayEnd[i]) && isTodayInDateRange(((timerMonth[i] >> 4) & 0x0F), timerDay[i], timerMonth[i] & 0x0F, timerDayEnd[i])
) )
{ {
unloadPlaylist();
applyPreset(timerMacro[i]); applyPreset(timerMacro[i]);
} }
} }
@ -423,7 +412,6 @@ void checkTimers()
&& (timerWeekday[8] & 0x01) //timer is enabled && (timerWeekday[8] & 0x01) //timer is enabled
&& ((timerWeekday[8] >> weekdayMondayFirst()) & 0x01)) //timer should activate at current day of week && ((timerWeekday[8] >> weekdayMondayFirst()) & 0x01)) //timer should activate at current day of week
{ {
unloadPlaylist();
applyPreset(timerMacro[8]); applyPreset(timerMacro[8]);
DEBUG_PRINTF_P(PSTR("Sunrise macro %d triggered."),timerMacro[8]); DEBUG_PRINTF_P(PSTR("Sunrise macro %d triggered."),timerMacro[8]);
} }
@ -438,7 +426,6 @@ void checkTimers()
&& (timerWeekday[9] & 0x01) //timer is enabled && (timerWeekday[9] & 0x01) //timer is enabled
&& ((timerWeekday[9] >> weekdayMondayFirst()) & 0x01)) //timer should activate at current day of week && ((timerWeekday[9] >> weekdayMondayFirst()) & 0x01)) //timer should activate at current day of week
{ {
unloadPlaylist();
applyPreset(timerMacro[9]); applyPreset(timerMacro[9]);
DEBUG_PRINTF_P(PSTR("Sunset macro %d triggered."),timerMacro[9]); DEBUG_PRINTF_P(PSTR("Sunset macro %d triggered."),timerMacro[9]);
} }

View File

@ -109,7 +109,7 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
DEBUG_PRINT(F("PIN ALLOC: Invalid pin attempted to be allocated: GPIO ")); DEBUG_PRINT(F("PIN ALLOC: Invalid pin attempted to be allocated: GPIO "));
DEBUG_PRINT(gpio); DEBUG_PRINT(gpio);
DEBUG_PRINT(F(" as ")); DEBUG_PRINT(mptArray[i].isOutput ? "output": "input"); DEBUG_PRINT(F(" as ")); DEBUG_PRINT(mptArray[i].isOutput ? F("output"): F("input"));
DEBUG_PRINTLN(F("")); DEBUG_PRINTLN(F(""));
#endif #endif
shouldFail = true; shouldFail = true;
@ -209,11 +209,10 @@ bool PinManagerClass::allocatePin(byte gpio, bool output, PinOwner tag)
// if tag is set to PinOwner::None, checks for ANY owner of the pin. // if tag is set to PinOwner::None, checks for ANY owner of the pin.
// if tag is set to any other value, checks if that tag is the current owner of the pin. // if tag is set to any other value, checks if that tag is the current owner of the pin.
bool PinManagerClass::isPinAllocated(byte gpio, PinOwner tag) bool PinManagerClass::isPinAllocated(byte gpio, PinOwner tag) const
{ {
if (!isPinOk(gpio, false)) return true; if (!isPinOk(gpio, false)) return true;
if ((tag != PinOwner::None) && (ownerTag[gpio] != tag)) return false; if ((tag != PinOwner::None) && (ownerTag[gpio] != tag)) return false;
if (gpio >= WLED_NUM_PINS) return false; // catch error case, to avoid array out-of-bounds access
byte by = gpio >> 3; byte by = gpio >> 3;
byte bi = gpio - (by<<3); byte bi = gpio - (by<<3);
return bitRead(pinAlloc[by], bi); return bitRead(pinAlloc[by], bi);
@ -236,8 +235,9 @@ bool PinManagerClass::isPinAllocated(byte gpio, PinOwner tag)
*/ */
// Check if supplied GPIO is ok to use // Check if supplied GPIO is ok to use
bool PinManagerClass::isPinOk(byte gpio, bool output) bool PinManagerClass::isPinOk(byte gpio, bool output) const
{ {
if (gpio >= WLED_NUM_PINS) return false; // catch error case, to avoid array out-of-bounds access
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
if (digitalPinIsValid(gpio)) { if (digitalPinIsValid(gpio)) {
#if defined(CONFIG_IDF_TARGET_ESP32C3) #if defined(CONFIG_IDF_TARGET_ESP32C3)
@ -248,7 +248,7 @@ bool PinManagerClass::isPinOk(byte gpio, bool output)
// 00 to 18 are for general use. Be careful about straping pins GPIO0 and GPIO3 - these may be pulled-up or pulled-down on your board. // 00 to 18 are for general use. Be careful about straping pins GPIO0 and GPIO3 - these may be pulled-up or pulled-down on your board.
if (gpio > 18 && gpio < 21) return false; // 19 + 20 = USB-JTAG. Not recommended for other uses. if (gpio > 18 && gpio < 21) return false; // 19 + 20 = USB-JTAG. Not recommended for other uses.
if (gpio > 21 && gpio < 33) return false; // 22 to 32: not connected + SPI FLASH if (gpio > 21 && gpio < 33) return false; // 22 to 32: not connected + SPI FLASH
//if (gpio > 32 && gpio < 38) return false; // 33 to 37: not available if using _octal_ SPI Flash or _octal_ PSRAM if (gpio > 32 && gpio < 38) return !psramFound(); // 33 to 37: not available if using _octal_ SPI Flash or _octal_ PSRAM
// 38 to 48 are for general use. Be careful about straping pins GPIO45 and GPIO46 - these may be pull-up or pulled-down on your board. // 38 to 48 are for general use. Be careful about straping pins GPIO45 and GPIO46 - these may be pull-up or pulled-down on your board.
#elif defined(CONFIG_IDF_TARGET_ESP32S2) #elif defined(CONFIG_IDF_TARGET_ESP32S2)
// strapping pins: 0, 45 & 46 // strapping pins: 0, 45 & 46
@ -257,9 +257,8 @@ bool PinManagerClass::isPinOk(byte gpio, bool output)
// GPIO46 is input only and pulled down // GPIO46 is input only and pulled down
#else #else
if (gpio > 5 && gpio < 12) return false; //SPI flash pins if (gpio > 5 && gpio < 12) return false; //SPI flash pins
#ifdef BOARD_HAS_PSRAM if (strncmp_P(PSTR("ESP32-PICO"), ESP.getChipModel(), 10) == 0 && (gpio == 16 || gpio == 17)) return false; // PICO-D4: gpio16+17 are in use for onboard SPI FLASH
if (gpio == 16 || gpio == 17) return false; //PSRAM pins if (gpio == 16 || gpio == 17) return !psramFound(); //PSRAM pins on ESP32 (these are IO)
#endif
#endif #endif
if (output) return digitalPinCanOutput(gpio); if (output) return digitalPinCanOutput(gpio);
else return true; else return true;
@ -272,8 +271,8 @@ bool PinManagerClass::isPinOk(byte gpio, bool output)
return false; return false;
} }
PinOwner PinManagerClass::getPinOwner(byte gpio) { PinOwner PinManagerClass::getPinOwner(byte gpio) const
if (gpio >= WLED_NUM_PINS) return PinOwner::None; // catch error case, to avoid array out-of-bounds access {
if (!isPinOk(gpio, false)) return PinOwner::None; if (!isPinOk(gpio, false)) return PinOwner::None;
return ownerTag[gpio]; return ownerTag[gpio];
} }

View File

@ -61,7 +61,8 @@ enum struct PinOwner : uint8_t {
UM_Audioreactive = USERMOD_ID_AUDIOREACTIVE, // 0x20 // Usermod "audio_reactive.h" UM_Audioreactive = USERMOD_ID_AUDIOREACTIVE, // 0x20 // Usermod "audio_reactive.h"
UM_SdCard = USERMOD_ID_SD_CARD, // 0x25 // Usermod "usermod_sd_card.h" UM_SdCard = USERMOD_ID_SD_CARD, // 0x25 // Usermod "usermod_sd_card.h"
UM_PWM_OUTPUTS = USERMOD_ID_PWM_OUTPUTS, // 0x26 // Usermod "usermod_pwm_outputs.h" UM_PWM_OUTPUTS = USERMOD_ID_PWM_OUTPUTS, // 0x26 // Usermod "usermod_pwm_outputs.h"
UM_LDR_DUSK_DAWN = USERMOD_ID_LDR_DUSK_DAWN // 0x2B // Usermod "usermod_LDR_Dusk_Dawn_v2.h" UM_LDR_DUSK_DAWN = USERMOD_ID_LDR_DUSK_DAWN, // 0x2B // Usermod "usermod_LDR_Dusk_Dawn_v2.h"
UM_MAX17048 = USERMOD_ID_MAX17048 // 0x2F // Usermod "usermod_max17048.h"
}; };
static_assert(0u == static_cast<uint8_t>(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected"); static_assert(0u == static_cast<uint8_t>(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected");
@ -108,11 +109,11 @@ class PinManagerClass {
inline void deallocatePin(byte gpio) { deallocatePin(gpio, PinOwner::None); } inline void deallocatePin(byte gpio) { deallocatePin(gpio, PinOwner::None); }
// will return true for reserved pins // will return true for reserved pins
bool isPinAllocated(byte gpio, PinOwner tag = PinOwner::None); bool isPinAllocated(byte gpio, PinOwner tag = PinOwner::None) const;
// will return false for reserved pins // will return false for reserved pins
bool isPinOk(byte gpio, bool output = true); bool isPinOk(byte gpio, bool output = true) const;
PinOwner getPinOwner(byte gpio); PinOwner getPinOwner(byte gpio) const;
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
byte allocateLedc(byte channels); byte allocateLedc(byte channels);

View File

@ -109,7 +109,10 @@ int16_t loadPlaylist(JsonObject playlistObj, byte presetId) {
if (playlistRepeat > 0) playlistRepeat++; //add one extra repetition immediately since it will be deducted on first start if (playlistRepeat > 0) playlistRepeat++; //add one extra repetition immediately since it will be deducted on first start
playlistEndPreset = playlistObj["end"] | 0; playlistEndPreset = playlistObj["end"] | 0;
// if end preset is 255 restore original preset (if any running) upon playlist end // if end preset is 255 restore original preset (if any running) upon playlist end
if (playlistEndPreset == 255 && currentPreset > 0) playlistEndPreset = currentPreset; if (playlistEndPreset == 255 && currentPreset > 0) {
playlistEndPreset = currentPreset;
playlistOptions |= PL_OPTION_RESTORE; // for async save operation
}
if (playlistEndPreset > 250) playlistEndPreset = 0; if (playlistEndPreset > 250) playlistEndPreset = 0;
shuffle = shuffle || playlistObj["r"]; shuffle = shuffle || playlistObj["r"];
if (shuffle) playlistOptions |= PL_OPTION_SHUFFLE; if (shuffle) playlistOptions |= PL_OPTION_SHUFFLE;
@ -122,8 +125,7 @@ int16_t loadPlaylist(JsonObject playlistObj, byte presetId) {
void handlePlaylist() { void handlePlaylist() {
static unsigned long presetCycledTime = 0; static unsigned long presetCycledTime = 0;
// if fileDoc is not null JSON buffer is in use so just quit if (currentPlaylist < 0 || playlistEntries == nullptr) return;
if (currentPlaylist < 0 || playlistEntries == nullptr || fileDoc != nullptr) return;
if (millis() - presetCycledTime > (100*playlistEntryDur)) { if (millis() - presetCycledTime > (100*playlistEntryDur)) {
presetCycledTime = millis(); presetCycledTime = millis();
@ -135,7 +137,7 @@ void handlePlaylist() {
if (!playlistIndex) { if (!playlistIndex) {
if (playlistRepeat == 1) { //stop if all repetitions are done if (playlistRepeat == 1) { //stop if all repetitions are done
unloadPlaylist(); unloadPlaylist();
if (playlistEndPreset) applyPreset(playlistEndPreset); if (playlistEndPreset) applyPresetFromPlaylist(playlistEndPreset);
return; return;
} }
if (playlistRepeat > 1) playlistRepeat--; // decrease repeat count on each index reset if not an endless playlist if (playlistRepeat > 1) playlistRepeat--; // decrease repeat count on each index reset if not an endless playlist
@ -146,7 +148,7 @@ void handlePlaylist() {
jsonTransitionOnce = true; jsonTransitionOnce = true;
strip.setTransition(fadeTransition ? playlistEntries[playlistIndex].tr * 100 : 0); strip.setTransition(fadeTransition ? playlistEntries[playlistIndex].tr * 100 : 0);
playlistEntryDur = playlistEntries[playlistIndex].dur; playlistEntryDur = playlistEntries[playlistIndex].dur;
applyPreset(playlistEntries[playlistIndex].preset); applyPresetFromPlaylist(playlistEntries[playlistIndex].preset);
} }
} }
@ -157,7 +159,7 @@ void serializePlaylist(JsonObject sObj) {
JsonArray dur = playlist.createNestedArray("dur"); JsonArray dur = playlist.createNestedArray("dur");
JsonArray transition = playlist.createNestedArray(F("transition")); JsonArray transition = playlist.createNestedArray(F("transition"));
playlist[F("repeat")] = (playlistIndex < 0 && playlistRepeat > 0) ? playlistRepeat - 1 : playlistRepeat; // remove added repetition count (if not yet running) playlist[F("repeat")] = (playlistIndex < 0 && playlistRepeat > 0) ? playlistRepeat - 1 : playlistRepeat; // remove added repetition count (if not yet running)
playlist["end"] = playlistEndPreset; playlist["end"] = playlistOptions & PL_OPTION_RESTORE ? 255 : playlistEndPreset;
playlist["r"] = playlistOptions & PL_OPTION_SHUFFLE; playlist["r"] = playlistOptions & PL_OPTION_SHUFFLE;
for (int i=0; i<playlistLen; i++) { for (int i=0; i<playlistLen; i++) {
ps.add(playlistEntries[i].preset); ps.add(playlistEntries[i].preset);

View File

@ -12,21 +12,22 @@ static volatile byte presetToApply = 0;
static volatile byte callModeToApply = 0; static volatile byte callModeToApply = 0;
static volatile byte presetToSave = 0; static volatile byte presetToSave = 0;
static volatile int8_t saveLedmap = -1; static volatile int8_t saveLedmap = -1;
static char quickLoad[9]; static char *quickLoad = nullptr;
static char saveName[33]; static char *saveName = nullptr;
static bool includeBri = true, segBounds = true, selectedOnly = false, playlistSave = false;; static bool includeBri = true, segBounds = true, selectedOnly = false, playlistSave = false;;
static const char *getFileName(bool persist = true) { static const char presets_json[] PROGMEM = "/presets.json";
return persist ? "/presets.json" : "/tmp.json"; static const char tmp_json[] PROGMEM = "/tmp.json";
const char *getPresetsFileName(bool persistent) {
return persistent ? presets_json : tmp_json;
} }
static void doSaveState() { static void doSaveState() {
bool persist = (presetToSave < 251); bool persist = (presetToSave < 251);
const char *filename = getFileName(persist);
unsigned long start = millis(); unsigned long start = millis();
while (strip.isUpdating() && millis()-start < (2*FRAMETIME_FIXED)+1) yield(); // wait 2 frames while (strip.isUpdating() && millis()-start < (2*FRAMETIME_FIXED)+1) yield(); // wait 2 frames
if (!requestJSONBufferLock(10)) return; // will set fileDoc if (!requestJSONBufferLock(10)) return;
initPresetsFile(); // just in case if someone deleted presets.json using /edit initPresetsFile(); // just in case if someone deleted presets.json using /edit
JsonObject sObj = pDoc->to<JsonObject>(); JsonObject sObj = pDoc->to<JsonObject>();
@ -38,8 +39,9 @@ static void doSaveState() {
} else { } else {
serializeState(sObj, true, includeBri, segBounds, selectedOnly); serializeState(sObj, true, includeBri, segBounds, selectedOnly);
} }
sObj["n"] = saveName; if (saveName) sObj["n"] = saveName;
if (quickLoad[0]) sObj[F("ql")] = quickLoad; else sObj["n"] = F("Unkonwn preset"); // should not happen, but just in case...
if (quickLoad && quickLoad[0]) sObj[F("ql")] = quickLoad;
if (saveLedmap >= 0) sObj[F("ledmap")] = saveLedmap; if (saveLedmap >= 0) sObj[F("ledmap")] = saveLedmap;
/* /*
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
@ -51,23 +53,21 @@ static void doSaveState() {
#if defined(ARDUINO_ARCH_ESP32) #if defined(ARDUINO_ARCH_ESP32)
if (!persist) { if (!persist) {
if (tmpRAMbuffer!=nullptr) free(tmpRAMbuffer); if (tmpRAMbuffer!=nullptr) free(tmpRAMbuffer);
size_t len = measureJson(*fileDoc) + 1; size_t len = measureJson(*pDoc) + 1;
DEBUG_PRINTLN(len); DEBUG_PRINTLN(len);
// if possible use SPI RAM on ESP32 // if possible use SPI RAM on ESP32
#if defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM) if (psramSafe && psramFound())
if (psramFound())
tmpRAMbuffer = (char*) ps_malloc(len); tmpRAMbuffer = (char*) ps_malloc(len);
else else
#endif
tmpRAMbuffer = (char*) malloc(len); tmpRAMbuffer = (char*) malloc(len);
if (tmpRAMbuffer!=nullptr) { if (tmpRAMbuffer!=nullptr) {
serializeJson(*fileDoc, tmpRAMbuffer, len); serializeJson(*pDoc, tmpRAMbuffer, len);
} else { } else {
writeObjectToFileUsingId(filename, presetToSave, fileDoc); writeObjectToFileUsingId(getPresetsFileName(persist), presetToSave, pDoc);
} }
} else } else
#endif #endif
writeObjectToFileUsingId(filename, presetToSave, fileDoc); writeObjectToFileUsingId(getPresetsFileName(persist), presetToSave, pDoc);
if (persist) presetsModifiedTime = toki.second(); //unix time if (persist) presetsModifiedTime = toki.second(); //unix time
releaseJSONBufferLock(); releaseJSONBufferLock();
@ -76,8 +76,10 @@ static void doSaveState() {
// clean up // clean up
saveLedmap = -1; saveLedmap = -1;
presetToSave = 0; presetToSave = 0;
saveName[0] = '\0'; delete[] saveName;
quickLoad[0] = '\0'; delete[] quickLoad;
saveName = nullptr;
quickLoad = nullptr;
playlistSave = false; playlistSave = false;
} }
@ -85,8 +87,7 @@ bool getPresetName(byte index, String& name)
{ {
if (!requestJSONBufferLock(19)) return false; if (!requestJSONBufferLock(19)) return false;
bool presetExists = false; bool presetExists = false;
if (readObjectFromFileUsingId(getFileName(), index, pDoc)) if (readObjectFromFileUsingId(getPresetsFileName(), index, pDoc)) {
{
JsonObject fdo = pDoc->as<JsonObject>(); JsonObject fdo = pDoc->as<JsonObject>();
if (fdo["n"]) { if (fdo["n"]) {
name = (const char*)(fdo["n"]); name = (const char*)(fdo["n"]);
@ -99,12 +100,13 @@ bool getPresetName(byte index, String& name)
void initPresetsFile() void initPresetsFile()
{ {
if (WLED_FS.exists(getFileName())) return; char fileName[33]; strncpy_P(fileName, getPresetsFileName(), 32); fileName[32] = 0; //use PROGMEM safe copy as FS.open() does not
if (WLED_FS.exists(fileName)) return;
StaticJsonDocument<64> doc; StaticJsonDocument<64> doc;
JsonObject sObj = doc.to<JsonObject>(); JsonObject sObj = doc.to<JsonObject>();
sObj.createNestedObject("0"); sObj.createNestedObject("0");
File f = WLED_FS.open(getFileName(), "w"); File f = WLED_FS.open(fileName, "w");
if (!f) { if (!f) {
errorFlag = ERR_FS_GENERAL; errorFlag = ERR_FS_GENERAL;
return; return;
@ -113,8 +115,18 @@ void initPresetsFile()
f.close(); f.close();
} }
bool applyPresetFromPlaylist(byte index)
{
DEBUG_PRINT(F("Request to apply preset: "));
DEBUG_PRINTLN(index);
presetToApply = index;
callModeToApply = CALL_MODE_DIRECT_CHANGE;
return true;
}
bool applyPreset(byte index, byte callMode) bool applyPreset(byte index, byte callMode)
{ {
unloadPlaylist(); // applying a preset unloads the playlist (#3827)
DEBUG_PRINT(F("Request to apply preset: ")); DEBUG_PRINT(F("Request to apply preset: "));
DEBUG_PRINTLN(index); DEBUG_PRINTLN(index);
presetToApply = index; presetToApply = index;
@ -140,17 +152,13 @@ void handlePresets()
return; return;
} }
if (presetToApply == 0 || fileDoc) return; // no preset waiting to apply, or JSON buffer is already allocated, return to loop until free if (presetToApply == 0 || !requestJSONBufferLock(9)) return; // no preset waiting to apply, or JSON buffer is already allocated, return to loop until free
bool changePreset = false; bool changePreset = false;
uint8_t tmpPreset = presetToApply; // store temporary since deserializeState() may call applyPreset() uint8_t tmpPreset = presetToApply; // store temporary since deserializeState() may call applyPreset()
uint8_t tmpMode = callModeToApply; uint8_t tmpMode = callModeToApply;
JsonObject fdo; JsonObject fdo;
const char *filename = getFileName(tmpPreset < 255);
// allocate buffer
if (!requestJSONBufferLock(9)) return; // will also assign fileDoc
presetToApply = 0; //clear request for preset presetToApply = 0; //clear request for preset
callModeToApply = 0; callModeToApply = 0;
@ -160,14 +168,14 @@ void handlePresets()
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
if (tmpPreset==255 && tmpRAMbuffer!=nullptr) { if (tmpPreset==255 && tmpRAMbuffer!=nullptr) {
deserializeJson(*fileDoc,tmpRAMbuffer); deserializeJson(*pDoc,tmpRAMbuffer);
errorFlag = ERR_NONE; errorFlag = ERR_NONE;
} else } else
#endif #endif
{ {
errorFlag = readObjectFromFileUsingId(filename, tmpPreset, fileDoc) ? ERR_NONE : ERR_FS_PLOAD; errorFlag = readObjectFromFileUsingId(getPresetsFileName(tmpPreset < 255), tmpPreset, pDoc) ? ERR_NONE : ERR_FS_PLOAD;
} }
fdo = fileDoc->as<JsonObject>(); fdo = pDoc->as<JsonObject>();
//HTTP API commands //HTTP API commands
const char* httpwin = fdo["win"]; const char* httpwin = fdo["win"];
@ -194,15 +202,19 @@ void handlePresets()
} }
#endif #endif
releaseJSONBufferLock(); // will also clear fileDoc releaseJSONBufferLock();
if (changePreset) notify(tmpMode); // force UDP notification if (changePreset) notify(tmpMode); // force UDP notification
stateUpdated(tmpMode); // was colorUpdated() if anything breaks stateUpdated(tmpMode); // was colorUpdated() if anything breaks
updateInterfaces(tmpMode); updateInterfaces(tmpMode);
} }
//called from handleSet(PS=) [network callback (fileDoc==nullptr), IR (irrational), deserializeState, UDP] and deserializeState() [network callback (filedoc!=nullptr)] //called from handleSet(PS=) [network callback (sObj is empty), IR (irrational), deserializeState, UDP] and deserializeState() [network callback (filedoc!=nullptr)]
void savePreset(byte index, const char* pname, JsonObject sObj) void savePreset(byte index, const char* pname, JsonObject sObj)
{ {
if (!saveName) saveName = new char[33];
if (!quickLoad) quickLoad = new char[9];
if (!saveName || !quickLoad) return;
if (index == 0 || (index > 250 && index < 255)) return; if (index == 0 || (index > 250 && index < 255)) return;
if (pname) strlcpy(saveName, pname, 33); if (pname) strlcpy(saveName, pname, 33);
else { else {
@ -215,6 +227,14 @@ void savePreset(byte index, const char* pname, JsonObject sObj)
presetToSave = index; presetToSave = index;
playlistSave = false; playlistSave = false;
if (sObj[F("ql")].is<const char*>()) strlcpy(quickLoad, sObj[F("ql")].as<const char*>(), 9); // client limits QL to 2 chars, buffer for 8 bytes to allow unicode if (sObj[F("ql")].is<const char*>()) strlcpy(quickLoad, sObj[F("ql")].as<const char*>(), 9); // client limits QL to 2 chars, buffer for 8 bytes to allow unicode
else quickLoad[0] = 0;
const char *bootPS = PSTR("bootps");
if (!sObj[FPSTR(bootPS)].isNull()) {
bootPreset = sObj[FPSTR(bootPS)] | bootPreset;
sObj.remove(FPSTR(bootPS));
doSerializeConfig = 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) if (sObj.size()==0 || sObj["o"].isNull()) { // no "o" means not a playlist or custom API call, saving of state is async (not immediately)
includeBri = sObj["ib"].as<bool>() || sObj.size()==0 || index==255; // temporary preset needs brightness includeBri = sObj["ib"].as<bool>() || sObj.size()==0 || index==255; // temporary preset needs brightness
@ -226,17 +246,22 @@ void savePreset(byte index, const char* pname, JsonObject sObj)
if (sObj[F("playlist")].isNull()) { if (sObj[F("playlist")].isNull()) {
// we will save API call immediately (often causes presets.json corruption) // we will save API call immediately (often causes presets.json corruption)
presetToSave = 0; presetToSave = 0;
if (index > 250 || !fileDoc) return; // cannot save API calls to temporary preset (255) if (index <= 250) { // cannot save API calls to temporary preset (255)
sObj.remove("o"); sObj.remove("o");
sObj.remove("v"); sObj.remove("v");
sObj.remove("time"); sObj.remove("time");
sObj.remove(F("error")); sObj.remove(F("error"));
sObj.remove(F("psave")); sObj.remove(F("psave"));
if (sObj["n"].isNull()) sObj["n"] = saveName; if (sObj["n"].isNull()) sObj["n"] = saveName;
initPresetsFile(); // just in case if someone deleted presets.json using /edit initPresetsFile(); // just in case if someone deleted presets.json using /edit
writeObjectToFileUsingId(getFileName(index<255), index, fileDoc); writeObjectToFileUsingId(getPresetsFileName(), index, pDoc);
presetsModifiedTime = toki.second(); //unix time presetsModifiedTime = toki.second(); //unix time
updateFSInfo(); updateFSInfo();
}
delete[] saveName;
delete[] quickLoad;
saveName = nullptr;
quickLoad = nullptr;
} else { } else {
// store playlist // 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
@ -248,7 +273,7 @@ void savePreset(byte index, const char* pname, JsonObject sObj)
void deletePreset(byte index) { void deletePreset(byte index) {
StaticJsonDocument<24> empty; StaticJsonDocument<24> empty;
writeObjectToFileUsingId(getFileName(), index, &empty); writeObjectToFileUsingId(getPresetsFileName(), index, &empty);
presetsModifiedTime = toki.second(); //unix time presetsModifiedTime = toki.second(); //unix time
updateFSInfo(); updateFSInfo();
} }

View File

@ -108,7 +108,6 @@ static void setOff() {
void presetWithFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID) { void presetWithFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID) {
resetNightMode(); resetNightMode();
unloadPlaylist();
applyPresetWithFallback(presetID, CALL_MODE_BUTTON_PRESET, effectID, paletteID); applyPresetWithFallback(presetID, CALL_MODE_BUTTON_PRESET, effectID, paletteID);
} }
@ -123,11 +122,11 @@ static bool remoteJson(int button)
sprintf_P(objKey, PSTR("\"%d\":"), button); sprintf_P(objKey, PSTR("\"%d\":"), button);
// attempt to read command from remote.json // attempt to read command from remote.json
readObjectFromFile("/remote.json", objKey, pDoc); readObjectFromFile(PSTR("/remote.json"), objKey, pDoc);
JsonObject fdo = pDoc->as<JsonObject>(); JsonObject fdo = pDoc->as<JsonObject>();
if (fdo.isNull()) { if (fdo.isNull()) {
// the received button does not exist // the received button does not exist
//if (!WLED_FS.exists("/remote.json")) errorFlag = ERR_FS_RMLOAD; //warn if file itself doesn't exist //if (!WLED_FS.exists(F("/remote.json"))) errorFlag = ERR_FS_RMLOAD; //warn if file itself doesn't exist
releaseJSONBufferLock(); releaseJSONBufferLock();
return parsed; return parsed;
} }

41
wled00/set.cpp Executable file → Normal file
View File

@ -102,12 +102,18 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
if (rlyPin>=0 && pinManager.isPinAllocated(rlyPin, PinOwner::Relay)) { if (rlyPin>=0 && pinManager.isPinAllocated(rlyPin, PinOwner::Relay)) {
pinManager.deallocatePin(rlyPin, PinOwner::Relay); pinManager.deallocatePin(rlyPin, PinOwner::Relay);
} }
#ifndef WLED_DISABLE_INFRARED
if (irPin>=0 && pinManager.isPinAllocated(irPin, PinOwner::IR)) { if (irPin>=0 && pinManager.isPinAllocated(irPin, PinOwner::IR)) {
pinManager.deallocatePin(irPin, PinOwner::IR); pinManager.deallocatePin(irPin, PinOwner::IR);
} }
#endif
for (uint8_t s=0; s<WLED_MAX_BUTTONS; s++) { for (uint8_t s=0; s<WLED_MAX_BUTTONS; s++) {
if (btnPin[s]>=0 && pinManager.isPinAllocated(btnPin[s], PinOwner::Button)) { if (btnPin[s]>=0 && pinManager.isPinAllocated(btnPin[s], PinOwner::Button)) {
pinManager.deallocatePin(btnPin[s], PinOwner::Button); pinManager.deallocatePin(btnPin[s], PinOwner::Button);
#ifdef SOC_TOUCH_VERSION_2 // ESP32 S2 and S3 have a function to check touch state, detach interrupt
if (digitalPinToTouchChannel(btnPin[s]) >= 0) // if touch capable pin
touchDetachInterrupt(btnPin[s]); // if not assigned previously, this will do nothing
#endif
} }
} }
@ -121,6 +127,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
autoSegments = request->hasArg(F("MS")); autoSegments = request->hasArg(F("MS"));
correctWB = request->hasArg(F("CCT")); correctWB = request->hasArg(F("CCT"));
cctFromRgb = request->hasArg(F("CR")); cctFromRgb = request->hasArg(F("CR"));
cctICused = request->hasArg(F("IC"));
strip.cctBlending = request->arg(F("CB")).toInt(); strip.cctBlending = request->arg(F("CB")).toInt();
Bus::setCCTBlend(strip.cctBlending); Bus::setCCTBlend(strip.cctBlending);
Bus::setGlobalAWMode(request->arg(F("AW")).toInt()); Bus::setGlobalAWMode(request->arg(F("AW")).toInt());
@ -165,12 +172,12 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
uint16_t freq = request->arg(sp).toInt(); uint16_t freq = request->arg(sp).toInt();
if (IS_PWM(type)) { if (IS_PWM(type)) {
switch (freq) { switch (freq) {
case 0 : freq = WLED_PWM_FREQ/3; break; case 0 : freq = WLED_PWM_FREQ/2; break;
case 1 : freq = WLED_PWM_FREQ/2; break; case 1 : freq = WLED_PWM_FREQ*2/3; break;
default: default:
case 2 : freq = WLED_PWM_FREQ; break; case 2 : freq = WLED_PWM_FREQ; break;
case 3 : freq = WLED_PWM_FREQ*4/3; break; case 3 : freq = WLED_PWM_FREQ*2; break;
case 4 : freq = WLED_PWM_FREQ*2; break; case 4 : freq = WLED_PWM_FREQ*10/3; break; // uint16_t max (19531 * 3.333)
} }
} else if (IS_DIGITAL(type) && IS_2PIN(type)) { } else if (IS_DIGITAL(type) && IS_2PIN(type)) {
switch (freq) { switch (freq) {
@ -218,6 +225,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
BusManager::updateColorOrderMap(com); BusManager::updateColorOrderMap(com);
// update other pins // update other pins
#ifndef WLED_DISABLE_INFRARED
int hw_ir_pin = request->arg(F("IR")).toInt(); int hw_ir_pin = request->arg(F("IR")).toInt();
if (pinManager.allocatePin(hw_ir_pin,false, PinOwner::IR)) { if (pinManager.allocatePin(hw_ir_pin,false, PinOwner::IR)) {
irPin = hw_ir_pin; irPin = hw_ir_pin;
@ -225,6 +233,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
irPin = -1; irPin = -1;
} }
irEnabled = request->arg(F("IT")).toInt(); irEnabled = request->arg(F("IT")).toInt();
#endif
irApplyToAllSelected = !request->hasArg(F("MSO")); irApplyToAllSelected = !request->hasArg(F("MSO"));
int hw_rly_pin = request->arg(F("RL")).toInt(); int hw_rly_pin = request->arg(F("RL")).toInt();
@ -236,6 +245,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
rlyMde = (bool)request->hasArg(F("RM")); rlyMde = (bool)request->hasArg(F("RM"));
disablePullUp = (bool)request->hasArg(F("IP")); disablePullUp = (bool)request->hasArg(F("IP"));
touchThreshold = request->arg(F("TT")).toInt();
for (uint8_t i=0; i<WLED_MAX_BUTTONS; i++) { for (uint8_t i=0; i<WLED_MAX_BUTTONS; i++) {
char bt[4] = "BT"; bt[2] = (i<10?48:55)+i; bt[3] = 0; // button pin (use A,B,C,... if WLED_MAX_BUTTONS>10) char bt[4] = "BT"; bt[2] = (i<10?48:55)+i; bt[3] = 0; // button pin (use A,B,C,... if WLED_MAX_BUTTONS>10)
char be[4] = "BE"; be[2] = (i<10?48:55)+i; be[3] = 0; // button type (use A,B,C,... if WLED_MAX_BUTTONS>10) char be[4] = "BE"; be[2] = (i<10?48:55)+i; be[3] = 0; // button type (use A,B,C,... if WLED_MAX_BUTTONS>10)
@ -252,12 +262,21 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
btnPin[i] = -1; btnPin[i] = -1;
pinManager.deallocatePin(hw_btn_pin,PinOwner::Button); pinManager.deallocatePin(hw_btn_pin,PinOwner::Button);
} }
else if ((buttonType[i] == BTN_TYPE_TOUCH || buttonType[i] == BTN_TYPE_TOUCH_SWITCH) && digitalPinToTouchChannel(btnPin[i]) < 0) else if ((buttonType[i] == BTN_TYPE_TOUCH || buttonType[i] == BTN_TYPE_TOUCH_SWITCH))
{ {
// not a touch pin if (digitalPinToTouchChannel(btnPin[i]) < 0)
DEBUG_PRINTF_P(PSTR("PIN ALLOC error: GPIO%d for touch button #%d is not an touch pin!\n"), btnPin[i], i); {
btnPin[i] = -1; // not a touch pin
pinManager.deallocatePin(hw_btn_pin,PinOwner::Button); DEBUG_PRINTF_P(PSTR("PIN ALLOC error: GPIO%d for touch button #%d is not an touch pin!\n"), btnPin[i], i);
btnPin[i] = -1;
pinManager.deallocatePin(hw_btn_pin,PinOwner::Button);
}
#ifdef SOC_TOUCH_VERSION_2 // ESP32 S2 and S3 have a fucntion to check touch state but need to attach an interrupt to do so
else
{
touchAttachInterrupt(btnPin[i], touchButtonISR, 256 + (touchThreshold << 4)); // threshold on Touch V2 is much higher (1500 is a value given by Espressif example, I measured changes of over 5000)
}
#endif
} }
else else
#endif #endif
@ -277,7 +296,6 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
buttonType[i] = BTN_TYPE_NONE; buttonType[i] = BTN_TYPE_NONE;
} }
} }
touchThreshold = request->arg(F("TT")).toInt();
briS = request->arg(F("CA")).toInt(); briS = request->arg(F("CA")).toInt();
@ -879,7 +897,6 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
//apply preset //apply preset
if (updateVal(req.c_str(), "PL=", &presetCycCurr, presetCycMin, presetCycMax)) { if (updateVal(req.c_str(), "PL=", &presetCycCurr, presetCycMin, presetCycMax)) {
unloadPlaylist();
applyPreset(presetCycCurr); applyPreset(presetCycCurr);
} }

View File

@ -30,8 +30,8 @@
bool dmxStarted = false; bool dmxStarted = false;
int sendPin = 2; //default on ESP8266 int sendPin = 2; //default on ESP8266
//DMX value array and size. Entry 0 will hold startbyte //DMX value array and size. Entry 0 will hold startbyte, so we need 512+1 elements
uint8_t dmxDataStore[dmxMaxChannel] = {}; uint8_t dmxDataStore[dmxMaxChannel+1] = {};
int channelSize; int channelSize;

View File

@ -21,8 +21,6 @@
#define DYNAMIC_JSON_DOCUMENT_SIZE 16384 #define DYNAMIC_JSON_DOCUMENT_SIZE 16384
#endif #endif
constexpr const char* JSON_MIMETYPE = "application/json";
/* /*
* Json Response * Json Response
* */ * */
@ -66,7 +64,7 @@ class AsyncJsonResponse: public AsyncAbstractResponse {
AsyncJsonResponse(JsonDocument *ref, bool isArray=false) : _jsonBuffer(1), _isValid{false} { AsyncJsonResponse(JsonDocument *ref, bool isArray=false) : _jsonBuffer(1), _isValid{false} {
_code = 200; _code = 200;
_contentType = JSON_MIMETYPE; _contentType = FPSTR(CONTENT_TYPE_JSON);
if(isArray) if(isArray)
_root = ref->to<JsonArray>(); _root = ref->to<JsonArray>();
else else
@ -75,7 +73,7 @@ class AsyncJsonResponse: public AsyncAbstractResponse {
AsyncJsonResponse(size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE, bool isArray=false) : _jsonBuffer(maxJsonBufferSize), _isValid{false} { AsyncJsonResponse(size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE, bool isArray=false) : _jsonBuffer(maxJsonBufferSize), _isValid{false} {
_code = 200; _code = 200;
_contentType = JSON_MIMETYPE; _contentType = FPSTR(CONTENT_TYPE_JSON);
if(isArray) if(isArray)
_root = _jsonBuffer.createNestedArray(); _root = _jsonBuffer.createNestedArray();
else else

View File

@ -209,6 +209,14 @@
#include "../usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h" #include "../usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h"
#endif #endif
#ifdef USERMOD_MAX17048
#include "../usermods/MAX17048_v2/usermod_max17048.h"
#endif
#ifdef USERMOD_TETRISAI
#include "../usermods/TetrisAI_v2/usermod_v2_tetrisai.h"
#endif
void registerUsermods() void registerUsermods()
{ {
/* /*
@ -405,4 +413,12 @@ void registerUsermods()
#ifdef USERMOD_STAIRCASE_WIPE #ifdef USERMOD_STAIRCASE_WIPE
usermods.add(new StairwayWipeUsermod()); usermods.add(new StairwayWipeUsermod());
#endif #endif
#ifdef USERMOD_MAX17048
usermods.add(new Usermod_MAX17048());
#endif
#ifdef USERMOD_TETRISAI
usermods.add(new TetrisAIUsermod());
#endif
} }

View File

@ -228,7 +228,6 @@ bool requestJSONBufferLock(uint8_t module)
DEBUG_PRINT(F("JSON buffer locked. (")); DEBUG_PRINT(F("JSON buffer locked. ("));
DEBUG_PRINT(jsonBufferLock); DEBUG_PRINT(jsonBufferLock);
DEBUG_PRINTLN(")"); DEBUG_PRINTLN(")");
fileDoc = pDoc; // used for applying presets (presets.cpp)
pDoc->clear(); pDoc->clear();
return true; return true;
} }
@ -239,7 +238,6 @@ void releaseJSONBufferLock()
DEBUG_PRINT(F("JSON buffer released. (")); DEBUG_PRINT(F("JSON buffer released. ("));
DEBUG_PRINT(jsonBufferLock); DEBUG_PRINT(jsonBufferLock);
DEBUG_PRINTLN(")"); DEBUG_PRINTLN(")");
fileDoc = nullptr;
jsonBufferLock = 0; jsonBufferLock = 0;
} }
@ -265,8 +263,8 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe
} else return 0; } else return 0;
} }
if (src == JSON_palette_names && mode > GRADIENT_PALETTE_COUNT) { if (src == JSON_palette_names && mode > (GRADIENT_PALETTE_COUNT + 13)) {
snprintf_P(dest, maxLen, PSTR("~ Custom %d~"), 255-mode); snprintf_P(dest, maxLen, PSTR("~ Custom %d ~"), 255-mode);
dest[maxLen-1] = '\0'; dest[maxLen-1] = '\0';
return strlen(dest); return strlen(dest);
} }
@ -539,13 +537,13 @@ um_data_t* simulateSound(uint8_t simulationId)
return um_data; return um_data;
} }
static const char s_ledmap_tmpl[] PROGMEM = "ledmap%d.json";
// enumerate all ledmapX.json files on FS and extract ledmap names if existing // enumerate all ledmapX.json files on FS and extract ledmap names if existing
void enumerateLedmaps() { void enumerateLedmaps() {
ledMaps = 1; ledMaps = 1;
for (size_t i=1; i<WLED_MAX_LEDMAPS; i++) { for (size_t i=1; i<WLED_MAX_LEDMAPS; i++) {
char fileName[33]; char fileName[33] = "/";
sprintf_P(fileName, PSTR("/ledmap%d.json"), i); sprintf_P(fileName+1, s_ledmap_tmpl, i);
bool isFile = WLED_FS.exists(fileName); bool isFile = WLED_FS.exists(fileName);
#ifndef ESP8266 #ifndef ESP8266
@ -574,7 +572,7 @@ void enumerateLedmaps() {
} }
if (!ledmapNames[i-1]) { if (!ledmapNames[i-1]) {
char tmp[33]; char tmp[33];
snprintf_P(tmp, 32, PSTR("ledmap%d.json"), i); snprintf_P(tmp, 32, s_ledmap_tmpl, i);
len = strlen(tmp); len = strlen(tmp);
ledmapNames[i-1] = new char[len+1]; ledmapNames[i-1] = new char[len+1];
if (ledmapNames[i-1]) strlcpy(ledmapNames[i-1], tmp, 33); if (ledmapNames[i-1]) strlcpy(ledmapNames[i-1], tmp, 33);

View File

@ -240,10 +240,11 @@ void WLED::loop()
DEBUG_PRINT(F("Runtime: ")); DEBUG_PRINTLN(millis()); DEBUG_PRINT(F("Runtime: ")); DEBUG_PRINTLN(millis());
DEBUG_PRINT(F("Unix time: ")); toki.printTime(toki.getTime()); DEBUG_PRINT(F("Unix time: ")); toki.printTime(toki.getTime());
DEBUG_PRINT(F("Free heap: ")); DEBUG_PRINTLN(ESP.getFreeHeap()); DEBUG_PRINT(F("Free heap: ")); DEBUG_PRINTLN(ESP.getFreeHeap());
#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) #if defined(ARDUINO_ARCH_ESP32)
if (psramFound()) { if (psramFound()) {
DEBUG_PRINT(F("Total PSRAM: ")); DEBUG_PRINT(ESP.getPsramSize()/1024); DEBUG_PRINTLN("kB"); DEBUG_PRINT(F("Total PSRAM: ")); DEBUG_PRINT(ESP.getPsramSize()/1024); DEBUG_PRINTLN("kB");
DEBUG_PRINT(F("Free PSRAM: ")); DEBUG_PRINT(ESP.getFreePsram()/1024); DEBUG_PRINTLN("kB"); DEBUG_PRINT(F("Free PSRAM: ")); DEBUG_PRINT(ESP.getFreePsram()/1024); DEBUG_PRINTLN("kB");
if (!psramSafe) DEBUG_PRINTLN(F("Not using PSRAM."));
} }
#endif #endif
DEBUG_PRINT(F("Wifi state: ")); DEBUG_PRINTLN(WiFi.status()); DEBUG_PRINT(F("Wifi state: ")); DEBUG_PRINTLN(WiFi.status());
@ -269,6 +270,7 @@ void WLED::loop()
maxLoopMillis = 0; maxLoopMillis = 0;
maxUsermodMillis = 0; maxUsermodMillis = 0;
maxStripMillis = 0; maxStripMillis = 0;
avgLoopMillis = 0;
avgUsermodMillis = 0; avgUsermodMillis = 0;
avgStripMillis = 0; avgStripMillis = 0;
debugTime = millis(); debugTime = millis();
@ -328,7 +330,7 @@ void WLED::setup()
DEBUG_PRINTLN(); DEBUG_PRINTLN();
DEBUG_PRINT(F("---WLED ")); DEBUG_PRINT(F("---WLED "));
DEBUG_PRINT(versionString); DEBUG_PRINT(versionString);
DEBUG_PRINT(" "); DEBUG_PRINT(F(" "));
DEBUG_PRINT(VERSION); DEBUG_PRINT(VERSION);
DEBUG_PRINTLN(F(" INIT---")); DEBUG_PRINTLN(F(" INIT---"));
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
@ -360,56 +362,26 @@ void WLED::setup()
DEBUG_PRINT(F(", speed ")); DEBUG_PRINT(ESP.getFlashChipSpeed()/1000000);DEBUG_PRINTLN(F("MHz.")); DEBUG_PRINT(F(", speed ")); DEBUG_PRINT(ESP.getFlashChipSpeed()/1000000);DEBUG_PRINTLN(F("MHz."));
#else #else
DEBUG_PRINT(F("esp8266 ")); DEBUG_PRINT(F("esp8266 @ ")); DEBUG_PRINT(ESP.getCpuFreqMHz()); DEBUG_PRINT(F("MHz.\nCore: "));
DEBUG_PRINTLN(ESP.getCoreVersion()); DEBUG_PRINTLN(ESP.getCoreVersion());
DEBUG_PRINT(F("FLASH: ")); DEBUG_PRINT((ESP.getFlashChipSize()/1024)/1024); DEBUG_PRINTLN(F(" MB"));
#endif #endif
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap()); DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) #if defined(ARDUINO_ARCH_ESP32)
/* // BOARD_HAS_PSRAM also means that a compiler flag "-mfix-esp32-psram-cache-issue" was used and so PSRAM is safe to use on rev.1 ESP32
* The following code is obsolete as PinManager::isPinOK() will return false for reserved GPIO. #if !defined(BOARD_HAS_PSRAM) && !(defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3))
* Additionally xml.cpp will inform UI about reserved GPIO. if (psramFound() && ESP.getChipRevision() < 3) psramSafe = false;
* if (!psramSafe) DEBUG_PRINTLN(F("Not using PSRAM."));
#if defined(CONFIG_IDF_TARGET_ESP32S3)
// S3: reserve GPIO 33-37 for "octal" PSRAM
managed_pin_type pins[] = { {33, true}, {34, true}, {35, true}, {36, true}, {37, true} };
pinManager.allocateMultiplePins(pins, sizeof(pins)/sizeof(managed_pin_type), PinOwner::SPI_RAM);
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
// S2: reserve GPIO 26-32 for PSRAM (may fail due to isPinOk() but that will also prevent other allocation)
managed_pin_type pins[] = { {26, true}, {27, true}, {28, true}, {29, true}, {30, true}, {31, true}, {32, true} };
pinManager.allocateMultiplePins(pins, sizeof(pins)/sizeof(managed_pin_type), PinOwner::SPI_RAM);
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
// C3: reserve GPIO 12-17 for PSRAM (may fail due to isPinOk() but that will also prevent other allocation)
managed_pin_type pins[] = { {12, true}, {13, true}, {14, true}, {15, true}, {16, true}, {17, true} };
pinManager.allocateMultiplePins(pins, sizeof(pins)/sizeof(managed_pin_type), PinOwner::SPI_RAM);
#else
// GPIO16/GPIO17 reserved for SPI RAM
managed_pin_type pins[] = { {16, true}, {17, true} };
pinManager.allocateMultiplePins(pins, sizeof(pins)/sizeof(managed_pin_type), PinOwner::SPI_RAM);
#endif #endif
*/ pDoc = new PSRAMDynamicJsonDocument((psramSafe && psramFound() ? 2 : 1)*JSON_BUFFER_SIZE);
#if defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM) DEBUG_PRINT(F("JSON buffer allocated: ")); DEBUG_PRINTLN((psramSafe && psramFound() ? 2 : 1)*JSON_BUFFER_SIZE);
pDoc = new PSRAMDynamicJsonDocument(2*JSON_BUFFER_SIZE); // if the above fails requestJsonBufferLock() will always return false preventing crashes
if (!pDoc) pDoc = new PSRAMDynamicJsonDocument(JSON_BUFFER_SIZE); // falback if double sized buffer could not be allocated
// if the above still fails requestJsonBufferLock() will always return false preventing crashes
if (psramFound()) { if (psramFound()) {
DEBUG_PRINT(F("Total PSRAM: ")); DEBUG_PRINT(ESP.getPsramSize()/1024); DEBUG_PRINTLN("kB"); DEBUG_PRINT(F("Total PSRAM: ")); DEBUG_PRINT(ESP.getPsramSize()/1024); DEBUG_PRINTLN("kB");
DEBUG_PRINT(F("Free PSRAM : ")); DEBUG_PRINT(ESP.getFreePsram()/1024); DEBUG_PRINTLN("kB"); DEBUG_PRINT(F("Free PSRAM : ")); DEBUG_PRINT(ESP.getFreePsram()/1024); DEBUG_PRINTLN("kB");
} }
#else
if (!pDoc) pDoc = &gDoc; // just in case ... (it should be globally assigned)
DEBUG_PRINTLN(F("PSRAM not used."));
#endif
#endif #endif
#if defined(ARDUINO_ESP32_PICO)
// special handling for PICO-D4: gpio16+17 are in use for onboard SPI FLASH (not PSRAM)
managed_pin_type pins[] = { {16, true}, {17, true} };
pinManager.allocateMultiplePins(pins, sizeof(pins)/sizeof(managed_pin_type), PinOwner::SPI_RAM);
#endif
//DEBUG_PRINT(F("LEDs inited. heap usage ~"));
//DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap());
#if defined(WLED_DEBUG) && !defined(WLED_DEBUG_HOST) #if defined(WLED_DEBUG) && !defined(WLED_DEBUG_HOST)
pinManager.allocatePin(hardwareTX, true, PinOwner::DebugOut); // TX (GPIO1 on ESP32) reserved for debug output pinManager.allocatePin(hardwareTX, true, PinOwner::DebugOut); // TX (GPIO1 on ESP32) reserved for debug output
@ -423,8 +395,6 @@ void WLED::setup()
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap()); DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
for (uint8_t i=1; i<WLED_MAX_BUTTONS; i++) btnPin[i] = -1;
bool fsinit = false; bool fsinit = false;
DEBUGFS_PRINTLN(F("Mount FS")); DEBUGFS_PRINTLN(F("Mount FS"));
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
@ -453,6 +423,7 @@ void WLED::setup()
DEBUG_PRINTLN(F("Reading config")); DEBUG_PRINTLN(F("Reading config"));
deserializeConfigFromFS(); deserializeConfigFromFS();
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
#if defined(STATUSLED) && STATUSLED>=0 #if defined(STATUSLED) && STATUSLED>=0
if (!pinManager.isPinAllocated(STATUSLED)) { if (!pinManager.isPinAllocated(STATUSLED)) {
@ -556,7 +527,7 @@ void WLED::setup()
void WLED::beginStrip() void WLED::beginStrip()
{ {
// Initialize NeoPixel Strip and button // Initialize NeoPixel Strip and button
strip.finalizeInit(); // busses created during deserializeConfig() strip.finalizeInit(); // busses created during deserializeConfig() if config existed
strip.makeAutoSegments(); strip.makeAutoSegments();
strip.setBrightness(0); strip.setBrightness(0);
strip.setShowCallback(handleOverlayDraw); strip.setShowCallback(handleOverlayDraw);

52
wled00/wled.h Executable file → Normal file
View File

@ -3,12 +3,12 @@
/* /*
Main sketch, global variable declarations Main sketch, global variable declarations
@title WLED project sketch @title WLED project sketch
@version 0.15.0-b1 @version 0.15.0-b2
@author Christian Schwinne @author Christian Schwinne
*/ */
// version code in format yymmddb (b = daily build) // version code in format yymmddb (b = daily build)
#define VERSION 2402170 #define VERSION 2404050
//uncomment this if you have a "my_config.h" file you'd like to use //uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG //#define WLED_USE_MY_CONFIG
@ -158,15 +158,16 @@
// The following is a construct to enable code to compile without it. // The following is a construct to enable code to compile without it.
// There is a code that will still not use PSRAM though: // There is a code that will still not use PSRAM though:
// AsyncJsonResponse is a derived class that implements DynamicJsonDocument (AsyncJson-v6.h) // AsyncJsonResponse is a derived class that implements DynamicJsonDocument (AsyncJson-v6.h)
#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM) #if defined(ARDUINO_ARCH_ESP32)
extern bool psramSafe;
struct PSRAM_Allocator { struct PSRAM_Allocator {
void* allocate(size_t size) { void* allocate(size_t size) {
if (psramFound()) return ps_malloc(size); // use PSRAM if it exists if (psramSafe && psramFound()) return ps_malloc(size); // use PSRAM if it exists
else return malloc(size); // fallback else return malloc(size); // fallback
} }
void* reallocate(void* ptr, size_t new_size) { void* reallocate(void* ptr, size_t new_size) {
if (psramFound()) return ps_realloc(ptr, new_size); // use PSRAM if it exists if (psramSafe && psramFound()) return ps_realloc(ptr, new_size); // use PSRAM if it exists
else return realloc(ptr, new_size); // fallback else return realloc(ptr, new_size); // fallback
} }
void deallocate(void* pointer) { void deallocate(void* pointer) {
free(pointer); free(pointer);
@ -271,9 +272,10 @@ WLED_GLOBAL char otaPass[33] _INIT(DEFAULT_OTA_PASS);
// Hardware and pin config // Hardware and pin config
#ifndef BTNPIN #ifndef BTNPIN
WLED_GLOBAL int8_t btnPin[WLED_MAX_BUTTONS] _INIT({0}); #define BTNPIN 0,-1
#else #endif
WLED_GLOBAL int8_t btnPin[WLED_MAX_BUTTONS] _INIT({BTNPIN}); #ifndef BTNTYPE
#define BTNTYPE BTN_TYPE_PUSH,BTN_TYPE_NONE
#endif #endif
#ifndef RLYPIN #ifndef RLYPIN
WLED_GLOBAL int8_t rlyPin _INIT(-1); WLED_GLOBAL int8_t rlyPin _INIT(-1);
@ -287,9 +289,10 @@ WLED_GLOBAL bool rlyMde _INIT(true);
WLED_GLOBAL bool rlyMde _INIT(RLYMDE); WLED_GLOBAL bool rlyMde _INIT(RLYMDE);
#endif #endif
#ifndef IRPIN #ifndef IRPIN
WLED_GLOBAL int8_t irPin _INIT(-1); #define IRPIN -1
#else #endif
WLED_GLOBAL int8_t irPin _INIT(IRPIN); #ifndef IRTYPE
#define IRTYPE 0
#endif #endif
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || (defined(RX) && defined(TX)) #if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || (defined(RX) && defined(TX))
@ -346,6 +349,11 @@ WLED_GLOBAL bool useGlobalLedBuffer _INIT(true); // double buffering enabled on
#endif #endif
WLED_GLOBAL bool correctWB _INIT(false); // CCT color correction of RGB color WLED_GLOBAL bool correctWB _INIT(false); // CCT color correction of RGB color
WLED_GLOBAL bool cctFromRgb _INIT(false); // CCT is calculated from RGB instead of using seg.cct WLED_GLOBAL bool cctFromRgb _INIT(false); // CCT is calculated from RGB instead of using seg.cct
#ifdef WLED_USE_IC_CCT
WLED_GLOBAL bool cctICused _INIT(true); // CCT IC used (Athom 15W bulbs)
#else
WLED_GLOBAL bool cctICused _INIT(false); // CCT IC used (Athom 15W bulbs)
#endif
WLED_GLOBAL bool gammaCorrectCol _INIT(true); // use gamma correction on colors WLED_GLOBAL bool gammaCorrectCol _INIT(true); // use gamma correction on colors
WLED_GLOBAL bool gammaCorrectBri _INIT(false); // use gamma correction on brightness WLED_GLOBAL bool gammaCorrectBri _INIT(false); // use gamma correction on brightness
WLED_GLOBAL float gammaCorrectVal _INIT(2.8f); // gamma correction value WLED_GLOBAL float gammaCorrectVal _INIT(2.8f); // gamma correction value
@ -374,13 +382,11 @@ WLED_GLOBAL NodesMap Nodes;
WLED_GLOBAL bool nodeListEnabled _INIT(true); WLED_GLOBAL bool nodeListEnabled _INIT(true);
WLED_GLOBAL bool nodeBroadcastEnabled _INIT(true); WLED_GLOBAL bool nodeBroadcastEnabled _INIT(true);
WLED_GLOBAL byte buttonType[WLED_MAX_BUTTONS] _INIT({BTN_TYPE_PUSH}); #ifndef WLED_DISABLE_INFRARED
#if defined(IRTYPE) && defined(IRPIN) WLED_GLOBAL int8_t irPin _INIT(IRPIN);
WLED_GLOBAL byte irEnabled _INIT(IRTYPE); // Infrared receiver WLED_GLOBAL byte irEnabled _INIT(IRTYPE); // Infrared receiver
#else
WLED_GLOBAL byte irEnabled _INIT(0); // Infrared receiver disabled
#endif #endif
WLED_GLOBAL bool irApplyToAllSelected _INIT(true); //apply IR to all selected segments WLED_GLOBAL bool irApplyToAllSelected _INIT(true); //apply IR or ESP-NOW to all selected segments
WLED_GLOBAL uint16_t udpPort _INIT(21324); // WLED notifier default port WLED_GLOBAL uint16_t udpPort _INIT(21324); // WLED notifier default port
WLED_GLOBAL uint16_t udpPort2 _INIT(65506); // WLED notifier supplemental port WLED_GLOBAL uint16_t udpPort2 _INIT(65506); // WLED notifier supplemental port
@ -567,9 +573,11 @@ WLED_GLOBAL byte bri _INIT(briS); // global brightness
WLED_GLOBAL byte briOld _INIT(0); // global brightness while in transition loop (previous iteration) WLED_GLOBAL byte briOld _INIT(0); // global brightness while in transition loop (previous iteration)
WLED_GLOBAL byte briT _INIT(0); // global brightness during transition WLED_GLOBAL byte briT _INIT(0); // global brightness during transition
WLED_GLOBAL byte briLast _INIT(128); // brightness before turned off. Used for toggle function WLED_GLOBAL byte briLast _INIT(128); // brightness before turned off. Used for toggle function
WLED_GLOBAL byte whiteLast _INIT(128); // white channel before turned off. Used for toggle function WLED_GLOBAL byte whiteLast _INIT(128); // white channel before turned off. Used for toggle function in ir.cpp
// button // button
WLED_GLOBAL int8_t btnPin[WLED_MAX_BUTTONS] _INIT({BTNPIN});
WLED_GLOBAL byte buttonType[WLED_MAX_BUTTONS] _INIT({BTNTYPE});
WLED_GLOBAL bool buttonPublishMqtt _INIT(false); WLED_GLOBAL bool buttonPublishMqtt _INIT(false);
WLED_GLOBAL bool buttonPressedBefore[WLED_MAX_BUTTONS] _INIT({false}); WLED_GLOBAL bool buttonPressedBefore[WLED_MAX_BUTTONS] _INIT({false});
WLED_GLOBAL bool buttonLongPressed[WLED_MAX_BUTTONS] _INIT({false}); WLED_GLOBAL bool buttonLongPressed[WLED_MAX_BUTTONS] _INIT({false});
@ -690,7 +698,6 @@ WLED_GLOBAL uint16_t olen _INIT(0);
WLED_GLOBAL size_t fsBytesUsed _INIT(0); WLED_GLOBAL size_t fsBytesUsed _INIT(0);
WLED_GLOBAL size_t fsBytesTotal _INIT(0); WLED_GLOBAL size_t fsBytesTotal _INIT(0);
WLED_GLOBAL unsigned long presetsModifiedTime _INIT(0L); WLED_GLOBAL unsigned long presetsModifiedTime _INIT(0L);
WLED_GLOBAL JsonDocument* fileDoc;
WLED_GLOBAL bool doCloseFile _INIT(false); WLED_GLOBAL bool doCloseFile _INIT(false);
// presets // presets
@ -703,7 +710,8 @@ WLED_GLOBAL byte optionType;
WLED_GLOBAL bool doSerializeConfig _INIT(false); // flag to initiate saving of config WLED_GLOBAL bool doSerializeConfig _INIT(false); // flag to initiate saving of config
WLED_GLOBAL bool doReboot _INIT(false); // flag to initiate reboot from async handlers WLED_GLOBAL bool doReboot _INIT(false); // flag to initiate reboot from async handlers
WLED_GLOBAL bool doPublishMqtt _INIT(false);
WLED_GLOBAL bool psramSafe _INIT(true); // is it safe to use PSRAM (on ESP32 rev.1; compiler fix used "-mfix-esp32-psram-cache-issue")
// status led // status led
#if defined(STATUSLED) #if defined(STATUSLED)
@ -780,7 +788,7 @@ WLED_GLOBAL int8_t spi_sclk _INIT(SPISCLKPIN);
#endif #endif
// global ArduinoJson buffer // global ArduinoJson buffer
#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM) #if defined(ARDUINO_ARCH_ESP32)
WLED_GLOBAL JsonDocument *pDoc _INIT(nullptr); WLED_GLOBAL JsonDocument *pDoc _INIT(nullptr);
#else #else
WLED_GLOBAL StaticJsonDocument<JSON_BUFFER_SIZE> gDoc; WLED_GLOBAL StaticJsonDocument<JSON_BUFFER_SIZE> gDoc;

View File

@ -365,7 +365,7 @@ void applyMacro(byte index) {
// De-EEPROM routine, upgrade from previous versions to v0.11 // De-EEPROM routine, upgrade from previous versions to v0.11
void deEEP() { void deEEP() {
if (WLED_FS.exists("/presets.json")) return; if (WLED_FS.exists(FPSTR(getPresetsFileName()))) return;
DEBUG_PRINTLN(F("Preset file not found, attempting to load from EEPROM")); DEBUG_PRINTLN(F("Preset file not found, attempting to load from EEPROM"));
DEBUGFS_PRINTLN(F("Allocating saving buffer for dEEP")); DEBUGFS_PRINTLN(F("Allocating saving buffer for dEEP"));
@ -442,7 +442,7 @@ void deEEP() {
EEPROM.end(); EEPROM.end();
File f = WLED_FS.open("/presets.json", "w"); File f = WLED_FS.open(FPSTR(getPresetsFileName()), "w");
if (!f) { if (!f) {
errorFlag = ERR_FS_GENERAL; errorFlag = ERR_FS_GENERAL;
releaseJSONBufferLock(); releaseJSONBufferLock();

View File

@ -18,11 +18,6 @@ static const char s_unlock_ota [] PROGMEM = "Please unlock OTA in security setti
static const char s_unlock_cfg [] PROGMEM = "Please unlock settings using PIN code!"; static const char s_unlock_cfg [] PROGMEM = "Please unlock settings using PIN code!";
static const char s_notimplemented[] PROGMEM = "Not implemented"; static const char s_notimplemented[] PROGMEM = "Not implemented";
static const char s_accessdenied[] PROGMEM = "Access Denied"; static const char s_accessdenied[] PROGMEM = "Access Denied";
static const char s_javascript[] PROGMEM = "application/javascript";
static const char s_json[] PROGMEM = "application/json";
static const char s_html[] PROGMEM = "text/html";
static const char s_plain[] PROGMEM = "text/plain";
static const char s_css[] PROGMEM = "text/css";
//Is this an IP? //Is this an IP?
static bool isIp(String str) { static bool isIp(String str) {
@ -158,7 +153,7 @@ static String msgProcessor(const String& var)
static void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) { static void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) {
if (!correctPIN) { if (!correctPIN) {
if (final) request->send(401, FPSTR(s_plain), FPSTR(s_unlock_cfg)); if (final) request->send(401, FPSTR(CONTENT_TYPE_PLAIN), FPSTR(s_unlock_cfg));
return; return;
} }
if (!index) { if (!index) {
@ -170,7 +165,7 @@ static void handleUpload(AsyncWebServerRequest *request, const String& filename,
request->_tempFile = WLED_FS.open(finalname, "w"); request->_tempFile = WLED_FS.open(finalname, "w");
DEBUG_PRINT(F("Uploading ")); DEBUG_PRINT(F("Uploading "));
DEBUG_PRINTLN(finalname); DEBUG_PRINTLN(finalname);
if (finalname.equals(F("/presets.json"))) presetsModifiedTime = toki.second(); if (finalname.equals(FPSTR(getPresetsFileName()))) presetsModifiedTime = toki.second();
} }
if (len) { if (len) {
request->_tempFile.write(data,len); request->_tempFile.write(data,len);
@ -179,10 +174,10 @@ static void handleUpload(AsyncWebServerRequest *request, const String& filename,
request->_tempFile.close(); request->_tempFile.close();
if (filename.indexOf(F("cfg.json")) >= 0) { // check for filename with or without slash if (filename.indexOf(F("cfg.json")) >= 0) { // check for filename with or without slash
doReboot = true; doReboot = true;
request->send(200, FPSTR(s_plain), F("Configuration restore successful.\nRebooting...")); request->send(200, FPSTR(CONTENT_TYPE_PLAIN), F("Configuration restore successful.\nRebooting..."));
} else { } else {
if (filename.indexOf(F("palette")) >= 0 && filename.indexOf(F(".json")) >= 0) strip.loadCustomPalettes(); if (filename.indexOf(F("palette")) >= 0 && filename.indexOf(F(".json")) >= 0) strip.loadCustomPalettes();
request->send(200, FPSTR(s_plain), F("File Uploaded!")); request->send(200, FPSTR(CONTENT_TYPE_PLAIN), F("File Uploaded!"));
} }
cacheInvalidate++; cacheInvalidate++;
} }
@ -198,12 +193,12 @@ void createEditHandler(bool enable) {
editHandler = &server.addHandler(new SPIFFSEditor("","",WLED_FS));//http_username,http_password)); editHandler = &server.addHandler(new SPIFFSEditor("","",WLED_FS));//http_username,http_password));
#endif #endif
#else #else
editHandler = &server.on("/edit", HTTP_GET, [](AsyncWebServerRequest *request){ editHandler = &server.on(SET_F("/edit"), HTTP_GET, [](AsyncWebServerRequest *request){
serveMessage(request, 501, FPSTR(s_notimplemented), F("The FS editor is disabled in this build."), 254); serveMessage(request, 501, FPSTR(s_notimplemented), F("The FS editor is disabled in this build."), 254);
}); });
#endif #endif
} else { } else {
editHandler = &server.on("/edit", HTTP_ANY, [](AsyncWebServerRequest *request){ editHandler = &server.on(SET_F("/edit"), HTTP_ANY, [](AsyncWebServerRequest *request){
serveMessage(request, 401, FPSTR(s_accessdenied), FPSTR(s_unlock_cfg), 254); serveMessage(request, 401, FPSTR(s_accessdenied), FPSTR(s_unlock_cfg), 254);
}); });
} }
@ -212,9 +207,9 @@ void createEditHandler(bool enable) {
static bool captivePortal(AsyncWebServerRequest *request) static bool captivePortal(AsyncWebServerRequest *request)
{ {
if (!apActive) return false; //only serve captive in AP mode if (!apActive) return false; //only serve captive in AP mode
if (!request->hasHeader("Host")) return false; if (!request->hasHeader(F("Host"))) return false;
String hostH = request->getHeader("Host")->value(); String hostH = request->getHeader(F("Host"))->value();
if (!isIp(hostH) && hostH.indexOf(F("wled.me")) < 0 && hostH.indexOf(cmDNS) < 0 && hostH.indexOf(':') < 0) { if (!isIp(hostH) && hostH.indexOf(F("wled.me")) < 0 && hostH.indexOf(cmDNS) < 0 && hostH.indexOf(':') < 0) {
DEBUG_PRINTLN(F("Captive portal")); DEBUG_PRINTLN(F("Captive portal"));
AsyncWebServerResponse *response = request->beginResponse(302); AsyncWebServerResponse *response = request->beginResponse(302);
@ -234,54 +229,57 @@ void initServer()
#ifdef WLED_ENABLE_WEBSOCKETS #ifdef WLED_ENABLE_WEBSOCKETS
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
server.on("/liveview2D", HTTP_GET, [](AsyncWebServerRequest *request) { server.on(F("/liveview2D"), HTTP_GET, [](AsyncWebServerRequest *request) {
handleStaticContent(request, "", 200, FPSTR(s_html), PAGE_liveviewws2D, PAGE_liveviewws2D_length); handleStaticContent(request, "", 200, FPSTR(CONTENT_TYPE_HTML), PAGE_liveviewws2D, PAGE_liveviewws2D_length);
}); });
#endif #endif
#endif #endif
server.on("/liveview", HTTP_GET, [](AsyncWebServerRequest *request) { server.on(F("/liveview"), HTTP_GET, [](AsyncWebServerRequest *request) {
handleStaticContent(request, "", 200, FPSTR(s_html), PAGE_liveview, PAGE_liveview_length); handleStaticContent(request, "", 200, FPSTR(CONTENT_TYPE_HTML), PAGE_liveview, PAGE_liveview_length);
}); });
//settings page //settings page
server.on("/settings", HTTP_GET, [](AsyncWebServerRequest *request){ server.on(F("/settings"), HTTP_GET, [](AsyncWebServerRequest *request){
serveSettings(request); serveSettings(request);
}); });
// "/settings/settings.js&p=x" request also handled by serveSettings() // "/settings/settings.js&p=x" request also handled by serveSettings()
static const char _style_css[] PROGMEM = "/style.css";
server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request) { server.on(_style_css, HTTP_GET, [](AsyncWebServerRequest *request) {
handleStaticContent(request, "/style.css", 200, FPSTR(s_css), PAGE_settingsCss, PAGE_settingsCss_length); handleStaticContent(request, FPSTR(_style_css), 200, FPSTR(CONTENT_TYPE_CSS), PAGE_settingsCss, PAGE_settingsCss_length);
}); });
server.on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest *request) { static const char _favicon_ico[] PROGMEM = "/favicon.ico";
handleStaticContent(request, "/favicon.ico", 200, "image/x-icon", favicon, favicon_length, false); server.on(_favicon_ico, HTTP_GET, [](AsyncWebServerRequest *request) {
handleStaticContent(request, FPSTR(_favicon_ico), 200, F("image/x-icon"), favicon, favicon_length, false);
}); });
server.on("/skin.css", HTTP_GET, [](AsyncWebServerRequest *request) { static const char _skin_css[] PROGMEM = "/skin.css";
if (handleFileRead(request, "/skin.css")) return; server.on(_skin_css, HTTP_GET, [](AsyncWebServerRequest *request) {
AsyncWebServerResponse *response = request->beginResponse(200, FPSTR(s_css)); if (handleFileRead(request, FPSTR(_skin_css))) return;
AsyncWebServerResponse *response = request->beginResponse(200, FPSTR(CONTENT_TYPE_CSS));
request->send(response); request->send(response);
}); });
server.on("/welcome", HTTP_GET, [](AsyncWebServerRequest *request){ server.on(F("/welcome"), HTTP_GET, [](AsyncWebServerRequest *request){
serveSettings(request); serveSettings(request);
}); });
server.on("/reset", HTTP_GET, [](AsyncWebServerRequest *request){ server.on(F("/reset"), HTTP_GET, [](AsyncWebServerRequest *request){
serveMessage(request, 200,F("Rebooting now..."),F("Please wait ~10 seconds..."),129); serveMessage(request, 200,F("Rebooting now..."),F("Please wait ~10 seconds..."),129);
doReboot = true; doReboot = true;
}); });
server.on("/settings", HTTP_POST, [](AsyncWebServerRequest *request){ server.on(F("/settings"), HTTP_POST, [](AsyncWebServerRequest *request){
serveSettings(request, true); serveSettings(request, true);
}); });
server.on("/json", HTTP_GET, [](AsyncWebServerRequest *request){ const static char _json[] PROGMEM = "/json";
server.on(FPSTR(_json), HTTP_GET, [](AsyncWebServerRequest *request){
serveJson(request); serveJson(request);
}); });
AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/json", [](AsyncWebServerRequest *request) { AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler(FPSTR(_json), [](AsyncWebServerRequest *request) {
bool verboseResponse = false; bool verboseResponse = false;
bool isConfig = false; bool isConfig = false;
@ -300,7 +298,7 @@ void initServer()
if (root.containsKey("pin")) checkSettingsPIN(root["pin"].as<const char*>()); if (root.containsKey("pin")) checkSettingsPIN(root["pin"].as<const char*>());
const String& url = request->url(); const String& url = request->url();
isConfig = url.indexOf("cfg") > -1; isConfig = url.indexOf(F("cfg")) > -1;
if (!isConfig) { if (!isConfig) {
/* /*
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
@ -329,49 +327,50 @@ void initServer()
doSerializeConfig = true; //serializeConfig(); //Save new settings to FS doSerializeConfig = true; //serializeConfig(); //Save new settings to FS
} }
} }
request->send(200, FPSTR(s_json), F("{\"success\":true}")); request->send(200, CONTENT_TYPE_JSON, F("{\"success\":true}"));
}, JSON_BUFFER_SIZE); }, JSON_BUFFER_SIZE);
server.addHandler(handler); server.addHandler(handler);
server.on("/version", HTTP_GET, [](AsyncWebServerRequest *request){ server.on(F("/version"), HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, FPSTR(s_plain), (String)VERSION); request->send(200, FPSTR(CONTENT_TYPE_PLAIN), (String)VERSION);
}); });
server.on("/uptime", HTTP_GET, [](AsyncWebServerRequest *request){ server.on(F("/uptime"), HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, FPSTR(s_plain), (String)millis()); request->send(200, FPSTR(CONTENT_TYPE_PLAIN), (String)millis());
}); });
server.on("/freeheap", HTTP_GET, [](AsyncWebServerRequest *request){ server.on(F("/freeheap"), HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, FPSTR(s_plain), (String)ESP.getFreeHeap()); request->send(200, FPSTR(CONTENT_TYPE_PLAIN), (String)ESP.getFreeHeap());
}); });
#ifdef WLED_ENABLE_USERMOD_PAGE #ifdef WLED_ENABLE_USERMOD_PAGE
server.on("/u", HTTP_GET, [](AsyncWebServerRequest *request) { server.on("/u", HTTP_GET, [](AsyncWebServerRequest *request) {
handleStaticContent(request, "", 200, FPSTR(s_html), PAGE_usermod, PAGE_usermod_length); handleStaticContent(request, "", 200, FPSTR(CONTENT_TYPE_HTML), PAGE_usermod, PAGE_usermod_length);
}); });
#endif #endif
server.on("/teapot", HTTP_GET, [](AsyncWebServerRequest *request){ server.on(F("/teapot"), HTTP_GET, [](AsyncWebServerRequest *request){
serveMessage(request, 418, F("418. I'm a teapot."), F("(Tangible Embedded Advanced Project Of Twinkling)"), 254); serveMessage(request, 418, F("418. I'm a teapot."), F("(Tangible Embedded Advanced Project Of Twinkling)"), 254);
}); });
server.on("/upload", HTTP_POST, [](AsyncWebServerRequest *request) {}, server.on(F("/upload"), HTTP_POST, [](AsyncWebServerRequest *request) {},
[](AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, [](AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data,
size_t len, bool final) {handleUpload(request, filename, index, data, len, final);} size_t len, bool final) {handleUpload(request, filename, index, data, len, final);}
); );
createEditHandler(correctPIN); createEditHandler(correctPIN);
static const char _update[] PROGMEM = "/update";
#ifndef WLED_DISABLE_OTA #ifndef WLED_DISABLE_OTA
//init ota page //init ota page
server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){ server.on(_update, HTTP_GET, [](AsyncWebServerRequest *request){
if (otaLock) { if (otaLock) {
serveMessage(request, 401, FPSTR(s_accessdenied), FPSTR(s_unlock_ota), 254); serveMessage(request, 401, FPSTR(s_accessdenied), FPSTR(s_unlock_ota), 254);
} else } else
serveSettings(request); // checks for "upd" in URL and handles PIN serveSettings(request); // checks for "upd" in URL and handles PIN
}); });
server.on("/update", HTTP_POST, [](AsyncWebServerRequest *request){ server.on(_update, HTTP_POST, [](AsyncWebServerRequest *request){
if (!correctPIN) { if (!correctPIN) {
serveSettings(request, true); // handle PIN page POST request serveSettings(request, true); // handle PIN page POST request
return; return;
@ -417,18 +416,18 @@ void initServer()
} }
}); });
#else #else
server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){ server.on(_update, HTTP_GET, [](AsyncWebServerRequest *request){
serveMessage(request, 501, FPSTR(s_notimplemented), F("OTA updating is disabled in this build."), 254); serveMessage(request, 501, FPSTR(s_notimplemented), F("OTA updating is disabled in this build."), 254);
}); });
#endif #endif
#ifdef WLED_ENABLE_DMX #ifdef WLED_ENABLE_DMX
server.on("/dmxmap", HTTP_GET, [](AsyncWebServerRequest *request){ server.on(SET_F("/dmxmap"), HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, FPSTR(s_html), PAGE_dmxmap , dmxProcessor); request->send_P(200, FPSTR(CONTENT_TYPE_HTML), PAGE_dmxmap , dmxProcessor);
}); });
#else #else
server.on("/dmxmap", HTTP_GET, [](AsyncWebServerRequest *request){ server.on(SET_F("/dmxmap"), HTTP_GET, [](AsyncWebServerRequest *request){
serveMessage(request, 501, FPSTR(s_notimplemented), F("DMX support is not enabled in this build."), 254); serveMessage(request, 501, FPSTR(s_notimplemented), F("DMX support is not enabled in this build."), 254);
}); });
#endif #endif
@ -436,26 +435,29 @@ void initServer()
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
if (captivePortal(request)) return; if (captivePortal(request)) return;
if (!showWelcomePage || request->hasArg(F("sliders"))) { if (!showWelcomePage || request->hasArg(F("sliders"))) {
handleStaticContent(request, "/index.htm", 200, FPSTR(s_html), PAGE_index, PAGE_index_L); handleStaticContent(request, F("/index.htm"), 200, FPSTR(CONTENT_TYPE_HTML), PAGE_index, PAGE_index_L);
} else { } else {
serveSettings(request); serveSettings(request);
} }
}); });
#ifdef WLED_ENABLE_PIXART #ifdef WLED_ENABLE_PIXART
server.on("/pixart.htm", HTTP_GET, [](AsyncWebServerRequest *request) { static const char _pixart_htm[] PROGMEM = "/pixart.htm";
handleStaticContent(request, "/pixart.htm", 200, FPSTR(s_html), PAGE_pixart, PAGE_pixart_L); server.on(_pixart_htm, HTTP_GET, [](AsyncWebServerRequest *request) {
handleStaticContent(request, FPSTR(_pixart_htm), 200, FPSTR(CONTENT_TYPE_HTML), PAGE_pixart, PAGE_pixart_L);
}); });
#endif #endif
#ifndef WLED_DISABLE_PXMAGIC #ifndef WLED_DISABLE_PXMAGIC
server.on("/pxmagic.htm", HTTP_GET, [](AsyncWebServerRequest *request) { static const char _pxmagic_htm[] PROGMEM = "/pxmagic.htm";
handleStaticContent(request, "/pxmagic.htm", 200, FPSTR(s_html), PAGE_pxmagic, PAGE_pxmagic_L); server.on(_pxmagic_htm, HTTP_GET, [](AsyncWebServerRequest *request) {
handleStaticContent(request, FPSTR(_pxmagic_htm), 200, FPSTR(CONTENT_TYPE_HTML), PAGE_pxmagic, PAGE_pxmagic_L);
}); });
#endif #endif
server.on("/cpal.htm", HTTP_GET, [](AsyncWebServerRequest *request) { static const char _cpal_htm[] PROGMEM = "/cpal.htm";
handleStaticContent(request, "/cpal.htm", 200, FPSTR(s_html), PAGE_cpal, PAGE_cpal_L); server.on(_cpal_htm, HTTP_GET, [](AsyncWebServerRequest *request) {
handleStaticContent(request, FPSTR(_cpal_htm), 200, FPSTR(CONTENT_TYPE_HTML), PAGE_cpal, PAGE_cpal_L);
}); });
#ifdef WLED_ENABLE_WEBSOCKETS #ifdef WLED_ENABLE_WEBSOCKETS
@ -480,7 +482,7 @@ void initServer()
#ifndef WLED_DISABLE_ALEXA #ifndef WLED_DISABLE_ALEXA
if(espalexa.handleAlexaApiCall(request)) return; if(espalexa.handleAlexaApiCall(request)) return;
#endif #endif
handleStaticContent(request, request->url(), 404, FPSTR(s_html), PAGE_404, PAGE_404_length); handleStaticContent(request, request->url(), 404, FPSTR(CONTENT_TYPE_HTML), PAGE_404, PAGE_404_length);
}); });
} }
@ -491,7 +493,7 @@ void serveMessage(AsyncWebServerRequest* request, uint16_t code, const String& h
messageSub = subl; messageSub = subl;
optionType = optionT; optionType = optionT;
request->send_P(code, FPSTR(s_html), PAGE_msg, msgProcessor); request->send_P(code, FPSTR(CONTENT_TYPE_HTML), PAGE_msg, msgProcessor);
} }
@ -499,7 +501,7 @@ void serveJsonError(AsyncWebServerRequest* request, uint16_t code, uint16_t erro
{ {
AsyncJsonResponse *response = new AsyncJsonResponse(64); AsyncJsonResponse *response = new AsyncJsonResponse(64);
if (error < ERR_NOT_IMPL) response->addHeader(F("Retry-After"), F("1")); if (error < ERR_NOT_IMPL) response->addHeader(F("Retry-After"), F("1"));
response->setContentType(FPSTR(s_json)); response->setContentType(CONTENT_TYPE_JSON);
response->setCode(code); response->setCode(code);
JsonObject obj = response->getRoot(); JsonObject obj = response->getRoot();
obj[F("error")] = error; obj[F("error")] = error;
@ -515,12 +517,12 @@ void serveSettingsJS(AsyncWebServerRequest* request)
byte subPage = request->arg(F("p")).toInt(); byte subPage = request->arg(F("p")).toInt();
if (subPage > 10) { if (subPage > 10) {
strcpy_P(buf, PSTR("alert('Settings for this request are not implemented.');")); strcpy_P(buf, PSTR("alert('Settings for this request are not implemented.');"));
request->send(501, FPSTR(s_javascript), buf); request->send(501, FPSTR(CONTENT_TYPE_JAVASCRIPT), buf);
return; return;
} }
if (subPage > 0 && !correctPIN && strlen(settingsPIN)>0) { if (subPage > 0 && !correctPIN && strlen(settingsPIN)>0) {
strcpy_P(buf, PSTR("alert('PIN incorrect.');")); strcpy_P(buf, PSTR("alert('PIN incorrect.');"));
request->send(401, FPSTR(s_javascript), buf); request->send(401, FPSTR(CONTENT_TYPE_JAVASCRIPT), buf);
return; return;
} }
strcat_P(buf,PSTR("function GetV(){var d=document;")); strcat_P(buf,PSTR("function GetV(){var d=document;"));
@ -528,7 +530,7 @@ void serveSettingsJS(AsyncWebServerRequest* request)
strcat_P(buf,PSTR("}")); strcat_P(buf,PSTR("}"));
AsyncWebServerResponse *response; AsyncWebServerResponse *response;
response = request->beginResponse(200, FPSTR(s_javascript), buf); response = request->beginResponse(200, FPSTR(CONTENT_TYPE_JAVASCRIPT), buf);
response->addHeader(F("Cache-Control"), F("no-store")); response->addHeader(F("Cache-Control"), F("no-store"));
response->addHeader(F("Expires"), F("0")); response->addHeader(F("Expires"), F("0"));
request->send(response); request->send(response);
@ -609,7 +611,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post) {
} }
int code = 200; int code = 200;
String contentType = FPSTR(s_html); String contentType = FPSTR(CONTENT_TYPE_HTML);
const uint8_t* content; const uint8_t* content;
size_t len; size_t len;
@ -635,7 +637,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post) {
return; return;
} }
case SUBPAGE_PINREQ : content = PAGE_settings_pin; len = PAGE_settings_pin_length; code = 401; break; case SUBPAGE_PINREQ : content = PAGE_settings_pin; len = PAGE_settings_pin_length; code = 401; break;
case SUBPAGE_CSS : content = PAGE_settingsCss; len = PAGE_settingsCss_length; contentType = FPSTR(s_css); break; case SUBPAGE_CSS : content = PAGE_settingsCss; len = PAGE_settingsCss_length; contentType = FPSTR(CONTENT_TYPE_CSS); break;
case SUBPAGE_JS : serveSettingsJS(request); return; case SUBPAGE_JS : serveSettingsJS(request); return;
case SUBPAGE_WELCOME : content = PAGE_welcome; len = PAGE_welcome_length; break; case SUBPAGE_WELCOME : content = PAGE_welcome; len = PAGE_welcome_length; break;
default: content = PAGE_settings; len = PAGE_settings_length; break; default: content = PAGE_settings; len = PAGE_settings_length; break;

View File

@ -55,7 +55,7 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
} else { } else {
verboseResponse = deserializeState(root); verboseResponse = deserializeState(root);
} }
releaseJSONBufferLock(); // will clean fileDoc releaseJSONBufferLock();
if (!interfaceUpdateCallMode) { // individual client response only needed if no WS broadcast soon if (!interfaceUpdateCallMode) { // individual client response only needed if no WS broadcast soon
if (verboseResponse) { if (verboseResponse) {
@ -102,7 +102,6 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
void sendDataWs(AsyncWebSocketClient * client) void sendDataWs(AsyncWebSocketClient * client)
{ {
if (!ws.count()) return; if (!ws.count()) return;
AsyncWebSocketMessageBuffer * buffer;
if (!requestJSONBufferLock(12)) { if (!requestJSONBufferLock(12)) {
if (client) { if (client) {
@ -129,7 +128,7 @@ void sendDataWs(AsyncWebSocketClient * client)
return; return;
} }
#endif #endif
buffer = ws.makeBuffer(len); // will not allocate correct memory sometimes on ESP8266 AsyncWebSocketBuffer buffer(len);
#ifdef ESP8266 #ifdef ESP8266
size_t heap2 = ESP.getFreeHeap(); size_t heap2 = ESP.getFreeHeap();
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap()); DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
@ -141,23 +140,18 @@ void sendDataWs(AsyncWebSocketClient * client)
DEBUG_PRINTLN(F("WS buffer allocation failed.")); DEBUG_PRINTLN(F("WS buffer allocation failed."));
ws.closeAll(1013); //code 1013 = temporary overload, try again later ws.closeAll(1013); //code 1013 = temporary overload, try again later
ws.cleanupClients(0); //disconnect all clients to release memory ws.cleanupClients(0); //disconnect all clients to release memory
ws._cleanBuffers();
return; //out of memory return; //out of memory
} }
serializeJson(*pDoc, (char *)buffer.data(), len);
buffer->lock();
serializeJson(*pDoc, (char *)buffer->get(), len);
DEBUG_PRINT(F("Sending WS data ")); DEBUG_PRINT(F("Sending WS data "));
if (client) { if (client) {
client->text(buffer); client->text(std::move(buffer));
DEBUG_PRINTLN(F("to a single client.")); DEBUG_PRINTLN(F("to a single client."));
} else { } else {
ws.textAll(buffer); ws.textAll(std::move(buffer));
DEBUG_PRINTLN(F("to multiple clients.")); DEBUG_PRINTLN(F("to multiple clients."));
} }
buffer->unlock();
ws._cleanBuffers();
releaseJSONBufferLock(); releaseJSONBufferLock();
} }
@ -187,11 +181,10 @@ bool sendLiveLedsWs(uint32_t wsClient)
#endif #endif
size_t bufSize = pos + (used/n)*3; size_t bufSize = pos + (used/n)*3;
AsyncWebSocketMessageBuffer * wsBuf = ws.makeBuffer(bufSize); AsyncWebSocketBuffer wsBuf(bufSize);
if (!wsBuf) return false; //out of memory if (!wsBuf) return false; //out of memory
uint8_t* buffer = wsBuf->get(); uint8_t* buffer = reinterpret_cast<uint8_t*>(wsBuf.data());
if (!buffer) return false; //out of memory if (!buffer) return false; //out of memory
wsBuf->lock(); // protect buffer from being cleaned by another WS instance
buffer[0] = 'L'; buffer[0] = 'L';
buffer[1] = 1; //version buffer[1] = 1; //version
@ -218,9 +211,7 @@ bool sendLiveLedsWs(uint32_t wsClient)
buffer[pos++] = scale8(qadd8(w, b), strip.getBrightness()); //B buffer[pos++] = scale8(qadd8(w, b), strip.getBrightness()); //B
} }
wsc->binary(wsBuf); wsc->binary(std::move(wsBuf));
wsBuf->unlock(); // un-protect buffer
ws._cleanBuffers();
return true; return true;
} }

25
wled00/xml.cpp Executable file → Normal file
View File

@ -145,10 +145,13 @@ void appendGPIOinfo() {
oappend(SET_F("d.rsvd=[22,23,24,25,26,27,28,29,30,31,32")); oappend(SET_F("d.rsvd=[22,23,24,25,26,27,28,29,30,31,32"));
#elif defined(CONFIG_IDF_TARGET_ESP32S3) #elif defined(CONFIG_IDF_TARGET_ESP32S3)
oappend(SET_F("d.rsvd=[19,20,22,23,24,25,26,27,28,29,30,31,32")); // includes 19+20 for USB OTG (JTAG) oappend(SET_F("d.rsvd=[19,20,22,23,24,25,26,27,28,29,30,31,32")); // includes 19+20 for USB OTG (JTAG)
if (psramFound()) oappend(SET_F(",33,34,35,36,37")); // in use for "octal" PSRAM or "octal" FLASH -seems that octal PSRAM is very common on S3.
#elif defined(CONFIG_IDF_TARGET_ESP32C3) #elif defined(CONFIG_IDF_TARGET_ESP32C3)
oappend(SET_F("d.rsvd=[11,12,13,14,15,16,17")); oappend(SET_F("d.rsvd=[11,12,13,14,15,16,17"));
#elif defined(ESP32) #elif defined(ESP32)
oappend(SET_F("d.rsvd=[6,7,8,9,10,11,24,28,29,30,31,37,38")); oappend(SET_F("d.rsvd=[6,7,8,9,10,11,24,28,29,30,31,37,38"));
if (!pinManager.isPinOk(16,false)) oappend(SET_F(",16")); // covers PICO & WROVER
if (!pinManager.isPinOk(17,false)) oappend(SET_F(",17")); // covers PICO & WROVER
#else #else
oappend(SET_F("d.rsvd=[6,7,8,9,10,11")); oappend(SET_F("d.rsvd=[6,7,8,9,10,11"));
#endif #endif
@ -163,14 +166,6 @@ void appendGPIOinfo() {
//Note: Using pin 3 (RX) disables Adalight / Serial JSON //Note: Using pin 3 (RX) disables Adalight / Serial JSON
#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM)
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3) && !defined(CONFIG_IDF_TARGET_ESP32C3)
if (psramFound()) oappend(SET_F(",16,17")); // GPIO16 & GPIO17 reserved for SPI RAM on ESP32 (not on S2, S3 or C3)
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
if (psramFound()) oappend(SET_F(",33,34,35,36,37")); // in use for "octal" PSRAM or "octal" FLASH -seems that octal PSRAM is very common on S3.
#endif
#endif
#ifdef WLED_USE_ETHERNET #ifdef WLED_USE_ETHERNET
if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) { if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) {
for (uint8_t p=0; p<WLED_ETH_RSVD_PINS_COUNT; p++) { oappend(","); oappend(itoa(esp32_nonconfigurable_ethernet_pins[p].pin,nS,10)); } for (uint8_t p=0; p<WLED_ETH_RSVD_PINS_COUNT; p++) { oappend(","); oappend(itoa(esp32_nonconfigurable_ethernet_pins[p].pin,nS,10)); }
@ -360,6 +355,7 @@ void getSettingsJS(byte subPage, char* dest)
sappend('c',SET_F("MS"),autoSegments); sappend('c',SET_F("MS"),autoSegments);
sappend('c',SET_F("CCT"),correctWB); sappend('c',SET_F("CCT"),correctWB);
sappend('c',SET_F("IC"),cctICused);
sappend('c',SET_F("CR"),cctFromRgb); sappend('c',SET_F("CR"),cctFromRgb);
sappend('v',SET_F("CB"),strip.cctBlending); sappend('v',SET_F("CB"),strip.cctBlending);
sappend('v',SET_F("FR"),strip.getTargetFps()); sappend('v',SET_F("FR"),strip.getTargetFps());
@ -402,12 +398,12 @@ void getSettingsJS(byte subPage, char* dest)
uint16_t speed = bus->getFrequency(); uint16_t speed = bus->getFrequency();
if (IS_PWM(bus->getType())) { if (IS_PWM(bus->getType())) {
switch (speed) { switch (speed) {
case WLED_PWM_FREQ/3 : speed = 0; break; case WLED_PWM_FREQ/2 : speed = 0; break;
case WLED_PWM_FREQ/2 : speed = 1; break; case WLED_PWM_FREQ*2/3 : speed = 1; break;
default: default:
case WLED_PWM_FREQ : speed = 2; break; case WLED_PWM_FREQ : speed = 2; break;
case WLED_PWM_FREQ*4/3 : speed = 3; break; case WLED_PWM_FREQ*2 : speed = 3; break;
case WLED_PWM_FREQ*2 : speed = 4; break; case WLED_PWM_FREQ*10/3 : speed = 4; break; // uint16_t max (19531 * 3.333)
} }
} else if (IS_DIGITAL(bus->getType()) && IS_2PIN(bus->getType())) { } else if (IS_DIGITAL(bus->getType()) && IS_2PIN(bus->getType())) {
switch (speed) { switch (speed) {
@ -425,6 +421,7 @@ void getSettingsJS(byte subPage, char* dest)
sumMa += bus->getMaxCurrent(); sumMa += bus->getMaxCurrent();
} }
sappend('v',SET_F("MA"),BusManager::ablMilliampsMax() ? BusManager::ablMilliampsMax() : sumMa); sappend('v',SET_F("MA"),BusManager::ablMilliampsMax() ? BusManager::ablMilliampsMax() : sumMa);
sappend('c',SET_F("ABL"),BusManager::ablMilliampsMax() || sumMa > 0);
sappend('c',SET_F("PPL"),!BusManager::ablMilliampsMax() && sumMa > 0); sappend('c',SET_F("PPL"),!BusManager::ablMilliampsMax() && sumMa > 0);
oappend(SET_F("resetCOM(")); oappend(SET_F("resetCOM("));
@ -470,8 +467,10 @@ void getSettingsJS(byte subPage, char* dest)
} }
sappend('c',SET_F("IP"),disablePullUp); sappend('c',SET_F("IP"),disablePullUp);
sappend('v',SET_F("TT"),touchThreshold); sappend('v',SET_F("TT"),touchThreshold);
#ifndef WLED_DISABLE_INFRARED
sappend('v',SET_F("IR"),irPin); sappend('v',SET_F("IR"),irPin);
sappend('v',SET_F("IT"),irEnabled); sappend('v',SET_F("IT"),irEnabled);
#endif
sappend('c',SET_F("MSO"),!irApplyToAllSelected); sappend('c',SET_F("MSO"),!irApplyToAllSelected);
} }