diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 0ac97d66..5c87f432 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,2 +1,2 @@ -github: [Aircoookie,MoonModules] -custom: ['https://paypal.me/Aircoookie','https://paypal.me/ewowi'] +github: [] +custom: ['https://www.paypal.com/donate?business=moonmodules@icloud.com'] diff --git a/.gitignore b/.gitignore index bb02e36e..789de0a9 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,5 @@ node_modules wled-update.sh esp01-update.sh /wled00/LittleFS -replace_fs.py \ No newline at end of file +replace_fs.py +wled00/wled00.ino.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index c99fe417..69129061 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,91 @@ ## WLED changelog +#### Build 2303240 +- Peek scaling of large 2D matrices +- Added 0D (1 pixel) metadata for effects & enhance 0D (analog strip) UI handling +- Added ability to disable ADAlight (-D WLED_DISABLE_ADALIGHT) +- Fixed APA102 output on Ethernet enabled controllers +- Added ArtNet virtual/network output (#3121) +- Klipper usermod (#3106) +- Remove DST from CST timezone +- various fixes and enhancements + +#### Build 2302180 + +- Removed Blynk support (servers shut down on 31st Dec 2022) +- Added `ledgap.json` to complement ledmaps for 2D matrices +- Added support for white addressable strips (#3073) +- Ability to use SHT temperature usermod with PWM fan usermod +- Added `onStateChange()` callback to usermods (#3081) +- Refactored `bus_manager` [internal] +- Dual 1D & 2D mode (add 1D strip after the matrix) +- Removed 1D -> 2D mapping for individual pixel control +- effect tweak: Fireworks 1D +- various bugfixes + +#### Build 2301240 + +- Version bump to v0.14.0-b2 "Hoshi" +- PixelArt converter (convert any image to pixel art and display it on a matrix) (PR #3042) +- various effect updates and optimisations + - added Overlay option to some effects (allows overlapping segments) + - added gradient text on Scrolling Text + - added #DDMM, #MMDD & #HHMM date and time options for Scrolling Text effect (PR #2990) + - deprecated: Dynamic Smooth, Dissolve Rnd, Solid Glitter + - optimised & enhanced loading of default values + - new effect: Distortion Waves (2D) + - 2D support for Ripple effect + - slower minimum speed for Railway effect +- DMX effect mode & segment controls (PR #2891) +- Optimisations for conditional compiles (further reduction of code size) +- better UX with effect sliders (PR #3012) +- enhanced support for ESP32 variants: C3, S2 & S3 +- usermod enhancements (PIR, Temperature, Battery (PR #2975), Analog Clock (PR #2993)) +- new usermod SHT (PR #2963) +- 2D matrix set up with gaps or irregular panels (breaking change!) (PR #2892) +- palette blending/transitions +- random palette smooth changes +- hex color notations in custom palettes +- allow more virtual buses +- plethora of bugfixes + ### WLED release 0.14.0-b1 #### Build 2212222 - Version bump to v0.14.0-b1 "Hoshi" -- Full changelog TBD +- 2D matrix support (including mapping 1D effects to 2D and 2D peek) +- [internal] completely rewritten Segment & WS2812FX handling code +- [internal] ability to add custom effects via usermods +- [internal] set of 2D drawing functions +- transitions on every segment (including ESP8266) +- enhanced old and new 2D effects (metadata: default values) +- custom palettes (up to 10; upload palette0.json, palette1.json, ...) +- custom effect sliders and options, quick filters +- global I2C and SPI GPIO allocation (for usermods) +- usermod settings page enhancements (dropdown & info) +- asynchronous preset loading (and added "pd" JSON API call for direct preset apply) +- new usermod Boblight (PR #2917) +- new usermod PWM Outputs (PR #2912) +- new usermod Audioreactive +- new usermod Word Clock Matrix (PR #2743) +- new usermod Ping Pong Clock (PR #2746) +- new usermod ADS1115 (PR #2752) +- new usermod Analog Clock (PR #2736) +- various usermod enhancements and updates +- allow disabling pull-up resistors on buttons +- SD card support (PR #2877) +- enhanced HTTP API to support custom effect sliders & options (X1, X2, X3, M1, M2, M3) +- multiple UDP sync message retries (PR #2830) +- network debug printer (PR #2870) +- automatic UI PC mode on large displays +- removed support for upgrading from pre-0.10 (EEPROM) +- support for setting GPIO level when LEDs are off (RMT idle level, ESP32 only) (PR #2478) +- Pakistan time-zone (PKT) +- ArtPoll support +- TM1829 LED support +- experimental support for ESP32 S2, S3 and C3 +- general improvements and bugfixes ### WLED release 0.13.3 diff --git a/boards/esp32_16MB-poe.json b/boards/esp32_16MB-poe.json new file mode 100644 index 00000000..a15ef9a3 --- /dev/null +++ b/boards/esp32_16MB-poe.json @@ -0,0 +1,38 @@ +{ +"build": { + "arduino": { + "ldscript": "esp32_out.ld" + }, + "core": "esp32", + "extra_flags": "-DARDUINO_ESP32_DEV -DARDUINO_ESP32_POE", + "f_cpu": "240000000L", + "f_flash": "40000000L", + "flash_mode": "dio", + "mcu": "esp32", + "variant": "esp32-poe", + "partitions": "partitions_16M.csv" +}, +"connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" +], +"debug": { + "openocd_board": "esp-wroom-32.cfg" +}, +"frameworks": [ + "arduino", + "espidf" +], +"name": "ESP32 16MB", +"upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "require_upload_port": true, + "speed": 2000000 +}, +"url": "https://en.wikipedia.org/wiki/ESP32", +"vendor": "Multiple" +} diff --git a/package-lock.json b/package-lock.json index 4d94a501..0f71186a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "wled", - "version": "0.14.0-b1.16", + "version": "0.14.0-b15.22", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "wled", - "version": "0.14.0-b1.16", + "version": "0.14.0-b15.22", "license": "ISC", "dependencies": { "clean-css": "^4.2.3", @@ -2939,24 +2939,6 @@ } }, "dependencies": { - "@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" - }, - "@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "requires": { - "defer-to-connect": "^1.0.1" - } - }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" - }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -2983,39 +2965,6 @@ "repeat-string": "^1.5.2" } }, - "ansi-align": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", - "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", - "requires": { - "string-width": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, "ansi-escapes": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", @@ -3032,9 +2981,9 @@ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" }, "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "requires": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -3082,9 +3031,9 @@ "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" }, "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "bcrypt-pbkdf": { "version": "1.0.2", @@ -3095,68 +3044,15 @@ } }, "binary-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" }, "boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" }, - "boxen": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", - "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", - "requires": { - "ansi-align": "^3.0.0", - "camelcase": "^5.3.1", - "chalk": "^3.0.0", - "cli-boxes": "^2.2.0", - "string-width": "^4.1.0", - "term-size": "^2.1.0", - "type-fest": "^0.8.1", - "widest-line": "^3.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3179,35 +3075,6 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, - "cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "dependencies": { - "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", - "requires": { - "pump": "^3.0.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" - } - } - }, "camelcase": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", @@ -3257,25 +3124,20 @@ } }, "chokidar": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz", - "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "requires": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.1.2", - "glob-parent": "~5.1.0", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.4.0" + "readdirp": "~3.6.0" } }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" - }, "clap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", @@ -3299,11 +3161,6 @@ } } }, - "cli-boxes": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz", - "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==" - }, "cliui": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", @@ -3314,14 +3171,6 @@ "wordwrap": "0.0.2" } }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "requires": { - "mimic-response": "^1.0.0" - } - }, "coa": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", @@ -3330,19 +3179,6 @@ "q": "^1.1.2" } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, "colors": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", @@ -3364,7 +3200,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "configstore": { "version": "1.4.0", @@ -3393,11 +3229,6 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, - "crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" - }, "css-select": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.0.0.tgz", @@ -3444,24 +3275,11 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "requires": { - "mimic-response": "^1.0.0" - } - }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" }, - "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" - }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -3525,19 +3343,6 @@ } } }, - "dot-prop": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", - "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", - "requires": { - "is-obj": "^2.0.0" - } - }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" - }, "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -3592,11 +3397,6 @@ "safer-buffer": "^2.1.0" } }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -3615,11 +3415,6 @@ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-2.3.0.tgz", "integrity": "sha1-lu258v2wGZWCKyY92KratnSBgbw=" }, - "escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==" - }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -3674,19 +3469,11 @@ } }, "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "optional": true }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "requires": { - "pump": "^3.0.0" - } - }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -3703,14 +3490,6 @@ "is-glob": "^4.0.1" } }, - "global-dirs": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", - "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", - "requires": { - "ini": "^1.3.5" - } - }, "got": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/got/-/got-3.3.1.tgz", @@ -3765,12 +3544,7 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "has-yarn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" }, "he": { "version": "1.2.0", @@ -3844,11 +3618,6 @@ } } }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" - }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -3870,12 +3639,7 @@ "ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=" - }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==" }, "imurmurhash": { "version": "0.1.4", @@ -3936,46 +3700,24 @@ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "requires": { - "ci-info": "^2.0.0" - } - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" }, "is-finite": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==" }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "requires": { "is-extglob": "^2.1.1" } }, - "is-installed-globally": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", - "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", - "requires": { - "global-dirs": "^2.0.1", - "is-path-inside": "^3.0.1" - } - }, "is-npm": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", @@ -3986,16 +3728,6 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" - }, - "is-path-inside": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", - "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==" - }, "is-redirect": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", @@ -4011,11 +3743,6 @@ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, - "is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" - }, "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", @@ -4045,11 +3772,6 @@ "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-1.6.0.tgz", "integrity": "sha512-xYuhvQ7I9PDJIGBWev9xm0+SMSed3ZDBAmvVjbFR1ZRLAF+vlXcQu6cRI9uAlj81rzikElRVteehwV7DuX2ZmQ==" }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" - }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -4076,14 +3798,6 @@ "verror": "1.10.0" } }, - "keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "requires": { - "json-buffer": "3.0.0" - } - }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -4227,21 +3941,6 @@ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -4260,15 +3959,10 @@ "mime-db": "1.44.0" } }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" - }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "requires": { "brace-expansion": "^1.1.7" } @@ -4300,152 +3994,34 @@ } }, "nodemon": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.4.tgz", - "integrity": "sha512-Ltced+hIfTmaS28Zjv1BM552oQ3dbwPqI4+zI0SLgq+wpJhSyqgYude/aZa/3i31VCQWMfXJVxvu86abcam3uQ==", + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz", + "integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==", "requires": { - "chokidar": "^3.2.2", - "debug": "^3.2.6", + "chokidar": "^3.5.2", + "debug": "^3.2.7", "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", - "pstree.remy": "^1.1.7", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", "supports-color": "^5.5.0", "touch": "^3.1.0", - "undefsafe": "^2.0.2", - "update-notifier": "^4.0.0" + "undefsafe": "^2.0.5" }, "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "requires": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - } - }, "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "requires": { "ms": "^2.1.1" } }, - "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - } - }, - "is-npm": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", - "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==" - }, - "latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", - "requires": { - "package-json": "^6.3.0" - } - }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", - "requires": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", - "requires": { - "rc": "^1.2.8" - } - }, - "semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", - "requires": { - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "supports-color": { "version": "5.5.0", @@ -4454,49 +4030,13 @@ "requires": { "has-flag": "^3.0.0" } - }, - "update-notifier": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.0.tgz", - "integrity": "sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew==", - "requires": { - "boxen": "^4.2.0", - "chalk": "^3.0.0", - "configstore": "^5.0.1", - "has-yarn": "^2.1.0", - "import-lazy": "^2.1.0", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.3.1", - "is-npm": "^4.0.0", - "is-yarn-global": "^0.3.0", - "latest-version": "^5.0.0", - "pupa": "^2.0.1", - "semver-diff": "^3.1.1", - "xdg-basedir": "^4.0.0" - } - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" } } }, "nopt": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", "requires": { "abbrev": "1" } @@ -4506,11 +4046,6 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, - "normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" - }, "nth-check": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", @@ -4556,11 +4091,6 @@ "os-tmpdir": "^1.0.0" } }, - "p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" - }, "package-json": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/package-json/-/package-json-1.2.0.tgz", @@ -4604,9 +4134,9 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, "pinkie": { "version": "2.0.4", @@ -4649,28 +4179,11 @@ "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, - "pupa": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.0.1.tgz", - "integrity": "sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==", - "requires": { - "escape-goat": "^2.0.0" - } - }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -4747,21 +4260,13 @@ } }, "readdirp": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", - "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "requires": { "picomatch": "^2.2.1" } }, - "registry-auth-token": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.1.1.tgz", - "integrity": "sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA==", - "requires": { - "rc": "^1.2.8" - } - }, "registry-url": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", @@ -4815,14 +4320,6 @@ "uuid": "^3.3.2" } }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "requires": { - "lowercase-keys": "^1.0.0" - } - }, "right-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", @@ -4859,10 +4356,20 @@ "semver": "^5.0.3" } }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + "simple-update-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "requires": { + "semver": "~7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" + } + } }, "slide": { "version": "1.1.6", @@ -4929,40 +4436,10 @@ "strip-ansi": "^3.0.0" } }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" - } - } - } + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" }, "strip-ansi": { "version": "3.0.1", @@ -4996,11 +4473,6 @@ "whet.extend": "~0.9.9" } }, - "term-size": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz", - "integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==" - }, "terser": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", @@ -5031,11 +4503,6 @@ "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-2.0.0.tgz", "integrity": "sha1-84sK6B03R9YoAB9B2vxlKs5nHAo=" }, - "to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -5079,19 +4546,6 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, "uglify-js": { "version": "2.8.29", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", @@ -5109,20 +4563,9 @@ "optional": true }, "undefsafe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", - "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==", - "requires": { - "debug": "^2.2.0" - } - }, - "unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "requires": { - "crypto-random-string": "^2.0.0" - } + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" }, "update-notifier": { "version": "0.5.0", @@ -5146,21 +4589,6 @@ "punycode": "^2.1.0" } }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "requires": { - "prepend-http": "^2.0.0" - }, - "dependencies": { - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" - } - } - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -5186,14 +4614,6 @@ "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=" }, - "widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "requires": { - "string-width": "^4.0.0" - } - }, "window-size": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", diff --git a/package.json b/package.json index 932bfaa9..d2c526d1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wled", - "version": "0.14.0-b1.16", + "version": "0.14.0-b15.22", "description": "Tools for WLED project", "main": "tools/cdata.js", "directories": { @@ -25,7 +25,7 @@ "clean-css": "^4.2.3", "html-minifier-terser": "^5.1.1", "inliner": "^1.13.1", - "nodemon": "^2.0.4", + "nodemon": "^2.0.20", "zlib": "^1.0.5" } } diff --git a/platformio.ini b/platformio.ini index 10822e36..0dbcefc5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -40,27 +40,41 @@ ; =================== default_envs = - esp32_4MB_max ; recommended default - esp32_4MB_all - esp32_16MB_max - esp32_16MB_all - esp8266_4MB_min - esp8266_4MB_max - esp32_4MB_PSRAM_max - esp32S3_8MB_max - wemos_shield_esp32_4MB_max - wemos_shield_esp32_4MB_ICS4343x_max - wemos_shield_esp32_4MB_SPM1423_max - wemos_shield_esp32_4MB_LineIn_max - wemos_shield_esp32_16MB_max - wemos_shield_esp32_16MB_ICS4343x_max - wemos_shield_esp32_16MB_SPM1423_max - wemos_shield_esp32_16MB_SPM1423_all - wemos_shield_esp32_16MB_LineIn_max - esp32_pico_4MB_max - esp32s2_tinyUF2_PSRAM_max - esp8266pro_16MB_min + esp32_4MB_S + esp32_4MB_M ; recommended default + esp32_4MB_M_debug + esp32_4MB_XL + esp32_16MB_M + esp32_16MB_M_debug + esp32_16MB_XL + esp8266_4MB_S + esp8266_4MB_M + wemos_shield_esp32_4MB_M + wemos_shield_esp32_4MB_ICS4343x_M + wemos_shield_esp32_4MB_SPM1423_M + wemos_shield_esp32_4MB_LineIn_M + wemos_shield_esp32_16MB_M + wemos_shield_esp32_16MB_ICS4343x_M + wemos_shield_esp32_16MB_SPM1423_M + wemos_shield_esp32_16MB_SPM1423_XL + wemos_shield_esp32_16MB_LineIn_M + esp32_pico_4MB_M + esp32_4MB_PSRAM_S + esp32S3_8MB_M + ;; esp32s2_tinyUF2_PSRAM_S ;; experimental - only for adafruit -S2 boards with tinyUF2 bootloader !!! + esp32s2_PSRAM_M ;; experimental + esp32c3dev_4MB_M ;; experimental + seeed_esp32c3_4MB_S ;; experimental + esp32_4MB_V4_S ;; experimental + esp32_16MB_V4_M ;; experimental + esp32_16MB_V4_M_debug ;; experimental + esp8266pro_16MB_S + esp8266pro_16MB_M + esp01_1MB_S + esp32_16MB_M_eth + athom_music_esp32_4MB_M +; Go to MoonModules environments for environments src_dir = ./wled00 data_dir = ./wled00/data @@ -136,7 +150,6 @@ build_flags = -D DECODE_SONY=true -D DECODE_SAMSUNG=true -D DECODE_LG=true - -D DECODE_RC5=true ; -Dregister= # remove warnings in C++17 due to use of deprecated register keyword by the FastLED library -DWLED_USE_MY_CONFIG ; -D USERMOD_SENSORSTOMQTT @@ -182,7 +195,7 @@ upload_speed = 115200 # Please note that we don't always use the latest version of a library. # # The following libraries have been included (and some of them changd) in the source: -# ArduinoJson@5.13.5, Blynk@0.5.4(changed), E131@1.0.0(changed), Time@1.5, Timezone@1.2.1 +# ArduinoJson@5.13.5, E131@1.0.0(changed), Time@1.5, Timezone@1.2.1 # ------------------------------------------------------------------------------ lib_compat_mode = strict lib_deps = @@ -213,17 +226,17 @@ build_flags = -DESP8266 -DFP_IN_IROM ;-Wno-deprecated-declarations - ;-Wno-register + ;-Wno-register ;; leaves some warnings when compiling C files: command-line option '-Wno-register' is valid for C++/ObjC++ but not for C ;-Wno-misleading-indentation -; NONOSDK22x_190703 = 2.2.2-dev(38a443e) + ; NONOSDK22x_190703 = 2.2.2-dev(38a443e) -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703 -; lwIP 2 - Higher Bandwidth no Features -; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH -; lwIP 1.4 - Higher Bandwidth (Aircoookie has) - -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH -; VTABLES in Flash + ; lwIP 2 - Higher Bandwidth no Features + ; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH + ; lwIP 1.4 - Higher Bandwidth (Aircoookie has) + -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH + ; VTABLES in Flash -DVTABLES_IN_FLASH -; restrict to minimal mime-types + ; restrict to minimal mime-types -DMIMETYPE_MINIMAL lib_deps = @@ -247,7 +260,8 @@ build_flags = -g -D LOROL_LITTLEFS ; -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when builing with arduino-esp32 >=2.0.3 -default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv +default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv ;; WLED standard for 4MB flash: 1.4MB firmware, 1MB filesystem +;default_partitions = tools/WLED_ESP32_4MB_256KB_FS.csv ;; Alternative for 4MB flash: 1.8MB firmware, 256KB filesystem (esptool erase_flash needed before changing) lib_deps = ${env.lib_deps} @@ -258,9 +272,13 @@ lib_deps = https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 ;; ** For compiling with latest Frameworks (IDF4.4.x and arduino-esp32 v2.0.x) ** -;;; standard platform +;;; standard V4 platform platformV4 = espressif32@ ~5.1.1 platformV4_packages = platformio/framework-arduinoespressif32@ ~3.20004.0 +;;; newer V4 platform - may help in case you experience flash corruption and boot loops +platformV4_new = espressif32@ ~5.2.0 +platformV4_new_packages = + ;;; tasmota platform ;platformV4 = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.5.1/platform-espressif32-2.0.5.1.zip ;platformV4_packages = @@ -276,12 +294,14 @@ lib_depsV4 = ;https://github.com/Makuna/NeoPixelBus.git#master ;; NPB 2.6.9 tends to crash whith IDF V4.4.3 -> use latest NeoPixelBus dev instead makuna/NeoPixelBus @ 2.7.1 https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 + https://github.com/someweisguy/esp_dmx.git#v3.0.2-beta [esp32s2] build_flags = -g - -DARDUINO_ARCH_ESP32 + -DARDUINO_ARCH_ESP32 -DESP32 ;; WLEDMM -DARDUINO_ARCH_ESP32S2 -DCONFIG_IDF_TARGET_ESP32S2 + -DCONFIG_LITTLEFS_FOR_IDF_3_2 -DLFS_THREADSAFE ;; WLEDMM -D CONFIG_ASYNC_TCP_USE_WDT=0 -DCO -DARDUINO_USB_MODE=0 ;; this flag is mandatory for ESP32-S2 ! @@ -295,9 +315,10 @@ lib_deps = [esp32c3] build_flags = -g - -DARDUINO_ARCH_ESP32 + -DARDUINO_ARCH_ESP32 -DESP32 ;; WLEDMM -DARDUINO_ARCH_ESP32C3 - -DCONFIG_IDF_TARGET_ESP32C3 + -DCONFIG_IDF_TARGET_ESP32C3=1 + -DCONFIG_LITTLEFS_FOR_IDF_3_2 -DLFS_THREADSAFE ;; WLEDMM -D CONFIG_ASYNC_TCP_USE_WDT=0 -DCO -DARDUINO_USB_MODE=1 ;; this flag is mandatory for ESP32-C3 @@ -305,8 +326,10 @@ build_flags = -g ;; ARDUINO_USB_CDC_ON_BOOT lib_deps = - ${env.lib_deps} - makuna/NeoPixelBus @ 2.7.1 ;; WLEDMM - new version is more stable on -C3 + makuna/NeoPixelBus @ 2.7.3 ;; WLEDMM - new version is more stable on -C3 + fastled/FastLED @ 3.5.0 + IRremoteESP8266 @ ~2.8.2 + https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.7 https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 [esp32s3] @@ -316,6 +339,7 @@ build_flags = -g -DARDUINO_ARCH_ESP32 -DARDUINO_ARCH_ESP32S3 -DCONFIG_IDF_TARGET_ESP32S3 + -DCONFIG_LITTLEFS_FOR_IDF_3_2 -DLFS_THREADSAFE ;; WLEDMM -D CONFIG_ASYNC_TCP_USE_WDT=0 -DCO ;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry: @@ -325,7 +349,7 @@ lib_deps = ${env.lib_deps} ;; currently we need the latest NeoPixelBus dev version, because it contains important bugfixes for -S3 ;https://github.com/Makuna/NeoPixelBus.git#master ;; NPB 2.6.9 tends to crash whith IDF V4.4.3 -> use latest NeoPixelBus dev instead - makuna/NeoPixelBus @ 2.7.1 + makuna/NeoPixelBus @ 2.7.3 https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 @@ -339,7 +363,7 @@ platform = ${common.platform_wled_default} platform_packages = ${common.platform_packages} board_build.ldscript = ${common.ldscript_4m1m} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D WLED_DISABLE_BLYNK #-DWLED_DISABLE_2D +build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 #-DWLED_DISABLE_2D lib_deps = ${esp8266.lib_deps} monitor_filters = esp8266_exception_decoder @@ -349,17 +373,18 @@ platform = ${common.platform_wled_default} platform_packages = ${common.platform_packages} board_build.ldscript = ${common.ldscript_2m512k} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02 -D WLED_DISABLE_BLYNK +build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02 lib_deps = ${esp8266.lib_deps} -[env:esp01_1m_full] -board = esp01_1m -platform = ${common.platform_wled_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_1m128k} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA -D WLED_DISABLE_BLYNK -lib_deps = ${esp8266.lib_deps} +;WLEDMM: see below +; [env:esp01_1m_full] +; board = esp01_1m +; platform = ${common.platform_wled_default} +; platform_packages = ${common.platform_packages} +; board_build.ldscript = ${common.ldscript_1m128k} +; build_unflags = ${common.build_unflags} +; build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA +; lib_deps = ${esp8266.lib_deps} [env:esp07] board = esp07 @@ -404,7 +429,7 @@ board = esp32dev platform = ${esp32.platform} platform_packages = ${esp32.platform_packages} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 -D WLED_DISABLE_BLYNK #-D WLED_DISABLE_BROWNOUT_DET +build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 #-D WLED_DISABLE_BROWNOUT_DET lib_deps = ${esp32.lib_deps} monitor_filters = esp32_exception_decoder board_build.partitions = ${esp32.default_partitions} @@ -414,7 +439,7 @@ board = esp32dev platform = ${esp32.platform} platform_packages = ${esp32.platform_packages} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_qio80 -D WLED_DISABLE_BLYNK #-D WLED_DISABLE_BROWNOUT_DET +build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_qio80 #-D WLED_DISABLE_BROWNOUT_DET lib_deps = ${esp32.lib_deps} monitor_filters = esp32_exception_decoder board_build.partitions = ${esp32.default_partitions} @@ -427,7 +452,7 @@ platform = ${esp32.platform} platform_packages = ${esp32.platform_packages} upload_speed = 921600 build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_Ethernet -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1 -D WLED_DISABLE_BLYNK +build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_Ethernet -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1 lib_deps = ${esp32.lib_deps} board_build.partitions = ${esp32.default_partitions} @@ -448,8 +473,6 @@ lib_deps = ${esp32s2.lib_deps} board = esp32-c3-devkitm-1 platform = ${esp32.platformV4} ;; standard platform, well-tested on -C3, good compatibility with WLED platform_packages = ${esp32.platformV4_packages} ;; use with standard platform -; platform = espressif32@~5.2.0 ;; alternative platform, might help in case you experience bootloops due to corrupted flash filesystem -; platform_packages = ;; for alternative platform board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME=ESP32-C3 -D WLED_WATCHDOG_TIMEOUT=0 @@ -458,7 +481,7 @@ build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME= -D CONFIG_LITTLEFS_FOR_IDF_3_2 ; WLEDMM -D WLED_USE_MY_CONFIG ; WLEDMM ; -D USERMOD_AUDIOREACTIVE ; WLEDMM - -D USERMOD_CUSTOMEFFECTS ; WLEDMM + -D USERMOD_ARTIFX ; WLEDMM upload_speed = 460800 build_unflags = ${common.build_unflags} lib_deps = ${esp32c3.lib_deps} @@ -477,7 +500,7 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -D CONFIG_LITTLEFS_F -D WLED_RELEASE_NAME=ESP32-S3 -D WLED_USE_MY_CONFIG -D USERMOD_AUDIOREACTIVE - -D USERMOD_CUSTOMEFFECTS + -D USERMOD_ARTIFX lib_deps = ${esp32s3.lib_deps} https://github.com/blazoncek/arduinoFFT.git board_build.partitions = tools/WLED_ESP32_8MB.csv @@ -491,8 +514,8 @@ monitor_filters = esp32_exception_decoder ;board = um_tinys3 ; -> needs workaround from https://github.com/Aircoookie/WLED/pull/2905#issuecomment-1328049860 ;board = esp32s3box ; -> error: 'esp32_adc2gpio' was not declared in this scope board = esp32-s3-devkitc-1 ; -> compiles, but does not support PSRAM -platform = espressif32 @ ~5.2.0 -platform_packages = +platform = ${esp32.platformV4_new} ;; alternative platform, might help in case you experience bootloops due to corrupted flash filesystem +platform_packages = ${esp32.platformV4_new_packages} upload_speed = 921600 build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} ${esp32s3.build_flags} @@ -594,678 +617,37 @@ build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME ; -D STATUSLED=15 -D WLED_USE_MY_CONFIG -D USERMOD_AUDIOREACTIVE - -D USERMOD_CUSTOMEFFECTS + -D USERMOD_ARTIFX lib_deps = ${esp32s2.lib_deps} https://github.com/blazoncek/arduinoFFT.git -# ------------------------------------------------------------------------------ -# MoonModules configs -# see https://mm.kno.wled.ge/moonmodules/platformio-entries/ -# ------------------------------------------------------------------------------ - -; shared build flags and lib deps for minimum and maximum config -[common_mm] -build_flags_min = - -Wall -Wformat -Woverflow -Wuninitialized -Winit-self -Warray-bounds ; enables more warnings - -Wno-attributes -Wno-unused-variable -Wno-unused-function -Wno-deprecated-declarations ;disables some stupid warnings - -D WLED_DISABLE_BLYNK ; BLYNK is only provided for backwards compatibility (no new users accepted) - ;-D WLED_DISABLE_BROWNOUT_DET ; enable if you get "brownout detected" errors at startup - -D WLED_USE_MY_CONFIG - ; -D WLED_USE_CIE_BRIGHTNESS_TABLE ;; experimental: use different color / brightness lookup table - -D USERMOD_AUDIOREACTIVE - -D UM_AUDIOREACTIVE_USE_NEW_FFT ; use latest (upstream) FFTLib, instead of older library modified by blazoncek. Slightly faster, more accurate, needs 2KB RAM extra - -D USERMOD_CUSTOMEFFECTS ; WLEDMM usermod - ; -D WLED_DEBUG_HOST='"192.168.xxx.xxx"' ;; to send debug messages over network to host 192.168.x.y - FQDN is also possible - ; -D WLED_DEBUG_NET_PORT=1768 ;; port for network debugging. default = 7868 - ; -D WLED_DEBUG ; lots of generic debug messages - ; -D SR_DEBUG ; some extra debug messages from audioreactive - ; -D MIC_LOGGER ; for sound input monitoring & debugging (use arduino serial plotter) - ; -D WLED_DISABLE_LOXONE - ; -D WLED_DISABLE_ALEXA - ; -D WLED_DISABLE_HUESYNC - ; -D WLED_DISABLE_MQTT - ; -D WLED_DISABLE_INFRARED - ; -D WLED_ENABLE_DMX - -lib_deps_min = - https://github.com/kosme/arduinoFFT#develop @ 1.9.2 ; used for USERMOD_AUDIOREACTIVE - -build_flags_max = - -D WLED_MAX_USERMODS=25 ; default only 4-6, also for _all configs takes 25 pointers in memory - ;; -D ARDUINO_USB_CDC_ON_BOOT=0 ; needed for arduino-esp32 >=2.0.4 ewowi to softhack: move to build_flags_min? - We need a different solution - -D WLED_USE_MY_CONFIG ; include custom my_config.h ewowi to softhack: redundant as also in build_flags_min? - -D USERMOD_DALLASTEMPERATURE - -D USE_ALT_DISPLAY ; new versions of USERMOD_FOUR_LINE_DISPLAY and USERMOD_ROTARY_ENCODER_UI - -D USERMOD_FOUR_LINE_DISPLAY - -D USERMOD_ROTARY_ENCODER_UI - -D USERMOD_AUTO_SAVE - -D USERMOD_WEATHER ; WLEDMM usermod - -D USERMOD_MPU6050_IMU ; gyro/accelero for USERMOD_GAMES (ONLY WORKS IF USERMOD_FOUR_LINE_DISPLAY NOT INCLUDED - I2C SHARING BUG) - -D USERMOD_GAMES ; WLEDMM usermod - -lib_deps_max = - OneWire@~2.3.5 ; used for USERMOD_FOUR_LINE_DISPLAY and USERMOD_DALLASTEMPERATURE - olikraus/U8g2 @ ^2.28.8 ; used for USERMOD_FOUR_LINE_DISPLAY - ElectronicCats/MPU6050 @ 0.6.0 ; used for USERMOD_MPU6050_IMU - -lib_deps_V4_max = - paulstoffregen/OneWire@ ^2.3.7 ; used for USERMOD_DALLASTEMPERATURE -> need newer release with bugfixes for -S3; still requires TEMPERATURE_PIN < 46 - olikraus/U8g2@ ^2.34.5 ; used for USERMOD_FOUR_LINE_DISPLAY -> need newer version with bugfixes for arduino-esp32 v2.0.4 (Wire inititialization) - ElectronicCats/MPU6050 @ 0.6.0 ; used for USERMOD_MPU6050_IMU - claws/BH1750 @^1.2.0 ; used for USERMOD_BH1750 - -build_flags_all = - -D USERMOD_BATTERY ;; enable Battery usermod - -D USERMOD_BATTERY_USE_LIPO ;; use new "decharging curve" for LiPo cells - -D USERMOD_BH1750 - -D USERMOD_ANIMATED_STAIRCASE - -D USERMOD_RTC ;; experimental - ; -D USERMOD_SENSORSTOMQTT ;; experimental ewpwi causes error: fatal error: Adafruit_Sensor.h: No such file or directory - -D USERMOD_ANALOG_CLOCK - -D USERMOD_MULTI_RELAY - -D USERMOD_PIRSWITCH - -D USERMOD_PWM_FAN - -D USERMOD_PING_PONG_CLOCK - -D USERMOD_BUZZER - -D USERMOD_SN_PHOTORESISTOR - -D USERMOD_BME280 - -D USERMOD_DHT - -D USERMOD_VL53L0X_GESTURES - -lib_deps_all = - claws/BH1750 @^1.2.0 ; used for USERMOD_BH1750 - ; adafruit/Adafruit BMP280 Library @ 2.1.0 ;; experimental for usermod USERMOD_SENSORSTOMQTT - ; adafruit/Adafruit CCS811 Library @ 1.0.4 ;; experimental for usermod USERMOD_SENSORSTOMQTT - ; adafruit/Adafruit Si7021 Library @ 1.4.0 ;; experimental for usermod USERMOD_SENSORSTOMQTT - BME280@~3.0.0 ; for usermod USERMOD_BME280 - https://github.com/alwynallan/DHT_nonblocking ; for usermod USERMOD_DHT - pololu/VL53L0X @ ^1.3.0 ; for usermod USERMOD_VL53L0X_GESTURES - -; end of common - -; base entries (without WLED_RELEASE_NAME) - -; common defaults for all MM environments -[esp32_4MB_min_base] -board = esp32dev -platform = ${esp32.platform} -upload_speed = 460800 ; or 921600 -platform_packages = ${esp32.platform_packages} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags_esp32} ${common_mm.build_flags_min} -lib_deps = ${esp32.lib_deps} ${common_mm.lib_deps_min} -board_build.partitions = ${esp32.default_partitions} -board_build.f_flash = 80000000L ; use full 80MHz speed for flash (default = 40Mhz) -board_build.flash_mode = dio ; (dio = dual i/o; more compatible than qio = quad i/o) -monitor_filters = esp32_exception_decoder ; used to show crash details - -;common default for all max environments -[esp32_4MB_max_base] -extends = esp32_4MB_min_base -build_flags = ${esp32_4MB_min_base.build_flags} ${common_mm.build_flags_max} -lib_deps = ${esp32_4MB_min_base.lib_deps} ${common_mm.lib_deps_max} -board_build.partitions = ${esp32_4MB_min_base.board_build.partitions} -; board_build.partitions = tools/WLED_ESP32-wrover_4MB.csv - -[esp32_4MB_all_base] -extends = esp32_4MB_max_base -build_flags = ${esp32_4MB_max_base.build_flags} ${common_mm.build_flags_all} -lib_deps = ${esp32_4MB_max_base.lib_deps} ${common_mm.lib_deps_all} -; board_build.partitions = tools/WLED_ESP32-wrover_4MB.csv - -;common default for all V4 min environments -[esp32_4MB_V4_min_base] -board = esp32dev -upload_speed = 460800 ; or 921600 -platform = ${esp32.platformV4} -platform_packages = ${esp32.platformV4_packages} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32.build_flagsV4} ${common_mm.build_flags_min} - -Wno-misleading-indentation -Wno-format-truncation -lib_deps = ${esp32.lib_depsV4} ${common_mm.lib_deps_min} -board_build.partitions = ${esp32.default_partitions} -board_build.f_flash = 80000000L ; use full 80MHz speed for flash (default = 40Mhz) -board_build.flash_mode = dio ; (dio = dual i/o; more compatible than qio = quad i/o) -;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation -; monitor_filters = esp32_exception_decoder ; used to show crash details - -[esp32_4MB_V4_max_base] -extends = esp32_4MB_V4_min_base -build_flags = ${esp32_4MB_V4_min_base.build_flags} ${common_mm.build_flags_max} -lib_deps = ${esp32_4MB_V4_min_base.lib_deps} ${common_mm.lib_deps_max} -board_build.partitions = ${esp32_4MB_V4_min_base.board_build.partitions} -;board_build.flash_mode = qio ; (dio = dual i/o; more compatible than qio = quad i/o) - -[Shield_ICS4343x] -build_flags = - -D SR_DMTYPE=1 -D I2S_SDPIN=32 -D I2S_WSPIN=15 -D I2S_CKPIN=14 ; for regular I2S microphone - -D SR_SQUELCH=10 -D SR_GAIN=30 -D SR_FREQ_PROF=5 ; ICS-43434 specific - -[Shield_SPM1423] -build_flags = - -D SR_DMTYPE=5 -D I2S_SDPIN=32 -D I2S_WSPIN=15 -D I2S_CKPIN=-1 ; for I2S PDM microphone - -D SR_SQUELCH=3 -D SR_GAIN=75 -D SR_FREQ_PROF=7 ; SPM1423 specific - -[Shield_LineIn] -build_flags = - -D SR_DMTYPE=4 -D MCLK_PIN=0 -D I2S_SDPIN=25 -D I2S_WSPIN=15 -D I2S_CKPIN=14 ; for audio Line-In shield - -D SR_SQUELCH=2 -D SR_GAIN=40 -D SR_FREQ_PROF=1 ; CS5343 Line-In specific - -[Board_ESP32_16MB] ; ESP32 with 16MB Flash -board = esp32_16MB -board_build.partitions = tools/WLED_ESP32_16MB.csv - - -; end of base entries - - -; bin entries (with WLED_RELEASE_NAME) - -[env:esp32_4MB_min] -extends = esp32_4MB_min_base -build_flags = ${esp32_4MB_min_base.build_flags} - -D WLED_RELEASE_NAME=esp32_4MB_min - ; RAM: [== ] 24.1% (used 78900 bytes from 327680 bytes) - ; Flash: [======== ] 83.7% (used 1315729 bytes from 1572864 bytes) - -[env:esp32_4MB_max] -extends = esp32_4MB_max_base -build_flags = ${esp32_4MB_max_base.build_flags} - -D WLED_RELEASE_NAME=esp32_4MB_max - ; RAM: [== ] 24.4% (used 79804 bytes from 327680 bytes) - ; Flash: [========= ] 88.7% (used 1394813 bytes from 1572864 bytes) - -[env:esp32_4MB_all] -extends = esp32_4MB_all_base -build_flags = ${esp32_4MB_all_base.build_flags} - -D WLED_RELEASE_NAME=esp32_4MB_all - ; RAM: [== ] 24.4% (used 79804 bytes from 327680 bytes) - ; Flash: [========= ] 88.7% (used 1394813 bytes from 1572864 bytes) - -[env:esp32_16MB_max] -extends = esp32_4MB_max_base -build_flags = ${esp32_4MB_max_base.build_flags} - -D WLED_RELEASE_NAME=esp32_16MB_max -board = ${Board_ESP32_16MB.board} -board_build.partitions = ${Board_ESP32_16MB.board_build.partitions} - ; RAM: [== ] 24.4% (used 79916 bytes from 327680 bytes) - ; Flash: [======= ] 67.0% (used 1405701 bytes from 2097152 bytes) -;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation - -[env:esp32_16MB_all] -extends = esp32_4MB_all_base -build_flags = ${esp32_4MB_all_base.build_flags} - -D WLED_RELEASE_NAME=esp32_16MB_all -board = ${Board_ESP32_16MB.board} -board_build.partitions = ${Board_ESP32_16MB.board_build.partitions} - ; RAM: [== ] 24.4% (used 79916 bytes from 327680 bytes) - ; Flash: [======= ] 67.0% (used 1405701 bytes from 2097152 bytes) -;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation - -[env:esp8266_4MB_min] -extends = env:d1_mini -upload_speed = 460800 ;115200 -board_build.f_cpu = 160000000L ;; we want 160Mhz (default = 80Mhz) -build_flags = ${common.build_flags_esp8266} - -D WLED_RELEASE_NAME=esp8266_4MB_min - -D WLED_DISABLE_ALEXA - -D WLED_DISABLE_BLYNK - -D WLED_DISABLE_HUESYNC - ; -D WLED_DISABLE_2D - ; -UWLED_USE_MY_CONFIG - ; -D WLED_DEBUG -; monitor_filters = esp8266_exception_decoder -; RAM: [====== ] 59.3% (used 48608 bytes from 81920 bytes) -; Flash: [======== ] 77.0% (used 804176 bytes from 1044464 bytes) - -[env:esp8266_4MB_max] -extends = env:d1_mini -upload_speed = 460800 ;115200 -board_build.f_cpu = 160000000L ;; we want 160Mhz (default = 80Mhz) -build_flags = ${common.build_flags_esp8266} - -D WLED_RELEASE_NAME=esp8266_4MB_max - -D WLED_MAX_USERMODS=5 ; default only 4-6 - -D WLED_DISABLE_ALEXA - -D WLED_DISABLE_BLYNK - -D WLED_DISABLE_HUESYNC - -D WLED_DISABLE_LOXONE - ; -D USERMOD_AUDIOREACTIVE - ; -D USERMOD_CUSTOMEFFECTS ; to be done - ; -UWLED_USE_MY_CONFIG - -D USERMOD_PIRSWITCH - -D USERMOD_DALLASTEMPERATURE ;; disabled because it hangs during usermod setup on -S3 (autodetect broken?) - -D USERMOD_MULTI_RELAY - -D USE_ALT_DISPLAY ; new versions of USERMOD_FOUR_LINE_DISPLAY and USERMOD_ROTARY_ENCODER_UI - -D USERMOD_FOUR_LINE_DISPLAY - -D USERMOD_MPU6050_IMU ; gyro/accelero for USERMOD_GAMES (ONLY WORKS IF USERMOD_FOUR_LINE_DISPLAY NOT INCLUDED - I2C SHARING BUG) - -D USERMOD_GAMES ; WLEDMM usermod - ; -D WLED_DEBUG -; monitor_filters = esp8266_exception_decoder -lib_deps = ${esp8266.lib_deps} - OneWire@~2.3.5 ; used for USERMOD_FOUR_LINE_DISPLAY and USERMOD_DALLASTEMPERATURE - olikraus/U8g2 @ ^2.28.8 ; used for USERMOD_FOUR_LINE_DISPLAY - ElectronicCats/MPU6050 @ 0.6.0 ; used for USERMOD_MPU6050_IMU -; RAM: [====== ] 61.5% (used 50344 bytes from 81920 bytes) -; Flash: [======== ] 81.8% (used 854444 bytes from 1044464 bytes) - -; Blaz env (for reference purposes) -[env:d1_mini_temp] -extends = env:d1_mini -board_build.filesystem = littlefs -build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 - -D WLED_DISABLE_ALEXA - -D WLED_DISABLE_BLYNK - -D WLED_DISABLE_HUESYNC - -D WLED_DISABLE_LOXONE - -D WLED_DISABLE_AUDIO ;WLEDMM not used anywhere - -D WLED_ENABLE_SIMPLE_UI - -D USERMOD_FOUR_LINE_DISPLAY - -D USE_ALT_DISPlAY - -D USERMOD_DALLASTEMPERATURE - -D TEMPERATURE_PIN=13 # (D7) - -D LEDPIN=2 # (D4) - -D RLYPIN=12 # (D6) - -D BTNPIN=0 # (D3) - -D IRPIN=14 # (D5) - -D USERMOD_MULTI_RELAY - -D MULTI_RELAY_MAX_RELAYS=2 - -D USERMOD_PIRSWITCH - -D PIR_SENSOR_PIN=16 - -D PIR_SENSOR_OFF_SEC=60 - -UWLED_USE_MY_CONFIG -lib_deps = ${esp8266.lib_deps} - paulstoffregen/OneWire@~2.3.7 ;WLEDMM Softhack, we need this as well (instead of 2.3.5)? - olikraus/U8g2 # @~2.33.15 - Wire ; WLEDMM needed? - -[env:esp8266pro_16MB_min] -extends = env:d1_mini -board = d1_mini_pro ;; "D1 mini pro": ESP8266EX, 160MHz, 80KB RAM, 16MB Flash -board_build.f_cpu = 160000000L ;; we want 160Mhz (default = 80Mhz) -;board_build.f_flash = 80000000L ;; for 80Mhz flash speed, in case your chip can handle it (default = 40Mhz) -board_build.flash_mode = qio ;; quad IO - fastest speed, in case your chip can handle it. -;;board_build.flash_mode = dout ;; use if your esp8266 becomes unstable with "qio" -board_build.ldscript = ${common.ldscript_16m14m} ;; 16MB flash, use 14MB for LittleFS - -upload_speed = 460800 ;115200 -build_flags = ${common.build_flags_esp8266} - -D WLED_RELEASE_NAME=esp8266pro_16MB_min - -D WLED_WATCHDOG_TIMEOUT=0 - -D WLED_DISABLE_ALEXA - -D WLED_DISABLE_BLYNK - -D WLED_DISABLE_HUESYNC - ; -D WLED_DEBUG - ; -D WLED_DISABLE_2D - ; -D USERMOD_AUDIOREACTIVE - ; -D USERMOD_CUSTOMEFFECTS ; to be done - ; -UWLED_USE_MY_CONFIG -monitor_filters = esp8266_exception_decoder -; RAM: [====== ] 59.3% (used 48616 bytes from 81920 bytes) -; Flash: [======== ] 77.0% (used 804236 bytes from 1044464 bytes) - -# ------------------------------------------------------------------------------ -# MoonModules configs for IDF V4.4.x -# ------------------------------------------------------------------------------ -;; Warning: the build-in LittleFS (arduino-esp32 2.0.x) seems to be "slightly different" from Lorol LittleFS. -;; When upgrading to the new framework, it might be necessary to first do a chip erase (make sure you have a backup of cfg.json and presets.json) - -; compiled with ESP-IDF 4.4.1 -[env:esp32_4MB_V4_min] -extends = esp32_4MB_V4_min_base -build_flags = ${esp32_4MB_V4_min_base.build_flags} - -D WLED_RELEASE_NAME=esp32_4MB_V4_min - -D WLED_WATCHDOG_TIMEOUT=0 #-D WLED_DISABLE_BROWNOUT_DET - -D ARDUINO_USB_CDC_ON_BOOT=0 ; needed for arduino-esp32 >=2.0.4; avoids errors on startup - -D WLED_DISABLE_LOXONE - -D WLED_DISABLE_ALEXA - -D WLED_DISABLE_HUESYNC - -D WLED_DISABLE_MQTT - -D WLED_DISABLE_INFRARED - ; -D WLED_DEBUG - ; -D SR_DEBUG - ; -D MIC_LOGGER - ; RAM: [== ] 24.2% (used 79372 bytes from 327680 bytes) - ; Flash: [========= ] 88.8% (used 1396073 bytes from 1572864 bytes) -;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation - -; compiled with ESP-IDF 4.4.1 -[env:esp32_4MB_V4_max] -extends = esp32_4MB_V4_max_base -build_flags = ${esp32_4MB_V4_max_base.build_flags} - -D WLED_RELEASE_NAME=esp32_4MB_V4_max - -D WLED_WATCHDOG_TIMEOUT=0 #-D WLED_DISABLE_BROWNOUT_DET - -D ARDUINO_USB_CDC_ON_BOOT=0 ; needed for arduino-esp32 >=2.0.4; avoids errors on startup - ; RAM: [== ] 24.8% (used 81316 bytes from 327680 bytes) - ; Flash: [==========] 97.2% (used 1528253 bytes from 1572864 bytes) - -D WLED_DISABLE_LOXONE ; FLASH 1272 bytes - -D WLED_DISABLE_ALEXA ; RAM 116 bytes; FLASH 13524 bytes - -D WLED_DISABLE_HUESYNC ;RAM 122 bytes; FLASH 6308 bytes - ; RAM: [== ] 24.7% (used 81076 bytes from 327680 bytes) - ; Flash: [==========] 95.8% (used 1506893 bytes from 1572864 bytes) - ;-D WLED_DISABLE_MQTT ; RAM 216 bytes; FLASH 16496 bytes - ;-D WLED_DISABLE_INFRARED ;RAM 136 bytes; FLASH 24492 bytes -;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation - -; compiled with ESP-IDF 4.4.1 -[env:esp32_16MB_V4_max] -extends = esp32_4MB_V4_max_base -build_flags = ${esp32_4MB_V4_max_base.build_flags} - -D WLED_RELEASE_NAME=esp32_16MB_V4_max - -D WLED_WATCHDOG_TIMEOUT=0 #-D WLED_DISABLE_BROWNOUT_DET - -D ARDUINO_USB_CDC_ON_BOOT=0 ; needed for arduino-esp32 >=2.0.4; avoids errors on startup -board = ${Board_ESP32_16MB.board} -board_build.partitions = ${Board_ESP32_16MB.board_build.partitions} - ; RAM: [== ] 24.8% (used 81316 bytes from 327680 bytes) - ; Flash: [======= ] 72.9% (used 1528253 bytes from 2097152 bytes) - -;; experimental environment for boards with PSRAM (needs ESP-IDF 4.4.1). -[env:esp32_4MB_PSRAM_max] -extends = esp32_4MB_V4_max_base -board = lolin_d32_pro -;board = esp32cam -build_flags = ${esp32_4MB_V4_max_base.build_flags} - -D WLED_RELEASE_NAME=esp32_4MB_PSRAM_max - -D WLED_WATCHDOG_TIMEOUT=0 #-D WLED_DISABLE_BROWNOUT_DET - -D ARDUINO_USB_CDC_ON_BOOT=0 ; needed for arduino-esp32 >=2.0.4; avoids errors on startup - -D WLED_USE_PSRAM - ; RAM: [== ] 24.8% (used 81424 bytes from 327680 bytes) - ; Flash: [==========] 99.8% (used 1570457 bytes from 1572864 bytes) - ; 99.8% !!!! - -D WLED_DISABLE_LOXONE ; FLASH 1272 bytes - -D WLED_DISABLE_ALEXA ; RAM 116 bytes; FLASH 13524 bytes - -D WLED_DISABLE_MQTT ; RAM 216 bytes; FLASH 16496 bytes - -D WLED_DISABLE_HUESYNC ;RAM 122 bytes; FLASH 6308 bytes - ; RAM: [== ] 24.7% (used 80948 bytes from 327680 bytes) - ; Flash: [==========] 97.4% (used 1532485 bytes from 1572864 bytes) - ;-D WLED_DISABLE_INFRARED ;RAM 136 bytes; FLASH 24492 bytes - ; -D WLED_ENABLE_DMX - ; -D WLED_DEBUG - ; -D SR_DEBUG - ; -D MIC_LOGGER - -[env:esp32S3_8MB_max] -extends = esp32_4MB_V4_max_base -board = esp32-s3-devkitc-1 -build_unflags = - -D USERMOD_DALLASTEMPERATURE ;; disabled because it hangs during usermod setup on -S3 (autodetect broken?) - -D USE_ALT_DISPLAY ;; four line display seems to have problems with I2C - it hangs during usermod setup - -D USERMOD_FOUR_LINE_DISPLAY ;; see above - -D USERMOD_ROTARY_ENCODER_UI ;; see above - -D WLED_ENABLE_DMX ;; disabled because it does not work with ESP-IDF 4.4.x (buggy driver in SparkFunDMX) - -build_flags = ${esp32_4MB_V4_max_base.build_flags} - -D WLED_RELEASE_NAME=esp32S3_8MB_max - -D ARDUINO_USB_MODE=1 -D ARDUINO_USB_CDC_ON_BOOT=0 -D ARDUINO_USB_MSC_ON_BOOT=0 -D ARDUINO_USB_DFU_ON_BOOT=0 - -D WLED_WATCHDOG_TIMEOUT=0 - ; -D U8X8_HAVE_2ND_HW_I2C ;; experimental - tells U8g2 lib that a second HW I2C unit exists - -D WLED_DISABLE_BLYNK ;; seems that BLYNK does not work on -S3 - -D WLED_DISABLE_LOXONE ; FLASH 1272 bytes - -D WLED_DISABLE_ALEXA ; RAM 116 bytes; FLASH 13524 bytes - ; -D WLED_DISABLE_MQTT ; RAM 216 bytes; FLASH 16496 bytes - ; -D WLED_DISABLE_HUESYNC ;RAM 122 bytes; FLASH 6308 bytes - ; -D WLED_DISABLE_INFRARED ;RAM 136 bytes; FLASH 24492 bytes - -D LEDPIN=4 - -D STATUSLED=39 - -D BTNPIN=48 - -D RLYPIN=21 - -D IRPIN=15 - -D HW_PIN_SDA=42 ;; ESP32-S3 default: SDA = 8 - -D HW_PIN_SCL=41 ;; ESP32-S3 default: SCL = 9 - -D AUDIOPIN=-1 - -D SR_DMTYPE=1 - -D I2S_SDPIN=16 - -D I2S_CKPIN=17 - -D I2S_WSPIN=47 - -D MCLK_PIN=40 - -D ES7243_SDAPIN=8 - -D ES7243_SCLPIN=18 - ; -D WLED_DEBUG - ; -D SR_DEBUG - ; -D MIC_LOGGER -lib_deps = ${esp32s3.lib_deps} ${common_mm.lib_deps_min} ${common_mm.lib_deps_V4_max} -;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation -board_build.partitions = tools/WLED_ESP32_8MB.csv -board_build.flash_mode = qio -; RAM: [== ] 24.7% (used 80856 bytes from 327680 bytes) -; Flash: [======= ] 66.9% (used 1403489 bytes from 2097152 bytes) - -;; MM max for Adafruit QT Py ESP32-S2 -> 4MB flash, PSRAM, and tinyUF2 bootloader -;; to ewowi - i'll optimize this entry later, as a few things can be inherited for sure. To softhack: sure ;-) -[env:esp32s2_tinyUF2_PSRAM_max] -extends = esp32_4MB_V4_min_base -platform = ${esp32.platformV4} -platform_packages = ${esp32.platformV4_packages} - -board = adafruit_qtpy_esp32s2 -board_build.partitions = tools/partitions-4MB-tinyuf2_spiffs.csv ;; this is needed for tinyUF2 bootloader! -board_build.f_flash = 80000000L -board_build.flash_mode = qio -upload_speed = 256000 ;; 921600 -build_unflags = ${common.build_unflags} - -D USERMOD_DALLASTEMPERATURE ;; disabled because it hangs during usermod setup on -S2 (autodetect broken?) - -D USE_ALT_DISPLAY ;; four line display seems to have problems with I2C - it hangs during usermod setup - -D USERMOD_FOUR_LINE_DISPLAY ;; see above - -D USERMOD_ROTARY_ENCODER_UI ;; see above - -D WLED_ENABLE_DMX ;; disabled because it does not work with ESP-IDF 4.4.x (buggy driver in SparkFunDMX) - -build_flags = ${common.build_flags} ${esp32.build_flagsV4} ${esp32s2.build_flags} - -D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0 - ${common_mm.build_flags_min} ${common_mm.build_flags_max} - -Wno-misleading-indentation -Wno-format-truncation - -D WLED_RELEASE_NAME=esp32S2_4MB_UF2_max - -DARDUINO_USB_CDC_ON_BOOT=1 ;; mandatory, otherwise USB does not work!! - -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 - -D SERVERNAME='"WLED-S2"' - -D WLED_USE_PSRAM - -D WLED_DISABLE_INFRARED ;; save flash space - -D WLED_DISABLE_ALEXA ;; save flash space - -D LEDPIN=39 ;; onboard neopixel LED. Attach your own LEDs to GPIO 7 or GPIO 6 - -D BTNPIN=0 - ;-D RLYPIN=6 - ;-D IRPIN=7 - -D HW_PIN_SCL=40 -D HW_PIN_SDA=41 - -D HW_PIN_MOSISPI=35 ;WLEDMM renamed from HW_PIN_DATASPI - -D HW_PIN_CLOCKSPI=36 - -D HW_PIN_MISOSPI=37 - -D AUDIOPIN=-1 - -D SR_DMTYPE=1 - -D I2S_SDPIN=9 - -D I2S_WSPIN=8 - -D I2S_CKPIN=17 - -D MCLK_PIN=18 - ;-D STATUSLED=-1 - -D WLED_USE_MY_CONFIG - ; -D WLED_DEBUG_HOST='"192.168.xxx.xxx"' ;; to send debug messages over network to host 192.168.x.y - FQDN is also possible - ; -D WLED_DEBUG_NET_PORT=1768 ;; port for network debugging. default = 7868 - ; -D WLED_DEBUG - ; -D SR_DEBUG - ; -D MIC_LOGGER -lib_deps = ${env.lib_deps} ${esp32s2.lib_deps} ${common_mm.lib_deps_min} ${common_mm.lib_deps_V4_max} -lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation -monitor_filters = esp32_exception_decoder -; RAM: [=== ] 25.6% (used 83796 bytes from 327680 bytes) -; Flash: [==========] 97.7% (used 1408626 bytes from 1441792 bytes) - -;; MM max config for ESP32-C3 -> 4MB flash, no PSRAM -;; to ewowi - i'll optimize this entry later ;-) really, I mean -[env:esp32c3dev_4MB_max] -extends = esp32_4MB_V4_min_base -;platform = ${esp32.platformV4} -;platform_packages = ${esp32.platformV4_packages} -platform = espressif32@~5.2.0 ;; alternative platform, might help in case you experience bootloops due to corrupted flash filesystem -platform_packages = -board = esp32-c3-devkitm-1 -upload_speed = 256000 ;; 921600 - -build_unflags = ${common.build_unflags} - -D USERMOD_DALLASTEMPERATURE ;; disabled because it hangs during usermod setup on -S2 (autodetect broken?) - -D USE_ALT_DISPLAY ;; four line display seems to have problems with I2C - it hangs during usermod setup - -D USERMOD_FOUR_LINE_DISPLAY ;; see above - -D USERMOD_ROTARY_ENCODER_UI ;; see above - -D WLED_ENABLE_DMX ;; disabled because it does not work with ESP-IDF 4.4.x (buggy driver in SparkFunDMX) - -build_flags = ${common.build_flags} ${esp32.build_flagsV4} ${esp32c3.build_flags} - -D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0 - ${common_mm.build_flags_min} ${common_mm.build_flags_max} - -Wno-misleading-indentation -Wno-format-truncation - -D WLED_RELEASE_NAME=esp32c3dev_4MB_max - ; -DARDUINO_USB_CDC_ON_BOOT=1 ;; enable CDC USB -> needed for debugging over serial USB - -DARDUINO_USB_CDC_ON_BOOT=0 ;; disable CDC USB - -D SERVERNAME='"WLED-C3"' - ;-D WLEDMM_WIFI_POWERON_HACK ;; use this _only_ if your device is not able to make a WiFI connection! - ;-D WLED_DISABLE_INFRARED ;; save flash space - ;-D WLED_DISABLE_ALEXA ;; save flash space - -D LEDPIN=8 ;; onboard neopixel 5x5 Matrix. Attach your own LEDs to GPIO 20 - -D BTNPIN=9 - ; -D STATUSLED=10 ;; onboard LED - ;-D RLYPIN=3 - ;-D IRPIN=2 - ;-D HW_PIN_SDA=0 -D HW_PIN_SCL=1 ;; for I2C Qwiic connector - -D SR_DMTYPE=1 -D I2S_SDPIN=5 -D I2S_WSPIN=6 -D I2S_CKPIN=4 -D MCLK_PIN=7 - -D WLED_USE_MY_CONFIG - -lib_deps = ${env.lib_deps} ${esp32c3.lib_deps} ${common_mm.lib_deps_min} ${common_mm.lib_deps_V4_max} -;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation -;monitor_filters = esp32_exception_decoder - # ------------------------------------------------------------------------------ # custom board configurations # ------------------------------------------------------------------------------ -[wemos_shield_esp32_4MB_max_base] -extends = esp32_4MB_max_base -build_flags = ${esp32_4MB_max_base.build_flags} - -D ABL_MILLIAMPS_DEFAULT=9500 ; Wemos max 10A - -D LEDPIN=16 - -D RLYPIN=19 - -D BTNPIN=17 - -D IRPIN=18 - -D AUDIOPIN=-1 - -D TEMPERATURE_PIN=23 - -D FLD_PIN_SCL=-1 -D FLD_PIN_SDA=-1 ; use global! - -D HW_PIN_SCL=22 -D HW_PIN_SDA=21 - -D HW_PIN_CLOCKSPI=-1 -D HW_PIN_MOSISPI=-1 -D HW_PIN_MISOSPI=-1 ; WLEDMM: is now also default but just to show we didn't agree on wemos pins for spi yet - -D ENCODER_DT_PIN=35 -D ENCODER_CLK_PIN=5 -D ENCODER_SW_PIN=39 ;WLEDMM spec by @SERG74: use 35 and 39 instead of 18 and 19 (conflicts) - -D PIR_SENSOR_PIN=-1 - -D PWM_PIN=-1 - ; -D WLED_USE_MY_CONFIG - -[wemos_shield_esp32_4MB_all_base] -extends = wemos_shield_esp32_4MB_max_base -build_flags = ${wemos_shield_esp32_4MB_max_base.build_flags} ${common_mm.build_flags_all} -lib_deps = ${wemos_shield_esp32_4MB_max_base.lib_deps} ${common_mm.lib_deps_all} - -[env:wemos_shield_esp32_4MB_max] -extends = wemos_shield_esp32_4MB_max_base -build_flags = ${wemos_shield_esp32_4MB_max_base.build_flags} - -D WLED_RELEASE_NAME=wemos_shield_esp32_4MB_max -; RAM: [== ] 24.4% (used 79820 bytes from 327680 bytes) -; Flash: [========= ] 88.6% (used 1393421 bytes from 1572864 bytes) - -[env:wemos_shield_esp32_4MB_ICS4343x_max] -extends = wemos_shield_esp32_4MB_max_base -build_flags = ${wemos_shield_esp32_4MB_max_base.build_flags} ${Shield_ICS4343x.build_flags} - -D WLED_RELEASE_NAME=wemos_shield_esp32_4MB_ICS4343x_max -; RAM: [== ] 24.4% (used 79820 bytes from 327680 bytes) -; Flash: [========= ] 88.6% (used 1393421 bytes from 1572864 bytes) - -[env:wemos_shield_esp32_4MB_SPM1423_max] -extends = wemos_shield_esp32_4MB_max_base -build_flags = ${wemos_shield_esp32_4MB_max_base.build_flags} ${Shield_SPM1423.build_flags} - -D WLED_RELEASE_NAME=wemos_shield_esp32_4MB_SPM1423_max -; RAM: [== ] 24.4% (used 79820 bytes from 327680 bytes) -; Flash: [========= ] 88.6% (used 1393421 bytes from 1572864 bytes) - -[env:wemos_shield_esp32_4MB_LineIn_max] -extends = wemos_shield_esp32_4MB_max_base -build_flags = ${wemos_shield_esp32_4MB_max_base.build_flags} ${Shield_LineIn.build_flags} - -D WLED_RELEASE_NAME=wemos_shield_esp32_4MB_LineIn_max - -[env:wemos_shield_esp32_16MB_max] -extends = wemos_shield_esp32_4MB_max_base -build_flags = ${wemos_shield_esp32_4MB_max_base.build_flags} - -D WLED_RELEASE_NAME=wemos_shield_esp32_16MB_max -board = ${Board_ESP32_16MB.board} -board_build.partitions = ${Board_ESP32_16MB.board_build.partitions} -; RAM: [== ] 24.4% (used 79820 bytes from 327680 bytes) -; Flash: [======= ] 66.4% (used 1393421 bytes from 2097152 bytes) - -[env:wemos_shield_esp32_16MB_ICS4343x_max] -extends = wemos_shield_esp32_4MB_max_base -build_flags = ${wemos_shield_esp32_4MB_max_base.build_flags} ${Shield_ICS4343x.build_flags} - -D WLED_RELEASE_NAME=wemos_shield_esp32_16MB_ICS4343x_max -board = ${Board_ESP32_16MB.board} -board_build.partitions = ${Board_ESP32_16MB.board_build.partitions} -; RAM: [== ] 24.4% (used 79820 bytes from 327680 bytes) -; Flash: [========= ] 88.6% (used 1393421 bytes from 1572864 bytes) - -[env:wemos_shield_esp32_16MB_ICS4343x_all] -extends = wemos_shield_esp32_4MB_all_base -build_flags = ${wemos_shield_esp32_4MB_all_base.build_flags} ${Shield_ICS4343x.build_flags} - -D WLED_RELEASE_NAME=wemos_shield_esp32_16MB_ICS4343x_all -board = ${Board_ESP32_16MB.board} -board_build.partitions = ${Board_ESP32_16MB.board_build.partitions} -; RAM: [== ] 24.4% (used 79820 bytes from 327680 bytes) -; Flash: [========= ] 88.6% (used 1393421 bytes from 1572864 bytes) -monitor_filters = esp32_exception_decoder ; used to show crash details - -[env:wemos_shield_esp32_16MB_SPM1423_max] -extends = wemos_shield_esp32_4MB_max_base -build_flags = ${wemos_shield_esp32_4MB_max_base.build_flags} ${Shield_SPM1423.build_flags} - -D WLED_RELEASE_NAME=wemos_shield_esp32_16MB_SPM1423_max -board = ${Board_ESP32_16MB.board} -board_build.partitions = ${Board_ESP32_16MB.board_build.partitions} -; RAM: [== ] 24.4% (used 79820 bytes from 327680 bytes) -; Flash: [========= ] 88.6% (used 1393421 bytes from 1572864 bytes) -monitor_filters = esp32_exception_decoder ; used to show crash details - -[env:wemos_shield_esp32_16MB_SPM1423_all] -extends = wemos_shield_esp32_4MB_all_base -build_flags = ${wemos_shield_esp32_4MB_all_base.build_flags} ${Shield_SPM1423.build_flags} - -D WLED_RELEASE_NAME=wemos_shield_esp32_16MB_SPM1423_all -board = ${Board_ESP32_16MB.board} -board_build.partitions = ${Board_ESP32_16MB.board_build.partitions} -; RAM: [== ] 24.4% (used 79820 bytes from 327680 bytes) -; Flash: [========= ] 88.6% (used 1393421 bytes from 1572864 bytes) -monitor_filters = esp32_exception_decoder ; used to show crash details - -[env:wemos_shield_esp32_16MB_LineIn_max] -extends = wemos_shield_esp32_4MB_max_base -build_flags = ${wemos_shield_esp32_4MB_max_base.build_flags} ${Shield_LineIn.build_flags} - -D WLED_RELEASE_NAME=wemos_shield_esp32_16MB_LineIn_max -board = ${Board_ESP32_16MB.board} -board_build.partitions = ${Board_ESP32_16MB.board_build.partitions} - -; ESP32 WLED pico board with builtin ICS-43432 microphpone -[env:esp32_pico_4MB_max] -extends = esp32_4MB_max_base -board = pico32 -build_flags = ${esp32_4MB_max_base.build_flags} - -D WLED_RELEASE_NAME=esp32_pico_4MB_max - -D WLED_DISABLE_BROWNOUT_DET - -D SERVERNAME='"WLED-pico32"' - ; -D WLED_WATCHDOG_TIMEOUT=60 - ; -D WLED_DEBUG - ; -D SR_DEBUG - -D LEDPIN=2 - -D RLYPIN=-1 - -D BTNPIN=-1 - -D IRPIN=-1 - -D HW_PIN_SCL=22 -D HW_PIN_SDA=21 - -D SR_DMTYPE=1 -D I2S_SDPIN=25 -D I2S_WSPIN=15 -D I2S_CKPIN=14 - -D SR_SQUELCH=5 -D SR_GAIN=30 -D SR_FREQ_PROF=5 ; ICS-43434 specific - ; -D MCLK_PIN=0 - -D SR_ENABLE_DEFAULT ;; enable at first start - no need to manually set "enable", then reboot - ; -D WLED_USE_MY_CONFIG - ; -D WLED_DISABLE_LOXONE - ; -D WLED_DISABLE_ALEXA - ; -D WLED_DISABLE_HUESYNC - ; -D WLED_DISABLE_MQTT - ; -D WLED_DISABLE_INFRARED - ; -D WLED_ENABLE_DMX -; RAM: [== ] 24.4% (used 79804 bytes from 327680 bytes) -; Flash: [========= ] 88.6% (used 1394241 bytes from 1572864 bytes) +; WLEDMM see below +; [env:wemos_shield_esp32] +; board = esp32dev +; platform = espressif32@3.2 +; upload_speed = 460800 +; build_unflags = ${common.build_unflags} +; build_flags = ${common.build_flags_esp32} +; -D LEDPIN=16 +; -D RLYPIN=19 +; -D BTNPIN=17 +; -D IRPIN=18 +; -D UWLED_USE_MY_CONFIG +; -D USERMOD_DALLASTEMPERATURE +; -D USERMOD_FOUR_LINE_DISPLAY +; -D TEMPERATURE_PIN=23 +; -D USE_ALT_DISPlAY ; new versions of USERMOD_FOUR_LINE_DISPLAY and USERMOD_ROTARY_ENCODER_UI +; -D USERMOD_AUDIOREACTIVE +; lib_deps = ${esp32.lib_deps} +; OneWire@~2.3.5 +; olikraus/U8g2 @ ^2.28.8 +; https://github.com/blazoncek/arduinoFFT.git +; board_build.partitions = ${esp32.default_partitions} [env:m5atom] board = esp32dev @@ -1289,20 +671,58 @@ board_build.ldscript = ${common.ldscript_2m512k} build_flags = ${common.build_flags_esp8266} -D LEDPIN=3 -D BTNPIN=2 -D IRPIN=5 -D WLED_MAX_BUTTONS=3 lib_deps = ${esp8266.lib_deps} -[env:athom7w] -board = esp_wroom_02 +[env:Athom_RGBCW] ;7w and 5w(GU10) bulbs +board = esp8285 platform = ${common.platform_wled_default} +platform_packages = ${common.platform_packages} board_build.ldscript = ${common.ldscript_2m512k} -build_flags = ${common.build_flags_esp8266} -D WLED_MAX_CCT_BLEND=0 -D BTNPIN=-1 -D IRPIN=-1 -D WLED_DISABLE_INFRARED +build_unflags = ${common.build_unflags} +build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,13,5 + -D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 lib_deps = ${esp8266.lib_deps} -[env:athom15w] -board = esp_wroom_02 + +[env:Athom_15w_RGBCW] ;15w bulb +board = esp8285 platform = ${common.platform_wled_default} +platform_packages = ${common.platform_packages} board_build.ldscript = ${common.ldscript_2m512k} -build_flags = ${common.build_flags_esp8266} -D WLED_USE_IC_CCT -D BTNPIN=-1 -D IRPIN=-1 -D WLED_DISABLE_INFRARED +build_unflags = ${common.build_unflags} +build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,5,13 + -D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 -D WLED_USE_IC_CCT lib_deps = ${esp8266.lib_deps} + +[env:Athom_3Pin_Controller] ;small controller with only data +board = esp8285 +platform = ${common.platform_wled_default} +platform_packages = ${common.platform_packages} +board_build.ldscript = ${common.ldscript_2m512k} +build_unflags = ${common.build_unflags} +build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=0 -D RLYPIN=-1 -D LEDPIN=1 -D WLED_DISABLE_INFRARED +lib_deps = ${esp8266.lib_deps} + + +[env:Athom_4Pin_Controller] ; With clock and data interface +board = esp8285 +platform = ${common.platform_wled_default} +platform_packages = ${common.platform_packages} +board_build.ldscript = ${common.ldscript_2m512k} +build_unflags = ${common.build_unflags} +build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=0 -D RLYPIN=12 -D LEDPIN=1 -D WLED_DISABLE_INFRARED +lib_deps = ${esp8266.lib_deps} + + +[env:Athom_5Pin_Controller] ;Analog light strip controller +board = esp8285 +platform = ${common.platform_wled_default} +platform_packages = ${common.platform_packages} +board_build.ldscript = ${common.ldscript_2m512k} +build_unflags = ${common.build_unflags} +build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=0 -D RLYPIN=-1 DATA_PINS=4,12,14,13 -D WLED_DISABLE_INFRARED +lib_deps = ${esp8266.lib_deps} + + [env:MY9291] board = esp01_1m platform = ${common.platform_wled_default} @@ -1348,7 +768,6 @@ build_flags = ${common.build_flags_esp32} -D WLED_DISABLE_BROWNOUT_DET -D WLED_D -D LEDPIN=12 -D RLYPIN=27 -D BTNPIN=34 - -D WLED_DISABLE_BLYNK -D DEFAULT_LED_COUNT=6 # Display config -D ST7789_DRIVER @@ -1367,3 +786,969 @@ lib_deps = ${esp32.lib_deps} TFT_eSPI @ ^2.3.70 board_build.partitions = ${esp32.default_partitions} + + + + + + + +# ------------------------------------------------------------------------------ +# MoonModules environments +# see https://mm.kno.wled.ge/moonmodules/platformio-entries/ +# ------------------------------------------------------------------------------ + +; shared build flags and lib deps for minimum and maximum environment +[common_mm] +build_flags_S = + -Wall -Wformat -Woverflow -Wuninitialized -Winit-self -Warray-bounds ; enables more warnings + -Wno-attributes -Wno-unused-variable -Wno-unused-function -Wno-deprecated-declarations ;disables some stupid warnings + ;-D WLED_DISABLE_BROWNOUT_DET ; enable if you get "brownout detected" errors at startup + -D WLED_USE_MY_CONFIG + ; -D WLED_USE_CIE_BRIGHTNESS_TABLE ;; experimental: use different color / brightness lookup table + -D USERMOD_AUDIOREACTIVE + -D UM_AUDIOREACTIVE_USE_NEW_FFT ; use latest (upstream) FFTLib, instead of older library modified by blazoncek. Slightly faster, more accurate, needs 2KB RAM extra + -D USERMOD_ARTIFX ; WLEDMM usermod + ; -D WLED_DISABLE_LOXONE + ; -D WLED_DISABLE_ALEXA + ; -D WLED_DISABLE_HUESYNC + ; -D WLED_DISABLE_MQTT + ; -D WLED_DISABLE_INFRARED + ; -D WLED_ENABLE_DMX + +lib_deps_S = + https://github.com/kosme/arduinoFFT#develop @ 1.9.2 ; used for USERMOD_AUDIOREACTIVE + +build_flags_M = + -D WLED_MAX_USERMODS=25 ; default only 4-6, also for _XL configs takes 25 pointers in memory + ;; -D ARDUINO_USB_CDC_ON_BOOT=0 ; needed for arduino-esp32 >=2.0.4 ewowi to softhack: move to build_flags_S? - We need a different solution + -D WLED_USE_MY_CONFIG ; include custom my_config.h ewowi to softhack: redundant as also in build_flags_S? + -D USERMOD_DALLASTEMPERATURE + -D USE_ALT_DISPLAY ; new versions of USERMOD_FOUR_LINE_DISPLAY and USERMOD_ROTARY_ENCODER_UI + -D USERMOD_FOUR_LINE_DISPLAY + -D USERMOD_ROTARY_ENCODER_UI + -D USERMOD_AUTO_SAVE + -D USERMOD_WEATHER ; WLEDMM usermod + -D USERMOD_MPU6050_IMU ; gyro/accelero for USERMOD_GAMES (ONLY WORKS IF USERMOD_FOUR_LINE_DISPLAY NOT INCLUDED - I2C SHARING BUG) + -D USERMOD_GAMES ; WLEDMM usermod + ; -D USERMOD_FASTLED ; WLEDMM usermod: CC BY-NC 3.0 licensed effects by Stefan Petrick, include this usermod only if you accept the terms! + ;WLEDMM: only setting WLED_DEBUG_HOST is enough, ip and port can be defined in sync settings as well + -D WLED_DEBUG_HOST='"192.168.x.x"' ;; to send debug messages over network to host 192.168.x.y - FQDN is also possible + -D WLED_DEBUG_PORT=1768 ;; port for network debugging. default = 7868 + +lib_deps_M = + ;https://github.com/blazoncek/OneWire.git ; includes bugfixes for inconsistent readings + OneWire@~2.3.5 ; used for USERMOD_FOUR_LINE_DISPLAY and USERMOD_DALLASTEMPERATURE + olikraus/U8g2 @ ^2.28.8 ; used for USERMOD_FOUR_LINE_DISPLAY + ElectronicCats/MPU6050 @ 0.6.0 ; used for USERMOD_MPU6050_IMU + +lib_deps_V4_M = + ;https://github.com/blazoncek/OneWire.git ; includes bugfixes for inconsistent readings + paulstoffregen/OneWire@ ^2.3.7 ; used for USERMOD_DALLASTEMPERATURE -> need newer release with bugfixes for -S3; still requires TEMPERATURE_PIN < 46 + olikraus/U8g2@ ^2.34.5 ; used for USERMOD_FOUR_LINE_DISPLAY -> need newer version with bugfixes for arduino-esp32 v2.0.4 (Wire inititialization) + ElectronicCats/MPU6050 @ 0.6.0 ; used for USERMOD_MPU6050_IMU + +build_flags_XL = + -D USERMOD_BATTERY ;; enable Battery usermod + -D USERMOD_BATTERY_USE_LIPO ;; use new "decharging curve" for LiPo cells + -D USERMOD_BH1750 + -D USERMOD_ANIMATED_STAIRCASE + -D USERMOD_RTC ;; experimental + ; -D USERMOD_SENSORSTOMQTT ;; experimental ewowi causes error: fatal error: Adafruit_Sensor.h: No such file or directory + -D USERMOD_ANALOG_CLOCK + -D USERMOD_MULTI_RELAY + -D USERMOD_PIRSWITCH + -D USERMOD_PWM_FAN + ; -D USERMOD_PING_PONG_CLOCK //Removed as dots is confusing + -D USERMOD_BUZZER + -D USERMOD_SN_PHOTORESISTOR + -D USERMOD_BME280 + -D USERMOD_DHT + -D USERMOD_VL53L0X_GESTURES + -D WLED_ENABLE_PIXART + +lib_deps_XL = + claws/BH1750 @^1.2.0 ; used for USERMOD_BH1750 + ; adafruit/Adafruit BMP280 Library @ 2.1.0 ;; experimental for usermod USERMOD_SENSORSTOMQTT + ; adafruit/Adafruit CCS811 Library @ 1.0.4 ;; experimental for usermod USERMOD_SENSORSTOMQTT + ; adafruit/Adafruit Si7021 Library @ 1.4.0 ;; experimental for usermod USERMOD_SENSORSTOMQTT + BME280@~3.0.0 ; for usermod USERMOD_BME280 + https://github.com/alwynallan/DHT_nonblocking ; for usermod USERMOD_DHT + pololu/VL53L0X @ ^1.3.0 ; for usermod USERMOD_VL53L0X_GESTURES + +; end of common + +; base entries (without WLED_RELEASE_NAME) + +; common defaults for all MM environments +[esp32_4MB_S_base] +board = esp32dev +platform = ${esp32.platform} +upload_speed = 460800 ; or 921600 +platform_packages = ${esp32.platform_packages} +build_unflags = ${common.build_unflags} +build_flags = ${common.build_flags_esp32} ${common_mm.build_flags_S} +lib_deps = ${esp32.lib_deps} ${common_mm.lib_deps_S} +board_build.partitions = ${esp32.default_partitions} +board_build.f_flash = 80000000L ; use full 80MHz speed for flash (default = 40Mhz) +board_build.flash_mode = dio ; (dio = dual i/o; more compatible than qio = quad i/o) + +;common default for all max environments +[esp32_4MB_M_base] +extends = esp32_4MB_S_base +build_flags = ${esp32_4MB_S_base.build_flags} ${common_mm.build_flags_M} +lib_deps = ${esp32_4MB_S_base.lib_deps} ${common_mm.lib_deps_M} +; board_build.partitions = tools/WLED_ESP32-wrover_4MB.csv + +[esp32_4MB_XL_base] +extends = esp32_4MB_M_base +build_flags = ${esp32_4MB_M_base.build_flags} ${common_mm.build_flags_XL} +lib_deps = ${esp32_4MB_M_base.lib_deps} ${common_mm.lib_deps_XL} +; board_build.partitions = tools/WLED_ESP32-wrover_4MB.csv + +;common default for all V4 min environments +[esp32_4MB_V4_S_base] +board = esp32dev +upload_speed = 460800 ; or 921600 +platform = ${esp32.platformV4} +platform_packages = ${esp32.platformV4_packages} +build_unflags = ${common.build_unflags} +build_flags = ${common.build_flags} ${esp32.build_flagsV4} ${common_mm.build_flags_S} + -Wno-misleading-indentation -Wno-format-truncation + -Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one + -D WLED_ENABLE_DMX_INPUT +lib_deps = ${esp32.lib_depsV4} ${common_mm.lib_deps_S} +board_build.partitions = ${esp32.default_partitions} +board_build.f_flash = 80000000L ; use full 80MHz speed for flash (default = 40Mhz) +board_build.flash_mode = dio ; (dio = dual i/o; more compatible than qio = quad i/o) +;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation +; monitor_filters = esp32_exception_decoder ; used to show crash details + +[esp32_4MB_V4_M_base] +extends = esp32_4MB_V4_S_base +build_flags = ${esp32_4MB_V4_S_base.build_flags} ${common_mm.build_flags_M} +lib_deps = ${esp32_4MB_V4_S_base.lib_deps} ${common_mm.lib_deps_V4_M} +board_build.partitions = ${esp32_4MB_V4_S_base.board_build.partitions} +;board_build.flash_mode = qio ; (dio = dual i/o; more compatible than qio = quad i/o) + +[Shield_ICS4343x] +build_flags = + -D SR_DMTYPE=1 -D I2S_SDPIN=32 -D I2S_WSPIN=15 -D I2S_CKPIN=14 ; for regular I2S microphone + -D SR_SQUELCH=10 -D SR_GAIN=30 -D SR_FREQ_PROF=5 ; ICS-43434 specific + +[Shield_SPM1423] +build_flags = + -D SR_DMTYPE=5 -D I2S_SDPIN=32 -D I2S_WSPIN=15 -D I2S_CKPIN=-1 ; for I2S PDM microphone + -D SR_SQUELCH=3 -D SR_GAIN=75 -D SR_FREQ_PROF=7 ; SPM1423 specific + +[Athom_PDMmic] +build_flags = + -D SR_DMTYPE=51 -D I2S_SDPIN=32 -D I2S_WSPIN=15 -D I2S_CKPIN=-1 ; for I2S PDM microphone Legacy mode! + -D SR_SQUELCH=10 -D SR_GAIN=40 -D SR_FREQ_PROF=7 ; SPM1423 specific + +[Shield_LineIn] +build_unflags = + -D BTNPIN=17 ;; remove - its in conflict with on-shield rotary + -D ENCODER_SW_PIN=5 ;; remove - its in conflict with on-shield rotary + -D BTNPIN=0 ;; remove - its in conflict with MCLK pin +build_flags = + -D BTNPIN=-1 + -D SR_DMTYPE=4 -D MCLK_PIN=0 -D I2S_SDPIN=26 -D I2S_WSPIN=25 -D I2S_CKPIN=27 ; for audio Line-In shield, final version + -D SR_LINE_DETECT=34 ; line-in plug insert detection sensor (future support) + -D SR_SQUELCH=8 -D SR_GAIN=40 -D SR_FREQ_PROF=1 ; CS5343 Line-In specific + -D ENCODER_DT_PIN=35 -D ENCODER_CLK_PIN=39 -D ENCODER_SW_PIN=17 ; on-shield rotary encoder + +[Debug_Flags] +build_flags = + -D WLED_DEBUG ; lots of generic debug messages + -D SR_DEBUG ; some extra debug messages from audioreactive + ; -D MIC_LOGGER ; for sound input monitoring & debugging (use arduino serial plotter) + ; NetDebug moved to build_flags_M + + +; end of base entries + + +; bin entries (with WLED_RELEASE_NAME) + +[env:esp32_4MB_S] +extends = esp32_4MB_S_base +build_flags = ${esp32_4MB_S_base.build_flags} + -D WLED_RELEASE_NAME=esp32_4MB_S +; RAM: [== ] 24.1% (used 78988 bytes from 327680 bytes) +; Flash: [========= ] 85.7% (used 1348593 bytes from 1572864 bytes) WLEDMM: Earlier 83.7 + +[env:esp32_4MB_M] +extends = esp32_4MB_M_base +build_flags = ${esp32_4MB_M_base.build_flags} + -D WLED_RELEASE_NAME=esp32_4MB_M +; RAM: [== ] 24.4% (used 79956 bytes from 327680 bytes) +; Flash: [========= ] 91.1% (used 1432245 bytes from 1572864 bytes) WLEDMM: earlier 88.7 + +[env:esp32_4MB_XL] +extends = esp32_4MB_XL_base +build_flags = ${esp32_4MB_XL_base.build_flags} + -D WLED_RELEASE_NAME=esp32_4MB_XL +; RAM: [== ] 24.4% (used 80060 bytes from 327680 bytes) +; Flash: [==========] 95.3% (used 1499037 bytes from 1572864 bytes) + +[env:esp32_16MB_M] +extends = esp32_4MB_M_base +build_flags = ${esp32_4MB_M_base.build_flags} + -D WLED_RELEASE_NAME=esp32_16MB_M +board = esp32_16MB +board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem + ; RAM: [== ] 24.4% (used 79916 bytes from 327680 bytes) + ; Flash: [======= ] 67.0% (used 1405701 bytes from 2097152 bytes) +;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation + +[env:esp32_4MB_M_debug] +extends = esp32_4MB_M_base +build_unflags = ${common.build_unflags} + -D CORE_DEBUG_LEVEL=0 +build_flags = ${esp32_4MB_M_base.build_flags} + ${Debug_Flags.build_flags} + -D CORE_DEBUG_LEVEL=2 ;; 2=warning + -D WLED_RELEASE_NAME=esp32_4MB_M_debug +monitor_filters = esp32_exception_decoder +; RAM: [== ] 24.5% (used 80292 bytes from 327680 bytes) +; Flash: [========= ] 94.5% (used 1487113 bytes from 1572864 bytes) + +[env:esp32_16MB_M_debug] +extends = esp32_4MB_M_base +build_unflags = ${common.build_unflags} + -D CORE_DEBUG_LEVEL=0 +build_flags = ${esp32_4MB_M_base.build_flags} + ${Debug_Flags.build_flags} + -D CORE_DEBUG_LEVEL=2 ;; 2=warning + -D WLED_RELEASE_NAME=esp32_16MB_M_debug +monitor_filters = esp32_exception_decoder +board = esp32_16MB +board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem +; RAM: [== ] 24.5% (used 80292 bytes from 327680 bytes) +; Flash: [======= ] 70.9% (used 1487129 bytes from 2097152 bytes) + +[env:esp32_16MB_XL] +extends = esp32_4MB_XL_base +build_flags = ${esp32_4MB_XL_base.build_flags} + -D WLED_RELEASE_NAME=esp32_16MB_XL +board = esp32_16MB +board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem + ; RAM: [== ] 24.4% (used 79916 bytes from 327680 bytes) + ; Flash: [======= ] 67.0% (used 1405701 bytes from 2097152 bytes) +;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation + +[env:esp32_16MB_M_eth] +extends = esp32_4MB_M_base +board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem +board = esp32_16MB-poe ;; needed for ethernet boards (selects "esp32-poe" as variant) +build_flags = ${esp32_4MB_M_base.build_flags} + -D WLED_RELEASE_NAME=esp32_16MB_M_eth ; This will be included in the firmware.bin filename + -D WLED_USE_ETHERNET +; RAM: [== ] 24.5% (used 80348 bytes from 327680 bytes) +; Flash: [======= ] 69.4% (used 1455233 bytes from 2097152 bytes) + +[env:esp8266_4MB_S] +extends = env:d1_mini +upload_speed = 460800 ;115200 +board_build.f_cpu = 160000000L ;; we want 160Mhz (default = 80Mhz) +build_flags = ${common.build_flags_esp8266} + -D WLED_RELEASE_NAME=esp8266_4MB_S + -D WLED_DISABLE_ALEXA + -D WLED_DISABLE_HUESYNC + ; -D WLED_DISABLE_2D + ; -UWLED_USE_MY_CONFIG + ; -D WLED_DEBUG +; monitor_filters = esp8266_exception_decoder +; RAM: [====== ] 59.3% (used 48608 bytes from 81920 bytes) +; Flash: [======== ] 77.0% (used 804176 bytes from 1044464 bytes) + +[env:esp8266_4MB_M] +extends = env:d1_mini +upload_speed = 460800 ;115200 +board_build.f_cpu = 160000000L ;; we want 160Mhz (default = 80Mhz) +build_flags = ${common.build_flags_esp8266} + -D WLED_RELEASE_NAME=esp8266_4MB_M + -D WLED_DISABLE_ALEXA + -D WLED_DISABLE_HUESYNC + -D WLED_DISABLE_LOXONE + ; -D USERMOD_AUDIOREACTIVE + ; -UWLED_USE_MY_CONFIG + ; -D USERMOD_PIRSWITCH + ; -D USERMOD_DALLASTEMPERATURE ;; disabled because it hangs during usermod setup on -S3 (autodetect broken?) + ; -D USERMOD_MULTI_RELAY + -D USE_ALT_DISPLAY ; new versions of USERMOD_FOUR_LINE_DISPLAY and USERMOD_ROTARY_ENCODER_UI + -D WLED_MAX_USERMODS=5 ; default only 4 on ESP8266 + -D USERMOD_FOUR_LINE_DISPLAY + -D USERMOD_MPU6050_IMU ; gyro/accelero for USERMOD_GAMES (ONLY WORKS IF USERMOD_FOUR_LINE_DISPLAY NOT INCLUDED - I2C SHARING BUG) + -D USERMOD_GAMES ; WLEDMM usermod + -D USERMOD_ARTIFX + -D USERMOD_BATTERY ;; enable Battery usermod + -D USERMOD_BATTERY_USE_LIPO ;; use new "decharging curve" for LiPo cells + -D WLED_DEBUG_HOST='"192.168.x.x"' ;; to send debug messages over network to host 192.168.x.y - FQDN is also possible + -D WLED_DEBUG_PORT=1768 ;; port for network debugging. default = 7868 + ; -D WLED_DEBUG +monitor_filters = esp8266_exception_decoder +lib_deps = ${esp8266.lib_deps} + OneWire@~2.3.5 ; used for USERMOD_FOUR_LINE_DISPLAY and USERMOD_DALLASTEMPERATURE + olikraus/U8g2 @ ^2.28.8 ; used for USERMOD_FOUR_LINE_DISPLAY + ElectronicCats/MPU6050 @ 0.6.0 ; used for USERMOD_MPU6050_IMU +; RAM: [====== ] 61.5% (used 50344 bytes from 81920 bytes) +; Flash: [======== ] 81.8% (used 854444 bytes from 1044464 bytes) + +; Blaz env (for reference purposes) +[env:d1_mini_temp] +extends = env:d1_mini +board_build.filesystem = littlefs +build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 + -D WLED_DISABLE_ALEXA + -D WLED_DISABLE_HUESYNC + -D WLED_DISABLE_LOXONE + -D WLED_DISABLE_AUDIO ;WLEDMM not used anywhere + -D WLED_ENABLE_SIMPLE_UI + -D USERMOD_FOUR_LINE_DISPLAY + -D USE_ALT_DISPlAY + -D USERMOD_DALLASTEMPERATURE + -D TEMPERATURE_PIN=13 # (D7) + -D LEDPIN=2 # (D4) + -D RLYPIN=12 # (D6) + -D BTNPIN=0 # (D3) + -D IRPIN=14 # (D5) + -D USERMOD_MULTI_RELAY + -D MULTI_RELAY_MAX_RELAYS=2 + -D USERMOD_PIRSWITCH + -D PIR_SENSOR_PIN=16 + -D PIR_SENSOR_OFF_SEC=60 + -UWLED_USE_MY_CONFIG +lib_deps = ${esp8266.lib_deps} + paulstoffregen/OneWire@~2.3.7 ;WLEDMM Softhack, we need this as well (instead of 2.3.5)? + olikraus/U8g2 # @~2.33.15 + Wire ; WLEDMM ewowi, needed? + +[env:esp8266pro_16MB_S] +extends = env:d1_mini +board = d1_mini_pro ;; "D1 mini pro": ESP8266EX, 160MHz, 80KB RAM, 16MB Flash +board_build.f_cpu = 160000000L ;; we want 160Mhz (default = 80Mhz) +;board_build.f_flash = 80000000L ;; for 80Mhz flash speed, in case your chip can handle it (default = 40Mhz) +board_build.flash_mode = qio ;; quad IO - fastest speed, in case your chip can handle it. +;;board_build.flash_mode = dout ;; use if your esp8266 becomes unstable with "qio" +board_build.ldscript = ${common.ldscript_16m14m} ;; 16MB flash, use 14MB for LittleFS + +upload_speed = 460800 ;115200 +build_flags = ${common.build_flags_esp8266} + -D WLED_RELEASE_NAME=esp8266pro_16MB_S + -D WLED_WATCHDOG_TIMEOUT=0 + -D WLED_DISABLE_ALEXA + -D WLED_DISABLE_HUESYNC + ; -D WLED_DEBUG + ; -D WLED_DISABLE_2D + ; -D USERMOD_AUDIOREACTIVE + ; -D USERMOD_ARTIFX ; to be done + ; -UWLED_USE_MY_CONFIG +monitor_filters = esp8266_exception_decoder +; RAM: [====== ] 59.3% (used 48616 bytes from 81920 bytes) +; Flash: [======== ] 77.0% (used 804236 bytes from 1044464 bytes) + +[env:esp8266pro_16MB_M] +extends = env:d1_mini +board = d1_mini_pro ;; "D1 mini pro": ESP8266EX, 160MHz, 80KB RAM, 16MB Flash +board_build.f_cpu = 160000000L ;; we want 160Mhz (default = 80Mhz) +;board_build.f_flash = 80000000L ;; for 80Mhz flash speed, in case your chip can handle it (default = 40Mhz) +board_build.flash_mode = qio ;; quad IO - fastest speed, in case your chip can handle it. +;;board_build.flash_mode = dout ;; use if your esp8266 becomes unstable with "qio" +board_build.ldscript = ${common.ldscript_16m14m} ;; 16MB flash, use 14MB for LittleFS + +upload_speed = 460800 ;115200 +build_flags = ${common.build_flags_esp8266} + -D WLED_RELEASE_NAME=esp8266pro_16MB_M + -D WLED_MAX_USERMODS=5 ; default only 4-6 + -D WLED_DISABLE_ALEXA + -D WLED_DISABLE_HUESYNC + -D WLED_DISABLE_LOXONE + ; -D USERMOD_AUDIOREACTIVE + ; -D USERMOD_ARTIFX ; to be done + -D USERMOD_PIRSWITCH + -D USERMOD_DALLASTEMPERATURE ;; disabled because it hangs during usermod setup on -S3 (autodetect broken?) + -D USERMOD_MULTI_RELAY + -D USE_ALT_DISPLAY ; new versions of USERMOD_FOUR_LINE_DISPLAY and USERMOD_ROTARY_ENCODER_UI + -D USERMOD_FOUR_LINE_DISPLAY + -D USERMOD_MPU6050_IMU ; gyro/accelero for USERMOD_GAMES (ONLY WORKS IF USERMOD_FOUR_LINE_DISPLAY NOT INCLUDED - I2C SHARING BUG) + -D USERMOD_GAMES ; WLEDMM usermod + ; -D WLED_DEBUG +monitor_filters = esp8266_exception_decoder +lib_deps = ${esp8266.lib_deps} + OneWire@~2.3.5 ; used for USERMOD_FOUR_LINE_DISPLAY and USERMOD_DALLASTEMPERATURE + olikraus/U8g2 @ ^2.28.8 ; used for USERMOD_FOUR_LINE_DISPLAY + ElectronicCats/MPU6050 @ 0.6.0 ; used for USERMOD_MPU6050_IMU +; RAM: [====== ] 62.4% (used 51092 bytes from 81920 bytes) +; Flash: [========= ] 85.5% (used 893056 bytes from 1044464 bytes) + +[env:esp01_1MB_S] +board = esp01_1m +platform = ${common.platform_wled_default} +platform_packages = ${common.platform_packages} +board_build.ldscript = ${common.ldscript_1m128k} +build_unflags = ${common.build_unflags} +build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA + -D WLED_RELEASE_NAME=esp01_1MB_S + -D WLED_DISABLE_ALEXA + -D WLED_DISABLE_HUESYNC +lib_deps = ${esp8266.lib_deps} +; RAM: [====== ] 59.5% (used 48748 bytes from 81920 bytes) +; Flash: [========= ] 90.7% (used 809992 bytes from 892912 bytes) + + +# ------------------------------------------------------------------------------ +# MoonModules environments for IDF V4.4.x +# ------------------------------------------------------------------------------ +;; Warning: the build-in LittleFS (arduino-esp32 2.0.x) seems to be "slightly different" from Lorol LittleFS. +;; When upgrading to the new framework, it might be necessary to first do a chip erase (make sure you have a backup of cfg.json and presets.json) + +; compiled with ESP-IDF 4.4.1 +[env:esp32_4MB_V4_S] +extends = esp32_4MB_V4_S_base +build_flags = ${esp32_4MB_V4_S_base.build_flags} + -D WLED_RELEASE_NAME=esp32_4MB_V4_S + -D WLED_WATCHDOG_TIMEOUT=0 #-D WLED_DISABLE_BROWNOUT_DET + -D ARDUINO_USB_CDC_ON_BOOT=0 ; needed for arduino-esp32 >=2.0.4; avoids errors on startup + -D WLED_DISABLE_LOXONE + -D WLED_DISABLE_ALEXA + -D WLED_DISABLE_HUESYNC + -D WLED_DISABLE_MQTT + -D WLED_DISABLE_INFRARED + ; -D WLED_DEBUG + ; -D SR_DEBUG + ; -D MIC_LOGGER + ; RAM: [== ] 24.2% (used 79372 bytes from 327680 bytes) + ; Flash: [========= ] 88.8% (used 1396073 bytes from 1572864 bytes) +;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation + +; compiled with ESP-IDF 4.4.1 +[env:esp32_4MB_V4_M] +extends = esp32_4MB_V4_M_base +build_flags = ${esp32_4MB_V4_M_base.build_flags} + -D WLED_RELEASE_NAME=esp32_4MB_V4_M + -D WLED_WATCHDOG_TIMEOUT=0 #-D WLED_DISABLE_BROWNOUT_DET + -D ARDUINO_USB_CDC_ON_BOOT=0 ; needed for arduino-esp32 >=2.0.4; avoids errors on startup + ; RAM: [== ] 24.8% (used 81316 bytes from 327680 bytes) + ; Flash: [==========] 97.2% (used 1528253 bytes from 1572864 bytes) + -D WLED_DISABLE_LOXONE ; FLASH 1272 bytes + -D WLED_DISABLE_ALEXA ; RAM 116 bytes; FLASH 13524 bytes + -D WLED_DISABLE_HUESYNC ;RAM 122 bytes; FLASH 6308 bytes + ; RAM: [== ] 24.7% (used 81076 bytes from 327680 bytes) + ; Flash: [==========] 95.8% (used 1506893 bytes from 1572864 bytes) + ;-D WLED_DISABLE_MQTT ; RAM 216 bytes; FLASH 16496 bytes + ;-D WLED_DISABLE_INFRARED ;RAM 136 bytes; FLASH 24492 bytes +;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation + +; compiled with ESP-IDF 4.4.1 +[env:esp32_16MB_V4_M] +extends = esp32_4MB_V4_M_base +build_flags = ${esp32_4MB_V4_M_base.build_flags} + -D WLED_RELEASE_NAME=esp32_16MB_V4_M + -D WLED_WATCHDOG_TIMEOUT=0 #-D WLED_DISABLE_BROWNOUT_DET + -D ARDUINO_USB_CDC_ON_BOOT=0 ; needed for arduino-esp32 >=2.0.4; avoids errors on startup +board = esp32_16MB +board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem + ; RAM: [== ] 24.8% (used 81316 bytes from 327680 bytes) + ; Flash: [======= ] 72.9% (used 1528253 bytes from 2097152 bytes) + +[env:esp32_16MB_V4_M_debug] +extends = esp32_4MB_V4_M_base +build_unflags = ${common.build_unflags} + -D CORE_DEBUG_LEVEL=0 +build_flags = ${esp32_4MB_V4_M_base.build_flags} + ${Debug_Flags.build_flags} + -D CORE_DEBUG_LEVEL=4 ;; 0=none, 1=error, 2=warning, 3=info, 4=debug, 5=verbose + -D WLED_RELEASE_NAME=esp32_16MB_V4_M_debug + -D WLED_WATCHDOG_TIMEOUT=0 #-D WLED_DISABLE_BROWNOUT_DET + -D ARDUINO_USB_CDC_ON_BOOT=0 ; needed for arduino-esp32 >=2.0.4; avoids errors on startup +board = esp32_16MB +board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem +monitor_filters = esp32_exception_decoder +; RAM: [=== ] 25.0% (used 82008 bytes from 327680 bytes) +; Flash: [======== ] 78.1% (used 1638193 bytes from 2097152 bytes) WLEDMM: Earlier 76.9 + +;; experimental environment for boards with PSRAM (needs ESP-IDF 4.4.1). +[env:esp32_4MB_PSRAM_S] +extends = esp32_4MB_V4_S_base +board = lolin_d32_pro +;board = esp32cam +build_flags = ${esp32_4MB_V4_S_base.build_flags} + -D WLED_RELEASE_NAME=esp32_4MB_PSRAM_S + -D WLED_WATCHDOG_TIMEOUT=0 #-D WLED_DISABLE_BROWNOUT_DET + -D ARDUINO_USB_CDC_ON_BOOT=0 ; needed for arduino-esp32 >=2.0.4; avoids errors on startup + -D WLED_USE_PSRAM + -D WLED_DISABLE_LOXONE ; FLASH 1272 bytes + -D WLED_DISABLE_HUESYNC ; RAM 122 bytes; FLASH 6308 bytes + -D WLED_DISABLE_ALEXA ; RAM 116 bytes; FLASH 13524 bytes + -D WLED_DISABLE_MQTT ; RAM 216 bytes; FLASH 16496 bytes + -D WLED_DISABLE_INFRARED ;RAM 136 bytes; FLASH 24492 bytes ewowi: disabled to stay below 100% + ; -D WLED_ENABLE_DMX + ; -D WLED_DEBUG + ; -D SR_DEBUG + ; -D MIC_LOGGER +lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation +;; RAM: [== ] 24.3% (used 79524 bytes from 327680 bytes) +;; Flash: [========= ] 93.2% (used 1466389 bytes from 1572864 bytes) + +;; PSRAM build env that only leaves 300Kb for filesystem (instead of 1MB), but adds 300kB for program space +[env:esp32_4MB_PSRAM_M] +extends = esp32_4MB_V4_M_base +board = lolin_d32_pro +;board = esp32cam +board_build.partitions = tools/WLED_ESP32_4MB_256KB_FS.csv ;; 1.8MB firmware, 256KB filesystem (esptool erase_flash needed when changing from "standard WLED" partitions) +build_flags = ${esp32_4MB_V4_M_base.build_flags} + -D WLED_RELEASE_NAME=esp32_4MB_PSRAM_M + -D WLED_WATCHDOG_TIMEOUT=0 #-D WLED_DISABLE_BROWNOUT_DET + -D ARDUINO_USB_CDC_ON_BOOT=0 ; needed for arduino-esp32 >=2.0.4; avoids errors on startup + -D WLED_USE_PSRAM + -D WLED_DISABLE_LOXONE ;; FLASH 1272 bytes + -D WLED_DISABLE_HUESYNC ;; RAM 122 bytes; FLASH 6308 bytes + -D WLED_DISABLE_ALEXA ;; RAM 116 bytes; FLASH 13524 bytes + ; -D WLED_DISABLE_MQTT ;; RAM 216 bytes; FLASH 16496 bytes + ; -D WLED_DISABLE_INFRARED ;;RAM 136 bytes; FLASH 24492 bytes + ; -D WLED_ENABLE_DMX + ; -D WLED_DEBUG + ; -D SR_DEBUG + ; -D MIC_LOGGER +;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation +;monitor_filters = esp32_exception_decoder +;; RAM: [== ] 24.9% (used 81484 bytes from 327680 bytes) +;; Flash: [======== ] 84.6% (used 1607857 bytes from 1900544 bytes) + +[env:esp32S3_8MB_M] +extends = esp32_4MB_V4_M_base +board = esp32-s3-devkitc-1 +build_unflags = + -D USERMOD_DALLASTEMPERATURE ;; disabled because it hangs during usermod setup on -S3 (autodetect broken?) + -D USE_ALT_DISPLAY ;; four line display seems to have problems with I2C - it hangs during usermod setup + -D USERMOD_FOUR_LINE_DISPLAY ;; see above + -D USERMOD_ROTARY_ENCODER_UI ;; see above + -D WLED_ENABLE_DMX ;; disabled because it does not work with ESP-IDF 4.4.x (buggy driver in SparkFunDMX) + +build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-indentation -Wno-format-truncation + ${common_mm.build_flags_S} ${common_mm.build_flags_M} + -D WLED_RELEASE_NAME=esp32S3_8MB_M + -D ARDUINO_USB_MODE=1 -D ARDUINO_USB_CDC_ON_BOOT=0 -D ARDUINO_USB_MSC_ON_BOOT=0 -D ARDUINO_USB_DFU_ON_BOOT=0 ;; for Serial-to-USB chip + ;; -D ARDUINO_USB_MODE=1 -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MSC_ON_BOOT=0 -D ARDUINO_USB_DFU_ON_BOOT=0 ;; for Hardware-CDC USB mode + ;; -D WLED_DISABLE_ADALIGHT ;; disables serial protocols when using CDC USB (Serial RX will receive junk commands when RX pin is unconnected, unless its pulled down by resistor) + -D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0 + ; -D U8X8_HAVE_2ND_HW_I2C ;; experimental - tells U8g2 lib that a second HW I2C unit exists + -D WLED_DISABLE_LOXONE ; FLASH 1272 bytes + -D WLED_DISABLE_ALEXA ; RAM 116 bytes; FLASH 13524 bytes + ; -D WLED_DISABLE_MQTT ; RAM 216 bytes; FLASH 16496 bytes + ; -D WLED_DISABLE_HUESYNC ;RAM 122 bytes; FLASH 6308 bytes + ; -D WLED_DISABLE_INFRARED ;RAM 136 bytes; FLASH 24492 bytes + -D LEDPIN=4 + -D STATUSLED=39 + -D BTNPIN=48 + -D RLYPIN=21 + -D IRPIN=15 + -D HW_PIN_SDA=42 ;; ESP32-S3 default: SDA = 8 + -D HW_PIN_SCL=41 ;; ESP32-S3 default: SCL = 9 + -D AUDIOPIN=-1 + -D SR_DMTYPE=1 + -D I2S_SDPIN=16 + -D I2S_CKPIN=17 + -D I2S_WSPIN=47 + -D MCLK_PIN=40 + -D ES7243_SDAPIN=8 + -D ES7243_SCLPIN=18 + ; -D WLED_DEBUG + ; -D SR_DEBUG + ; -D MIC_LOGGER +lib_deps = ${esp32s3.lib_deps} ${common_mm.lib_deps_S} ${common_mm.lib_deps_V4_M} +;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation +board_build.partitions = tools/WLED_ESP32_8MB.csv +board_build.flash_mode = qio +; RAM: [== ] 24.7% (used 80856 bytes from 327680 bytes) +; Flash: [======= ] 66.9% (used 1403489 bytes from 2097152 bytes) + +;; MM for Adafruit QT Py ESP32-S2 -> 4MB flash, PSRAM, and tinyUF2 bootloader +;; to ewowi - i'll optimize this entry later, as a few things can be inherited for sure. To softhack: sure ;-) +[env:esp32s2_tinyUF2_PSRAM_S] +extends = esp32_4MB_V4_S_base +platform = ${esp32.platformV4} +platform_packages = ${esp32.platformV4_packages} + +board = adafruit_qtpy_esp32s2 +board_build.partitions = tools/partitions-4MB_spiffs-tinyuf2.csv ;; this is needed for tinyUF2 bootloader! Filename has to end in "tinyuf2.csv" +board_build.f_flash = 80000000L +board_build.flash_mode = qio +upload_speed = 256000 ;; 921600 +build_unflags = ${common.build_unflags} + -D USERMOD_DALLASTEMPERATURE ;; disabled because it hangs during usermod setup on -S3 (autodetect broken?) + -D WLED_ENABLE_DMX ;; disabled because it does not work with ESP-IDF 4.4.x (buggy driver in SparkFunDMX) + -D WLED_ENABLE_DMX_INPUT ;; needs more testing + +build_flags = ${common.build_flags} ${esp32s2.build_flags} + ; ${Debug_Flags.build_flags} ;ewowi: enabling debug causes Error: The program size (1463330 bytes) is greater than maximum allowed (1441792 bytes) + -D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0 + ${common_mm.build_flags_S} + -Wno-misleading-indentation -Wno-format-truncation + -D WLED_RELEASE_NAME=esp32S2_4MB_UF2_S + -DARDUINO_USB_CDC_ON_BOOT=1 ;; mandatory, otherwise USB does not work!! + -D WLED_DISABLE_ADALIGHT ;; disables serial protocols when using CDC USB (Serial RX will receive junk commands, unless its pulled down by resistor) + -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 + -D SERVERNAME='"WLED-S2"' + -D WLED_USE_PSRAM + -D WLED_DISABLE_LOXONE ;; FLASH 1272 bytes + -D WLED_DISABLE_HUESYNC ;; RAM 122 bytes; FLASH 6308 bytes + -D WLED_DISABLE_ALEXA ;; RAM 116 bytes; FLASH 13524 bytes + ; -D WLED_DISABLE_MQTT ;; RAM 216 bytes; FLASH 16496 bytes + -D WLED_DISABLE_INFRARED ;; RAM 136 bytes; FLASH 24492 bytes + -D LEDPIN=39 ;; onboard neopixel LED. Attach your own LEDs to GPIO 7 or GPIO 6 + -D BTNPIN=0 + ;-D RLYPIN=6 + ;-D IRPIN=7 + -D HW_PIN_SCL=40 -D HW_PIN_SDA=41 + -D HW_PIN_MOSISPI=35 ;WLEDMM renamed from HW_PIN_DATASPI + -D HW_PIN_CLOCKSPI=36 + -D HW_PIN_MISOSPI=37 + -D AUDIOPIN=-1 + -D SR_DMTYPE=1 -D I2S_SDPIN=9 -D I2S_WSPIN=8 -D I2S_CKPIN=17 -D MCLK_PIN=18 + ;-D STATUSLED=-1 + -D WLED_USE_MY_CONFIG +lib_deps = ${env.lib_deps} ${esp32s2.lib_deps} ${common_mm.lib_deps_S} +lib_ignore = + IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation + OneWire ; not needed as we don't include USERMOD_DALLASTEMPERATURE +monitor_filters = esp32_exception_decoder +; RAM: [=== ] 25.1% (used 82348 bytes from 327680 bytes) +; Flash: [==========] 95.8% (used 1381742 bytes from 1441792 bytes) + +;; MM environment for generic ESP32-S2, with PSRAM, 4MB flash (300kB filesystem to have more program space) +[env:esp32s2_PSRAM_M] +extends = esp32_4MB_V4_M_base +platform = ${esp32.platformV4_new} ;; more stable on -S2 than 5.1.1 +platform_packages = ${esp32.platformV4_new_packages} + +board = lolin_s2_mini +board_build.partitions = tools/WLED_ESP32_4MB_256KB_FS.csv ;; 1.8MB firmware, 256KB filesystem (esptool erase_flash needed when changing from "standard WLED" partitions) +board_build.flash_mode = dio +upload_speed = 256000 ;; 921600 +build_unflags = ${common.build_unflags} + -DARDUINO_USB_CDC_ON_BOOT=1 + -D USERMOD_DALLASTEMPERATURE ;; disabled because it hangs during usermod setup on -S3 (autodetect broken?) + -D WLED_ENABLE_DMX ;; disabled because it does not work with ESP-IDF 4.4.x (buggy driver in SparkFunDMX) + -D WLED_ENABLE_DMX_INPUT ;; needs more testing +build_flags = ${common.build_flags} ${esp32s2.build_flags} + ${Debug_Flags.build_flags} + -D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0 + ${common_mm.build_flags_S} ${common_mm.build_flags_M} + -Wno-misleading-indentation -Wno-format-truncation + -D WLED_RELEASE_NAME=esp32S2_4MB_M + -DARDUINO_USB_CDC_ON_BOOT=0 + -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 + -D WLED_DISABLE_ADALIGHT ;; disables serial protocols, as the board onl has CDC USB + -D WLED_USE_PSRAM -DBOARD_HAS_PSRAM + -D WLED_DISABLE_INFRARED ;; save flash space + -D WLED_DISABLE_ALEXA ;; save flash space + -D WLED_DISABLE_HUESYNC ;; save flash space + -D WLED_DISABLE_LOXONE ;; save flash space + -D LEDPIN=16 + -D BTNPIN=-1 -D RLYPIN=-1 -D IRPIN=-1 + -D HW_PIN_SCL=40 -D HW_PIN_SDA=41 + -D HW_PIN_MOSISPI=35 ;WLEDMM renamed from HW_PIN_DATASPI + -D HW_PIN_CLOCKSPI=36 + -D HW_PIN_MISOSPI=37 + -D AUDIOPIN=-1 + -D SR_DMTYPE=1 -D I2S_SDPIN=9 -D I2S_WSPIN=8 -D I2S_CKPIN=17 -D MCLK_PIN=18 + -D WLED_USE_MY_CONFIG +lib_deps = ${env.lib_deps} ${esp32s2.lib_deps} ${common_mm.lib_deps_S} ${common_mm.lib_deps_V4_M} +lib_ignore = + IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation + OneWire ; not needed as we don't include USERMOD_DALLASTEMPERATURE +monitor_filters = esp32_exception_decoder +; RAM: [== ] 22.9% (used 75068 bytes from 327680 bytes) +; Flash: [======== ] 78.3% (used 1487802 bytes from 1900544 bytes) + +;; MM environment for generic ESP32-C3 -> 4MB flash, no PSRAM +[env:esp32c3dev_4MB_M] +extends = esp32_4MB_V4_S_base +;platform = ${esp32.platformV4} ;; standard IDF 4.4.1 platform +;platform_packages = ${esp32.platformV4_packages} +;board_build.flash_mode = dout +platform = ${esp32.platformV4_new} ;; alternative platform, might help in case you experience bootloops due to corrupted flash filesystem +platform_packages = ${esp32.platformV4_new_packages} +board_build.flash_mode = qio +board = esp32-c3-devkitm-1 +;board_build.partitions = tools/WLED_ESP32_4MB_256KB_FS.csv ;; 1.8MB firmware, 256KB filesystem (esptool erase_flash needed when changing from "standard WLED" partitions) +;board_build.partitions = tools/WLED_ESP32_2MB_noOTA.csv ;; use this for boards with 2MB flash (like some Ai-Thinker ESP32-C3-12F models) +upload_speed = 230400 ;; 460800 ;; 256000 ;; 921600 + +build_unflags = ${common.build_unflags} + -D USERMOD_DALLASTEMPERATURE ;; disabled because it hangs during usermod setup on -S2 (autodetect broken?) + -D USE_ALT_DISPLAY ;; four line display seems to have problems with I2C - it hangs during usermod setup + -D USERMOD_FOUR_LINE_DISPLAY ;; see above + -D USERMOD_ROTARY_ENCODER_UI ;; see above + -D WLED_ENABLE_DMX ;; disabled because it does not work with ESP-IDF 4.4.x (buggy driver in SparkFunDMX) + -D WLED_ENABLE_DMX_INPUT ;; needs more testing + ;-D WLED_DEBUG_HOST='"192.168.x.x"' ;; to disable net print + +build_flags = ${common.build_flags} ${esp32c3.build_flags} + ; -D WLED_DISABLE_OTA ;; OTA is not possible for boards with 2MB flash only (like some Ai-Thinker ESP32-C3-12F models) + -D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0 + ${common_mm.build_flags_S} ${common_mm.build_flags_M} + -Wno-misleading-indentation -Wno-format-truncation + -D WLED_RELEASE_NAME=esp32c3dev_4MB_M + ; -D WLED_DISABLE_BROWNOUT_DET + ; -DARDUINO_USB_CDC_ON_BOOT=1 ;; enable CDC USB -> needed for debugging over serial USB + -D WLED_DISABLE_ADALIGHT ;; to disable serial protocols for boards with CDC USB (Serial RX will receive junk commands, unless its pulled down by resistor) + -DARDUINO_USB_CDC_ON_BOOT=0 ;; disable CDC USB + -D SERVERNAME='"WLED-C3"' + ;-D WLEDMM_WIFI_POWERON_HACK ;; use this _only_ if your device is not able to make a WiFI connection! + ;-D WLED_DISABLE_INFRARED ;; save flash space + ;-D WLED_DISABLE_ALEXA ;; save flash space + -D LEDPIN=8 ;; onboard neopixel 5x5 Matrix. Attach your own LEDs to GPIO 20 + -D BTNPIN=9 + ; -D STATUSLED=10 ;; onboard LED + -D RLYPIN=-1 -D IRPIN=-1 + ;-D HW_PIN_SDA=0 -D HW_PIN_SCL=1 ;; for I2C Qwiic connector + -D SR_DMTYPE=1 -D I2S_SDPIN=5 -D I2S_WSPIN=6 -D I2S_CKPIN=4 -D MCLK_PIN=7 + ; -D WLED_USE_MY_CONFIG + +lib_deps = ${esp32c3.lib_deps} ${common_mm.lib_deps_S} ${common_mm.lib_deps_V4_M} +lib_ignore = + ;IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation + OneWire ; not needed as we don't include USERMOD_DALLASTEMPERATURE + U8g2 ; not needed as we don't include USERMOD_FOUR_LINE_DISPLAY +;monitor_filters = esp32_exception_decoder +; RAM: [== ] 23.1% (used 75636 bytes from 327680 bytes) +; Flash: [==========] 96.0% (used 1510452 bytes from 1572864 bytes) + +;; MM environment for "seeed xiao -C3" boards +[env:seeed_esp32c3_4MB_S] +extends = env:esp32c3dev_4MB_M +board = seeed_xiao_esp32c3 +platform = ${esp32.platformV4} ;; standard IDF 4.4.1 platform +platform_packages = ${esp32.platformV4_packages} +board_build.flash_mode = qio +upload_speed = 460800 +build_unflags = ${env:esp32c3dev_4MB_M.build_unflags} +build_flags = ${common.build_flags} ${esp32c3.build_flags} + -D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0 + ${common_mm.build_flags_S} -Wno-misleading-indentation -Wno-format-truncation + -D WLED_RELEASE_NAME=seeed_esp32c3_4MB_S + -DARDUINO_USB_CDC_ON_BOOT=1 ;; enable CDC USB -> needed for debugging over serial USB + -D WLED_DISABLE_ADALIGHT ;; to disable serial protocols when using CDC USB (Serial RX pin will receive junk commands, unless its pulled down by resistor) + -D WLED_DISABLE_INFRARED ;; save flash space + ;-D WLED_DISABLE_ALEXA ;; save flash space + ;-D WLED_DISABLE_HUESYNC ;; save flash space + ;-D WLED_DISABLE_LOXONE ;; save flash space + ;-D WLEDMM_WIFI_POWERON_HACK ;; use this _only_ if your device is not able to make a WiFI connection! + -D LEDPIN=3 ;; attach your LEDs to GPIO3 aka "D1" / "A1" + -D BTNPIN=9 + -D RLYPIN=-1 -D IRPIN=-1 + -D SR_DMTYPE=1 + -D I2S_SDPIN=5 -D I2S_WSPIN=6 -D I2S_CKPIN=4 -D MCLK_PIN=7 + -D WLED_USE_MY_CONFIG + ;-D WLED_DEBUG -D SR_DEBUG +lib_deps = ${esp32c3.lib_deps} ${common_mm.lib_deps_S} +;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation +monitor_filters = esp32_exception_decoder +; RAM: [== ] 22.9% (used 75108 bytes from 327680 bytes) +; Flash: [========= ] 90.5% (used 1423788 bytes from 1572864 bytes) + + +# ------------------------------------------------------------------------------ +# custom board environments +# ------------------------------------------------------------------------------ + +[wemos_shield_esp32_4MB_M_base] +extends = esp32_4MB_M_base +build_flags = ${esp32_4MB_M_base.build_flags} + -D ABL_MILLIAMPS_DEFAULT=9500 ; Wemos max 10A + -D LEDPIN=16 + -D RLYPIN=19 + -D BTNPIN=17 + -D IRPIN=18 + -D AUDIOPIN=-1 + -D TEMPERATURE_PIN=23 + -D FLD_PIN_SCL=-1 -D FLD_PIN_SDA=-1 ; use global! + -D HW_PIN_SCL=22 -D HW_PIN_SDA=21 + -D HW_PIN_CLOCKSPI=-1 -D HW_PIN_MOSISPI=-1 -D HW_PIN_MISOSPI=-1 ; WLEDMM: is now also default but just to show we didn't agree on wemos pins for spi yet + -D ENCODER_DT_PIN=35 -D ENCODER_CLK_PIN=39 -D ENCODER_SW_PIN=5 ; WLEDMM spec by @SERG74: use 35 and 39 instead of 18 and 19 (conflicts) + -D PIR_SENSOR_PIN=-1 + -D PWM_PIN=-1 + ; -D WLED_USE_MY_CONFIG + +[wemos_shield_esp32_4MB_XL_base] +extends = wemos_shield_esp32_4MB_M_base +build_flags = ${wemos_shield_esp32_4MB_M_base.build_flags} ${common_mm.build_flags_XL} +lib_deps = ${wemos_shield_esp32_4MB_M_base.lib_deps} ${common_mm.lib_deps_XL} + +[env:wemos_shield_esp32_4MB_M] +extends = wemos_shield_esp32_4MB_M_base +build_flags = ${wemos_shield_esp32_4MB_M_base.build_flags} + -D WLED_RELEASE_NAME=wemos_shield_esp32_4MB_M +; RAM: [== ] 24.4% (used 79820 bytes from 327680 bytes) +; Flash: [========= ] 88.6% (used 1393421 bytes from 1572864 bytes) + +[env:wemos_shield_esp32_4MB_ICS4343x_M] +extends = wemos_shield_esp32_4MB_M_base +build_flags = ${wemos_shield_esp32_4MB_M_base.build_flags} ${Shield_ICS4343x.build_flags} + -D WLED_RELEASE_NAME=wemos_shield_esp32_4MB_ICS4343x_M +; RAM: [== ] 24.4% (used 79820 bytes from 327680 bytes) +; Flash: [========= ] 88.6% (used 1393421 bytes from 1572864 bytes) + +[env:wemos_shield_esp32_4MB_SPM1423_M] +extends = wemos_shield_esp32_4MB_M_base +build_flags = ${wemos_shield_esp32_4MB_M_base.build_flags} ${Shield_SPM1423.build_flags} + -D WLED_RELEASE_NAME=wemos_shield_esp32_4MB_SPM1423_M +; RAM: [== ] 24.4% (used 79820 bytes from 327680 bytes) +; Flash: [========= ] 88.6% (used 1393421 bytes from 1572864 bytes) + +[env:wemos_shield_esp32_4MB_LineIn_M] +extends = wemos_shield_esp32_4MB_M_base +build_unflags = ${common.build_unflags} ${Shield_LineIn.build_unflags} +build_flags = ${wemos_shield_esp32_4MB_M_base.build_flags} ${Shield_LineIn.build_flags} + -D WLED_RELEASE_NAME=wemos_shield_esp32_4MB_LineIn_M + +[env:wemos_shield_esp32_16MB_M] +extends = wemos_shield_esp32_4MB_M_base +build_flags = ${wemos_shield_esp32_4MB_M_base.build_flags} + -D WLED_RELEASE_NAME=wemos_shield_esp32_16MB_M +board = esp32_16MB +board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem +;board_build.flash_mode = qio +; RAM: [== ] 24.4% (used 79820 bytes from 327680 bytes) +; Flash: [======= ] 66.4% (used 1393421 bytes from 2097152 bytes) + +[env:wemos_shield_esp32_16MB_ICS4343x_M] +extends = wemos_shield_esp32_4MB_M_base +build_flags = ${wemos_shield_esp32_4MB_M_base.build_flags} ${Shield_ICS4343x.build_flags} + -D WLED_RELEASE_NAME=wemos_shield_esp32_16MB_ICS4343x_M +board = esp32_16MB +board_build.partitions = tools/WLED_ESP32_16MB.csv +; RAM: [== ] 24.4% (used 79820 bytes from 327680 bytes) +; Flash: [========= ] 88.6% (used 1393421 bytes from 1572864 bytes) + +[env:wemos_shield_esp32_16MB_ICS4343x_XL] +extends = wemos_shield_esp32_4MB_XL_base +build_flags = ${wemos_shield_esp32_4MB_XL_base.build_flags} ${Shield_ICS4343x.build_flags} + -D WLED_RELEASE_NAME=wemos_shield_esp32_16MB_ICS4343x_XL +board = esp32_16MB +board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem +; RAM: [== ] 24.4% (used 80044 bytes from 327680 bytes) +; Flash: [======= ] 67.9% (used 1424185 bytes from 2097152 bytes) + +[env:wemos_shield_esp32_16MB_SPM1423_M] +extends = wemos_shield_esp32_4MB_M_base +build_flags = ${wemos_shield_esp32_4MB_M_base.build_flags} ${Shield_SPM1423.build_flags} + -D WLED_RELEASE_NAME=wemos_shield_esp32_16MB_SPM1423_M +board = esp32_16MB +board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem +; RAM: [== ] 24.4% (used 79820 bytes from 327680 bytes) +; Flash: [========= ] 88.6% (used 1393421 bytes from 1572864 bytes) + +[env:wemos_shield_esp32_16MB_SPM1423_XL] +extends = wemos_shield_esp32_4MB_XL_base +build_flags = ${wemos_shield_esp32_4MB_XL_base.build_flags} ${Shield_SPM1423.build_flags} + -D WLED_RELEASE_NAME=wemos_shield_esp32_16MB_SPM1423_XL +board = esp32_16MB +board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem +; RAM: [== ] 24.4% (used 79820 bytes from 327680 bytes) +; Flash: [========= ] 88.6% (used 1393421 bytes from 1572864 bytes) + +[env:wemos_shield_esp32_16MB_LineIn_M] +extends = wemos_shield_esp32_4MB_M_base +build_unflags = ${common.build_unflags} ${Shield_LineIn.build_unflags} +build_flags = ${wemos_shield_esp32_4MB_M_base.build_flags} ${Shield_LineIn.build_flags} + -D WLED_RELEASE_NAME=wemos_shield_esp32_16MB_LineIn_M +board = esp32_16MB +board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem + +[env:athom_music_esp32_4MB_M] +extends = esp32_4MB_M_base +build_flags = ${esp32_4MB_M_base.build_flags} ${Athom_PDMmic.build_flags} + -D WLED_AP_SSID_UNIQUE + -D WLED_RELEASE_NAME=athom_music_esp32_4MB_M + -D ABL_MILLIAMPS_DEFAULT=14500 ; max 15A + -D WLED_DISABLE_MQTT -D WLED_DISABLE_LOXONE + -D WLED_DISABLE_ADALIGHT ;to get 4ld working + -D BTNPIN=0 -D RLYPIN=2 -D IRPIN=25 -D IRTYPE=9 -D LEDPIN=18 -D + -D AUDIOPIN=-1 + ; -D TEMPERATURE_PIN=23 + -D FLD_PIN_SCL=-1 -D FLD_PIN_SDA=-1 ; use global! + -D HW_PIN_SCL=3 -D HW_PIN_SDA=1 ;4ld uses rx and tx + -D HW_PIN_CLOCKSPI=-1 -D HW_PIN_MOSISPI=-1 -D HW_PIN_MISOSPI=-1 ; WLEDMM: is now also default but just to show we didn't agree on wemos pins for spi yet + ; -D ENCODER_DT_PIN=35 -D ENCODER_CLK_PIN=39 -D ENCODER_SW_PIN=5 ; WLEDMM spec by @SERG74: use 35 and 39 instead of 18 and 19 (conflicts) + ; -D PIR_SENSOR_PIN=-1 + ; -D PWM_PIN=-1 + ; -D WLED_USE_MY_CONFIG + +; ESP32 WLED pico board with builtin ICS-43432 microphpone +[env:esp32_pico_4MB_M] +extends = esp32_4MB_M_base +board = pico32 +board_build.flash_mode = dout ;; (dout = dual out; more compatible than qio = quad i/o) +upload_speed = 256000 ;; or 115200 ;; or 460800 ; or 921600 (slower speeds are better when flashing without a soldered connection) + +build_flags = ${esp32_4MB_M_base.build_flags} + -D WLED_RELEASE_NAME=esp32_pico_4MB_M + -D WLED_DISABLE_BROWNOUT_DET + -D SERVERNAME='"WLED-pico32"' + ; -D WLED_WATCHDOG_TIMEOUT=60 + -D WLED_DISABLE_ADALIGHT ;; WLEDMM this board does not have a serial-to-USB chip. Better to disable serial protocols, to avoid crashes (see upstream #3128) + ; -D WLED_DEBUG + ; -D SR_DEBUG + -D LEDPIN=2 + -D RLYPIN=-1 -D BTNPIN=-1 -D IRPIN=-1 + -D HW_PIN_SCL=22 -D HW_PIN_SDA=21 + -D SR_DMTYPE=1 -D I2S_SDPIN=25 -D I2S_WSPIN=15 -D I2S_CKPIN=14 + -D SR_SQUELCH=5 -D SR_GAIN=30 -D SR_FREQ_PROF=5 ; ICS-4343x specific + ; -D MCLK_PIN=0 + -D SR_ENABLE_DEFAULT ;; enable at first start - no need to manually set "enable", then reboot + ; -D WLED_USE_MY_CONFIG + ; -D WLED_DISABLE_LOXONE + ; -D WLED_DISABLE_ALEXA + ; -D WLED_DISABLE_HUESYNC + ; -D WLED_DISABLE_MQTT + ; -D WLED_DISABLE_INFRARED + ; -D WLED_ENABLE_DMX +; RAM: [== ] 24.4% (used 79812 bytes from 327680 bytes) +; Flash: [========= ] 90.4% (used 1422581 bytes from 1572864 bytes) + + +;; experimental +;; PICO environment with ESP-IDF v4.4.1 / arduino-esp32 v2.0.4 +[env:esp32_pico_4MB_V4_S] +extends = esp32_4MB_V4_S_base +board = pico32 +;platform = espressif32@~5.2.0 ;; alternative platform, might help in case you experience bootloops due to corrupted flash filesystem +;platform_packages = +upload_speed = 256000 ;; or 115200 ;; or 460800 ; or 921600 (slower speeds are better when flashing without a soldered connection) + +build_flags = ${esp32_4MB_V4_S_base.build_flags} + -D ARDUINO_USB_CDC_ON_BOOT=0 ; needed for arduino-esp32 >=2.0.4; avoids errors on startup + -D WLED_RELEASE_NAME=esp32_pico_4MB_V4_S + -D WLED_DISABLE_BROWNOUT_DET + -D SERVERNAME='"WLED-pico32-V4"' + -D WLED_WATCHDOG_TIMEOUT=0 + -D WLED_DISABLE_ADALIGHT ;; WLEDMM this board does not have a serial-to-USB chip. Better to disable serial protocols, to avoid crashes (see upstream #3128) + ; -D WLED_WATCHDOG_TIMEOUT=60 + ; -D WLED_DEBUG + ; -D SR_DEBUG + -D LEDPIN=2 + -D RLYPIN=-1 -D BTNPIN=-1 -D IRPIN=-1 + -D HW_PIN_SCL=22 -D HW_PIN_SDA=21 + -D SR_DMTYPE=1 -D I2S_SDPIN=25 -D I2S_WSPIN=15 -D I2S_CKPIN=14 + -D SR_SQUELCH=5 -D SR_GAIN=30 -D SR_FREQ_PROF=5 ; ICS-4343x specific + ; -D MCLK_PIN=0 + -D SR_ENABLE_DEFAULT ;; enable audioreactive at first start - no need to manually set "enable", then reboot + ; -D WLED_USE_MY_CONFIG +; RAM: [== ] 24.5% (used 80436 bytes from 327680 bytes) +; Flash: [========= ] 93.9% (used 1476341 bytes from 1572864 bytes) diff --git a/platformio_override.ini.sample b/platformio_override.ini.sample index cd81c517..d6ea5d96 100644 --- a/platformio_override.ini.sample +++ b/platformio_override.ini.sample @@ -26,7 +26,6 @@ build_flags = ${common.build_flags_esp8266} ; disable specific features ; -D WLED_DISABLE_OTA ; -D WLED_DISABLE_ALEXA -; -D WLED_DISABLE_BLYNK ; -D WLED_DISABLE_HUESYNC ; -D WLED_DISABLE_INFRARED ; -D WLED_DISABLE_WEBSOCKETS diff --git a/readme.md b/readme.md index 15aaf885..132fdc41 100644 --- a/readme.md +++ b/readme.md @@ -4,82 +4,25 @@ - +

-# Welcome to my project WLED! ✨ +# Welcome to WLED MoonModules! ✨ -A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B, WS2811, SK6812) LEDs or also SPI based chipsets like the WS2801 and APA102! +image -## ⚙️ Features -- WS2812FX library with more than 100 special effects -- FastLED noise effects and 50 palettes -- Modern UI with color, effect and segment controls -- Segments to set different effects and colors to user defined parts of the LED string -- Settings page - configuration via the network -- Access Point and station mode - automatic failsafe AP -- Up to 10 LED outputs per instance -- Support for RGBW strips -- Up to 250 user presets to save and load colors/effects easily, supports cycling through them. -- Presets can be used to automatically execute API calls -- Nightlight function (gradually dims down) -- Full OTA software updatability (HTTP + ArduinoOTA), password protectable -- Configurable analog clock (Cronixie, 7-segment and EleksTube IPS clock support via usermods) -- Configurable Auto Brightness limit for safe operation -- Filesystem-based config for easier backup of presets and settings +MoonModules/WLED is a fork from Aircoookie/WLED which contains latest merge of v0.14 of WLED with additional features. -## 💡 Supported light control interfaces -- WLED app for [Android](https://play.google.com/store/apps/details?id=com.aircoookie.WLED) and [iOS](https://apps.apple.com/us/app/wled/id1475695033) -- JSON and HTTP request APIs -- MQTT -- Blynk IoT -- E1.31, Art-Net, DDP and TPM2.net -- [diyHue](https://github.com/diyhue/diyHue) (Wled is supported by diyHue, including Hue Sync Entertainment under udp. Thanks to [Gregory Mallios](https://github.com/gmallios)) -- [Hyperion](https://github.com/hyperion-project/hyperion.ng) -- UDP realtime -- Alexa voice control (including dimming and color) -- Sync to Philips hue lights -- Adalight (PC ambilight via serial) and TPM2 -- Sync color of multiple WLED devices (UDP notifier) -- Infrared remotes (24-key RGB, receiver required) -- Simple timers/schedules (time from NTP, timezones/DST supported) +This fork is created by members of the Atuline/WLED team to make development against v0.14 possible while still preserving Atuline/WLED v0.13 as a stable and supported version. The Atuline/WLED fork is also called WLED SR (Sound Reactive). -## 📲 Quick start guide and documentation - -See the [documentation on our official site](https://mm.kno.wled.ge)! - -[On this page](https://mm.kno.wled.ge/basics/tutorials/) you can find excellent tutorials and tools to help you get your new project up and running! - -## 🖼️ User interface - - -## 💾 Compatible hardware - -See [here](https://mm.kno.wled.ge/basics/compatible-hardware)! - -## ✌️ Other - -Licensed under the MIT license -Credits [here](https://mm.kno.wled.ge/about/contributors/)! - -Join the Discord server to discuss everything about WLED! - - - -Check out the WLED [Discourse forum](https://wled.discourse.group)! - -You can also send me mails to [dev.aircoookie@gmail.com](mailto:dev.aircoookie@gmail.com), but please, only do so if you want to talk to me privately. - -If WLED really brightens up your day, you can [![](https://img.shields.io/badge/send%20me%20a%20small%20gift-paypal-blue.svg?style=flat-square)](https://paypal.me/aircoookie) +More info here: what-is-moonmodules +HTML tutorial +Donations will be used to buy WLED related hardware, software or drinks shared with the contributors of this repo. *Disclaimer:* -If you are prone to photosensitive epilepsy, we recommended you do **not** use this software. -If you still want to try, don't use strobe, lighting or noise modes or high effect speed settings. - -As per the MIT license, I assume no liability for any damage to you or any other person or equipment. - +Using this software is the users responsibility as it is not bug free. Therefore contributors of this repo are not reliable for anything including but not limited to spontaneous combustion of the entire led strip, the house and the inevitable heat death of the universe diff --git a/tools/SoundReactive_ESP32_16MB.csv b/tools/SoundReactive_ESP32_16MB.csv deleted file mode 100644 index e547a824..00000000 --- a/tools/SoundReactive_ESP32_16MB.csv +++ /dev/null @@ -1,6 +0,0 @@ -# Name, Type, SubType, Offsaet, Size, Flags -nvs, data, nvs, 0x9000, 0x5000, -otadata, data, ota, 0xe000, 0x2000, -app0, app, ota_0, 0x10000, 0x200000, -app1, app, ota_1, 0x210000, 0x200000, -spiffs, data, spiffs, 0x410000, 0x7f0000, diff --git a/tools/WLED_ESP32_16MB_9MB_FS.csv b/tools/WLED_ESP32_16MB_9MB_FS.csv new file mode 100644 index 00000000..9ecac04e --- /dev/null +++ b/tools/WLED_ESP32_16MB_9MB_FS.csv @@ -0,0 +1,8 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x300000, +app1, app, ota_1, 0x310000,0x300000, +spiffs, data, spiffs, 0x610000,0x9E0000, +coredump, data, coredump,0xFF0000,0x10000, +# to create/use ffat, see https://github.com/marcmerlin/esp32_fatfsimage \ No newline at end of file diff --git a/tools/WLED_ESP32_2MB_noOTA.csv b/tools/WLED_ESP32_2MB_noOTA.csv new file mode 100644 index 00000000..7a1cf15f --- /dev/null +++ b/tools/WLED_ESP32_2MB_noOTA.csv @@ -0,0 +1,5 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 20K, +otadata, data, ota, 0xe000, 8K, +app0, app, ota_0, 0x10000, 1536K, +spiffs, data, spiffs, 0x190000, 384K, diff --git a/tools/WLED_ESP32_4MB_256KB_FS.csv b/tools/WLED_ESP32_4MB_256KB_FS.csv new file mode 100644 index 00000000..f9e1be26 --- /dev/null +++ b/tools/WLED_ESP32_4MB_256KB_FS.csv @@ -0,0 +1,7 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x1D0000, +app1, app, ota_1, 0x1E0000,0x1D0000, +spiffs, data, spiffs, 0x3B0000,0x40000, +coredump, data, coredump,0x3F0000,0x10000, \ No newline at end of file diff --git a/tools/cdata.js b/tools/cdata.js index d307bbcd..55b04e13 100644 --- a/tools/cdata.js +++ b/tools/cdata.js @@ -220,6 +220,7 @@ function writeChunks(srcDir, specs, resultFile) { writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h", 'index'); writeHtmlGzipped("wled00/data/simple.htm", "wled00/html_simple.h", 'simple'); +writeHtmlGzipped("wled00/data/pixart/pixart.htm", "wled00/html_pixart.h", 'pixart'); /* writeChunks( "wled00/data", @@ -399,6 +400,13 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()====="; method: "gzip", filter: "html-minify", }, + //WLEDMM + { + file: "peek.js", + name: "PAGE_peekJs", + method: "gzip", + filter: "js-minify", + }, { file: "404.htm", name: "PAGE_404", diff --git a/tools/partitions-4MB-tinyuf2_spiffs.csv b/tools/partitions-4MB_spiffs-tinyuf2.csv similarity index 100% rename from tools/partitions-4MB-tinyuf2_spiffs.csv rename to tools/partitions-4MB_spiffs-tinyuf2.csv diff --git a/usermods/BH1750_v2/usermod_bh1750.h b/usermods/BH1750_v2/usermod_bh1750.h index 1304747f..77a57efb 100644 --- a/usermods/BH1750_v2/usermod_bh1750.h +++ b/usermods/BH1750_v2/usermod_bh1750.h @@ -2,13 +2,12 @@ // #warning **** Included USERMOD_BH1750 **** #ifndef WLED_ENABLE_MQTT -#error "This user mod requires MQTT to be enabled." +#warning "This user mod expects MQTT to be enabled." #endif #pragma once #include // WLEDMM: make sure that I2C drivers have the "right" Wire Object -#include #include "wled.h" #include @@ -20,7 +19,8 @@ // the min frequency to check photoresistor, 500 ms #ifndef USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL -#define USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL 500 +//#define USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL 500 +#define USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL 2500 // WLEDMM this makes more sense #endif // how many seconds after boot to take first measurement, 10 seconds @@ -30,7 +30,7 @@ // only report if differance grater than offset value #ifndef USERMOD_BH1750_OFFSET_VALUE -#define USERMOD_BH1750_OFFSET_VALUE 1 +#define USERMOD_BH1750_OFFSET_VALUE 2 // WLEDMM this makes more sense #endif class Usermod_BH1750 : public Usermod @@ -91,14 +91,17 @@ private: // set up Home Assistant discovery entries void _mqttInitialize() { +#ifdef WLED_ENABLED_MQTT mqttLuminanceTopic = String(mqttDeviceTopic) + F("/brightness"); if (HomeAssistantDiscovery) _createMqttSensor(F("Brightness"), mqttLuminanceTopic, F("Illuminance"), F(" lx")); +#endif } // Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop. void _createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement) { +#ifdef WLED_ENABLED_MQTT String t = String(F("homeassistant/sensor/")) + mqttClientID + F("/") + name + F("/config"); StaticJsonDocument<600> doc; @@ -125,11 +128,13 @@ private: DEBUG_PRINTLN(temp); mqtt->publish(t.c_str(), 0, true, temp.c_str()); +#endif } public: void setup() { +#if 0 bool HW_Pins_Used = (ioPin[0]==i2c_scl && ioPin[1]==i2c_sda); // note whether architecture-based hardware SCL/SDA pins used PinOwner po = PinOwner::UM_BH1750; // defaults to being pinowner for SCL/SDA pins if (HW_Pins_Used) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins @@ -147,13 +152,18 @@ public: #else //Wire.begin(); // WLEDMM - i2c pins on 8266 are fixed. #endif +#endif + if (!enabled) return; if (!pinManager.joinWire()) { // WLEDMM - this allocates global I2C pins, then starts Wire - if not started previously sensorFound = false; - enabled = false; + //enabled = false; + USER_PRINTLN(F("BH1750: failed to join I2C bus.")); return; } sensorFound = lightMeter.begin(); + if (sensorFound) { USER_PRINTLN(F("BH1750 sensor found.")); } + else{ USER_PRINTLN(F("BH1750 sensor not found.")); } initDone = true; } @@ -226,7 +236,7 @@ public: lux_json.add(F(" sec until read")); return; } else { - lux_json.add(lastLux); + lux_json.add(round(lastLux)); // WLEDMM lux_json.add(F(" lx")); } } @@ -235,9 +245,9 @@ public: oappend(SET_F("addHB('BH1750');")); // WLEDMM this usermod can ONLY use HW_I2C - so always use globals //oappend(SET_F("addInfo('BH1750:pin[]',0,'','I2C SCL');")); - //oappend(SET_F("rOption('BH1750:pin[]',0,'use global (")); oappendi(i2c_scl); oappend(")',-1);"); + //oappend(SET_F("rOpt('BH1750:pin[]',0,'use global (")); oappendi(i2c_scl); oappend(")',-1);"); //oappend(SET_F("addInfo('BH1750:pin[]',1,'','I2C SDA');")); - //oappend(SET_F("rOption('BH1750:pin[]',1,'use global (")); oappendi(i2c_sda); oappend(")',-1);"); + //oappend(SET_F("rOpt('BH1750:pin[]',1,'use global (")); oappendi(i2c_sda); oappend(")',-1);"); } // (called from set.cpp) stores persistent properties to cfg.json @@ -295,6 +305,7 @@ public: } else { DEBUG_PRINTLN(F(" config (re)loaded.")); // changing parameters from settings page +#if 0 bool pinsChanged = false; for (byte i=0; i<2; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; } // check if any pins changed if (pinsChanged) { //if pins changed, deallocate old pins and allocate new ones @@ -305,6 +316,9 @@ public: for (byte i=0; i<2; i++) ioPin[i] = newPin[i]; setup(); } +#else + if (enabled && !sensorFound) setup(); +#endif // use "return !top["newestParameter"].isNull();" when updating Usermod with new features return !top[F("pin")].isNull(); } diff --git a/usermods/BME280_v2/usermod_bme280.h b/usermods/BME280_v2/usermod_bme280.h index f2b6022d..3b9c80ec 100644 --- a/usermods/BME280_v2/usermod_bme280.h +++ b/usermods/BME280_v2/usermod_bme280.h @@ -405,14 +405,14 @@ public: } void appendConfigData() { - oappend(SET_F("addHB('BME280/BMP280');")); + oappend(SET_F("addHB('BME280');")); oappend(SET_F("addInfo('BME280/BMP280:pin[]',0,'','I2C/SPI CLK');")); - oappend(SET_F("disableRO('BME280/BMP280:pin[]',0);")); - oappend(SET_F("rOption('BME280/BMP280:pin[]',0,'use global (")); oappendi(i2c_scl); oappend(")',-1);"); + oappend(SET_F("dRO('BME280/BMP280:pin[]',0);")); // disable read only pins + oappend(SET_F("rOpt('BME280/BMP280:pin[]',0,'use global (")); oappendi(i2c_scl); oappend(")',-1);"); oappend(SET_F("addInfo('BME280/BMP280:pin[]',1,'','I2C/SPI DTA');")); - oappend(SET_F("rOption('BME280/BMP280:pin[]',1,'use global (")); oappendi(i2c_sda); oappend(")',-1);"); + oappend(SET_F("rOpt('BME280/BMP280:pin[]',1,'use global (")); oappendi(i2c_sda); oappend(")',-1);"); } diff --git a/usermods/EXAMPLE_v2/usermod_v2_example.h b/usermods/EXAMPLE_v2/usermod_v2_example.h index a4fe9389..ba2ffc4e 100644 --- a/usermods/EXAMPLE_v2/usermod_v2_example.h +++ b/usermods/EXAMPLE_v2/usermod_v2_example.h @@ -20,11 +20,32 @@ * 2. Register the usermod by adding #include "usermod_filename.h" in the top and registerUsermod(new MyUsermodClass()) in the bottom of usermods_list.cpp */ +/* WLEDMM: move usermod variables to class. + +As of March 2023 this is work in progress, more variables will be moved in the future. +See Example v2, Temperature, MPU6050 and weather and fastled (rest to be done) as examples which has been converted using the steps below: + +Part 1 +- remove bool enabled = false/true (now default false) +- remove static const char _name[] and _enabled[] +- add constructor which calls superclass (temp?): XXXUsermod(const char *name, bool enabled):Usermod(name, enabled) {} +- replace _enabled with "enabled" +- remove const char PROGMEM init for _name[] and _enabled[] +Part 2 +- Remove bool initDone = false; +- addToConfig: replace createNestedObject with Usermod::addToConfig(root); JsonObject top = root[FPSTR(_name)]; +- readFromConfig: replace !top.isNull and enabled with bool configComplete = Usermod::readFromConfig(root);JsonObject top = root[FPSTR(_name)]; +Part 3 +- remove unsigned long lastTime = 0; //WLEDMM + +*/ + //class name. Use something descriptive and leave the ": public Usermod" part :) class MyExampleUsermod : public Usermod { + private: - //Private class members. You can declare variables and functions only accessible to your usermod here - unsigned long lastTime = 0; + + // Private class members. You can declare variables and functions only accessible to your usermod here // set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer) bool testBool = false; @@ -37,15 +58,53 @@ class MyExampleUsermod : public Usermod { long testLong; int8_t testPins[2]; + // any private methods should go here (non-inline methosd should be defined out of class) + void publishMqtt(const char* state, bool retain = false); // example for publishing MQTT message + + public: - //Functions called by WLED + + MyExampleUsermod(const char *name, bool enabled):Usermod(name, enabled) {} //WLEDMM + + // non WLED related methods, may be used for data exchange between usermods (non-inline methods should be defined out of class) + + /** + * Enable/Disable the usermod + */ + // inline void enable(bool enable) { enabled = enable; } + + /** + * Get usermod enabled/disabled state + */ + // inline bool isEnabled() { return enabled; } + + // in such case add the following to another usermod: + // in private vars: + // #ifdef USERMOD_EXAMPLE + // MyExampleUsermod* UM; + // #endif + // in setup() + // #ifdef USERMOD_EXAMPLE + // UM = (MyExampleUsermod*) usermods.lookup(USERMOD_ID_EXAMPLE); + // #endif + // somewhere in loop() or other member method + // #ifdef USERMOD_EXAMPLE + // if (UM != nullptr) isExampleEnabled = UM->isEnabled(); + // if (!isExampleEnabled) UM->enable(true); + // #endif + + + // methods called by WLED (can be inlined as they are called only once but if you call them explicitly define them out of class) /* * setup() is called once at boot. WiFi is not yet connected at this point. + * readFromConfig() is called prior to setup() * You can use it to initialize variables, sensors or similar. */ void setup() { + // do your set-up here //Serial.println("Hello from my usermod!"); + initDone = true; } @@ -69,6 +128,11 @@ class MyExampleUsermod : public Usermod { * Instead, use a timer check as shown here. */ 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; + + // do your magic here if (millis() - lastTime > 1000) { //Serial.println("I'm alive!"); lastTime = millis(); @@ -81,19 +145,25 @@ class MyExampleUsermod : public Usermod { * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI. * Below it is shown how this could be used for e.g. a light sensor */ - /* void addToJsonInfo(JsonObject& root) { - int reading = 20; - //this code adds "u":{"Light":[20," lux"]} to the info object + // if "u" object does not exist yet wee need to create it JsonObject user = root["u"]; if (user.isNull()) user = root.createNestedObject("u"); - JsonArray lightArr = user.createNestedArray("Light"); //name - lightArr.add(reading); //value - lightArr.add(" lux"); //unit + //this code adds "u":{"ExampleUsermod":[20," lux"]} to the info object + //int reading = 20; + //JsonArray lightArr = user.createNestedArray(FPSTR(_name))); //name + //lightArr.add(reading); //value + //lightArr.add(F(" lux")); //unit + + // if you are implementing a sensor usermod, you may publish sensor data + //JsonObject sensor = root[F("sensor")]; + //if (sensor.isNull()) sensor = root.createNestedObject(F("sensor")); + //temp = sensor.createNestedArray(F("light")); + //temp.add(reading); + //temp.add(F("lux")); } - */ /* @@ -102,7 +172,12 @@ class MyExampleUsermod : public Usermod { */ void addToJsonState(JsonObject& root) { - //root["user0"] = userVar0; + if (!initDone || !enabled) return; // prevent crash on boot applyPreset() + + JsonObject usermod = root[FPSTR(_name)]; + if (usermod.isNull()) usermod = root.createNestedObject(FPSTR(_name)); + + //usermod["user0"] = userVar0; } @@ -112,7 +187,14 @@ class MyExampleUsermod : public Usermod { */ void readFromJsonState(JsonObject& root) { - userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value + if (!initDone) return; // prevent crash on boot applyPreset() + + JsonObject usermod = root[FPSTR(_name)]; + if (!usermod.isNull()) { + // expect JSON usermod data in usermod name object: {"ExampleUsermod:{"user0":10}"} + userVar0 = usermod["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value + } + // you can as well check WLED state JSON keys //if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!")); } @@ -154,8 +236,10 @@ class MyExampleUsermod : public Usermod { */ void addToConfig(JsonObject& root) { - JsonObject top = root.createNestedObject("exampleUsermod"); - top["great"] = userVar0; //save these vars persistently whenever settings are saved + Usermod::addToConfig(root); JsonObject top = root[FPSTR(_name)]; //WLEDMM + + //save these vars persistently whenever settings are saved + top["great"] = userVar0; top["testBool"] = testBool; top["testInt"] = testInt; top["testLong"] = testLong; @@ -188,9 +272,7 @@ class MyExampleUsermod : public Usermod { // default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor // setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed) - JsonObject top = root["exampleUsermod"]; - - bool configComplete = !top.isNull(); + bool configComplete = Usermod::readFromConfig(root);JsonObject top = root[FPSTR(_name)]; //WLEDMM configComplete &= getJsonValue(top["great"], userVar0); configComplete &= getJsonValue(top["testBool"], testBool); @@ -201,6 +283,8 @@ class MyExampleUsermod : public Usermod { // A 3-argument getJsonValue() assigns the 3rd argument as a default value if the Json value is missing configComplete &= getJsonValue(top["testInt"], testInt, 42); configComplete &= getJsonValue(top["testLong"], testLong, -42424242); + + // "pin" fields have special handling in settings page (or some_pin as well) configComplete &= getJsonValue(top["pin"][0], testPins[0], -1); configComplete &= getJsonValue(top["pin"][1], testPins[1], -1); @@ -208,6 +292,21 @@ class MyExampleUsermod : public Usermod { } + /* + * appendConfigData() is called when user enters usermod settings page + * it may add additional metadata for certain entry fields (adding drop down is possible) + * be careful not to add too much as oappend() buffer is limited to 3k + */ + void appendConfigData() + { + oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":great")); oappend(SET_F("',1,'(this is a great config value)');")); + oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":testString")); oappend(SET_F("',1,'enter any string you want');")); + oappend(SET_F("dd=addDropdown('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F("','testInt');")); + oappend(SET_F("addOption(dd,'Nothing',0);")); + oappend(SET_F("addOption(dd,'Everything',42);")); + } + + /* * handleOverlayDraw() is called just before every show() (LED strip update frame) after effects have set the colors. * Use this to blank out some LEDs or set them to a different color regardless of the set effect mode. @@ -218,7 +317,72 @@ class MyExampleUsermod : public Usermod { //strip.setPixelColor(0, RGBW32(0,0,0,0)) // set the first pixel to black } - + + /** + * handleButton() can be used to override default button behaviour. Returning true + * will prevent button working in a default way. + * Replicating button.cpp + */ + bool handleButton(uint8_t b) { + yield(); + // ignore certain button types as they may have other consequences + if (!enabled + || buttonType[b] == BTN_TYPE_NONE + || buttonType[b] == BTN_TYPE_RESERVED + || buttonType[b] == BTN_TYPE_PIR_SENSOR + || buttonType[b] == BTN_TYPE_ANALOG + || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) { + return false; + } + + bool handled = false; + // do your button handling here + return handled; + } + + +#ifndef WLED_DISABLE_MQTT + /** + * handling of MQTT message + * topic only contains stripped topic (part after /wled/MAC) + */ + bool onMqttMessage(char* topic, char* payload) { + // check if we received a command + //if (strlen(topic) == 8 && strncmp_P(topic, PSTR("/command"), 8) == 0) { + // String action = payload; + // if (action == "on") { + // enabled = true; + // return true; + // } else if (action == "off") { + // enabled = false; + // return true; + // } else if (action == "toggle") { + // enabled = !enabled; + // return true; + // } + //} + return false; + } + + /** + * onMqttConnect() is called when MQTT connection is established + */ + void onMqttConnect(bool sessionPresent) { + // do any MQTT related initialisation here + //publishMqtt("I am alive!"); + } +#endif + + + /** + * onStateChanged() is used to detect WLED state change + * @mode parameter is CALL_MODE_... parameter used for notifications + */ + void onStateChange(uint8_t mode) { + // do something if WLED state changed (color, brightness, effect, preset, etc) + } + + /* * getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!). * This could be used in the future for the system to determine whether your usermod is installed. @@ -230,4 +394,23 @@ class MyExampleUsermod : public Usermod { //More methods can be added in the future, this example will then be extended. //Your usermod will remain compatible as it does not need to implement all methods from the Usermod base class! -}; \ No newline at end of file +}; + + +// add more strings here to reduce flash memory usage + + +// implementation of non-inline member methods + +void MyExampleUsermod::publishMqtt(const char* state, bool retain) +{ +#ifndef WLED_DISABLE_MQTT + //Check if MQTT Connected, otherwise it will crash the 8266 + if (WLED_MQTT_CONNECTED) { + char subuf[64]; + strcpy(subuf, mqttDeviceTopic); + strcat_P(subuf, PSTR("/example")); + mqtt->publish(subuf, 0, retain, state); + } +#endif +} diff --git a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h index 9aede3a8..288edb32 100644 --- a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h +++ b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h @@ -110,6 +110,7 @@ private: if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return; //if lights on and off only, do nothing if (PIRtriggered && switchOn) return; //if already on and triggered before, do nothing PIRtriggered = switchOn; + DEBUG_PRINT(F("PIR: strip=")); DEBUG_PRINTLN(switchOn?"on":"off"); if (switchOn) { if (m_onPreset) { if (currentPlaylist>0 && !offMode) { @@ -366,6 +367,20 @@ public: sensor[F("motion")] = sensorPinState || offTimerStart>0 ? true : false; } + /** + * onStateChanged() is used to detect WLED state change + */ + void onStateChange(uint8_t mode) { + if (!initDone) return; + DEBUG_PRINT(F("PIR: offTimerStart=")); DEBUG_PRINTLN(offTimerStart); + if (PIRtriggered && offTimerStart) { + // checking PIRtriggered and offTimerStart will prevent cancellation upon On trigger + DEBUG_PRINTLN(F("PIR: Canceled.")); + offTimerStart = 0; + PIRtriggered = false; + } + } + /** * addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object). * Values in the state object may be modified by connected clients diff --git a/usermods/PWM_fan/usermod_PWM_fan.h b/usermods/PWM_fan/usermod_PWM_fan.h index 9be45075..d8aa7a1a 100644 --- a/usermods/PWM_fan/usermod_PWM_fan.h +++ b/usermods/PWM_fan/usermod_PWM_fan.h @@ -1,7 +1,7 @@ #pragma once -#ifndef USERMOD_DALLASTEMPERATURE -#error The "PWM fan" usermod requires "Dallas Temeprature" usermod to function properly. +#if !defined(USERMOD_DALLASTEMPERATURE) && !defined(USERMOD_SHT) +#error The "PWM fan" usermod requires "Dallas Temeprature" or "SHT" usermod to function properly. #endif #include "wled.h" @@ -42,6 +42,8 @@ class PWMFanUsermod : public Usermod { #ifdef USERMOD_DALLASTEMPERATURE UsermodTemperature* tempUM; + #elif defined(USERMOD_SHT) + ShtUsermod* tempUM; #endif // configurable parameters @@ -145,7 +147,7 @@ class PWMFanUsermod : public Usermod { } float getActualTemperature(void) { - #ifdef USERMOD_DALLASTEMPERATURE + #if defined(USERMOD_DALLASTEMPERATURE) || defined(USERMOD_SHT) if (tempUM != nullptr) return tempUM->getTemperatureC(); #endif @@ -189,6 +191,8 @@ class PWMFanUsermod : public Usermod { #ifdef USERMOD_DALLASTEMPERATURE // This Usermod requires Temperature usermod tempUM = (UsermodTemperature*) usermods.lookup(USERMOD_ID_TEMPERATURE); + #elif defined(USERMOD_SHT) + tempUM = (ShtUsermod*) usermods.lookup(USERMOD_ID_SHT); #endif initTacho(); initPWMfan(); diff --git a/usermods/RTC/usermod_rtc.h b/usermods/RTC/usermod_rtc.h index f25e16e3..08b92ef1 100644 --- a/usermods/RTC/usermod_rtc.h +++ b/usermods/RTC/usermod_rtc.h @@ -6,6 +6,8 @@ #include "src/dependencies/time/DS1307RTC.h" #include "wled.h" +#define RTC_DELTA 2 // only modify RTC time if delta exceeds this number of seconds + //Connect DS1307 to standard I2C pins (ESP32: GPIO 21 (SDA)/GPIO 22 (SCL)) class RTCUsermod : public Usermod { @@ -36,6 +38,7 @@ class RTCUsermod : public Usermod { if (rtcTime) { toki.setTime(rtcTime,TOKI_NO_MS_ACCURACY,TOKI_TS_RTC); updateLocalTime(); + USER_PRINTLN(F("Localtime updated from RTC.")); } else { if (!RTC.chipPresent()) disabled = true; //don't waste time if H/W error } @@ -45,7 +48,24 @@ class RTCUsermod : public Usermod { if (strip.isUpdating()) return; if (!disabled && toki.isTick()) { time_t t = toki.second(); - if (abs(t - RTC.get())> 2) RTC.set(t); //set RTC to NTP/UI-provided value - WLEDMM allow up to 3 sec deviation + + if (abs(t - RTC.get())> RTC_DELTA) { // WLEDMM only consider time diffs > 2 seconds + if ( (toki.getTimeSource() == TOKI_TS_NTP) + ||( (toki.getTimeSource() != TOKI_TS_NONE) && (toki.getTimeSource() != TOKI_TS_RTC) + && (toki.getTimeSource() != TOKI_TS_BAD) && (toki.getTimeSource() != TOKI_TS_UDP_SEC) && (toki.getTimeSource() != TOKI_TS_UDP))) + { // WLEMM update RTC if we have a reliable time source + RTC.set(t); //set RTC to NTP/UI-provided value - WLEDMM allow up to 3 sec deviation + USER_PRINTLN(F("RTC updated using localtime.")); + } else { + // WLEDMM if no reliable time -> update from RTC + time_t rtcTime = RTC.get(); + if (rtcTime) { + toki.setTime(rtcTime,TOKI_NO_MS_ACCURACY,TOKI_TS_RTC); + updateLocalTime(); + USER_PRINTLN(F("Localtime updated from RTC.")); + } + } + } } } diff --git a/usermods/Temperature/usermod_temperature.h b/usermods/Temperature/usermod_temperature.h index 88719786..1c49163a 100644 --- a/usermods/Temperature/usermod_temperature.h +++ b/usermods/Temperature/usermod_temperature.h @@ -21,7 +21,6 @@ class UsermodTemperature : public Usermod { private: - bool initDone = false; OneWire *oneWire; // GPIO pin used for sensor (with a default compile-time fallback) int8_t temperaturePin = TEMPERATURE_PIN; @@ -45,13 +44,9 @@ class UsermodTemperature : public Usermod { // temperature if flashed to a board without a sensor attached byte sensorFound; - bool enabled = true; - bool HApublished = false; // strings to reduce flash memory usage (used more than twice) - static const char _name[]; - static const char _enabled[]; static const char _readInterval[]; static const char _parasite[]; static const char _parasitePin[]; @@ -163,6 +158,7 @@ class UsermodTemperature : public Usermod { #endif public: + UsermodTemperature(const char *name, bool enabled):Usermod(name, enabled) {} //WLEDMM: this shouldn't be necessary (passthrough of constructor), maybe because Usermod is an abstract class void setup() { int retries = 10; @@ -325,9 +321,10 @@ class UsermodTemperature : public Usermod { * addToConfig() (called from set.cpp) stores persistent properties to cfg.json */ void addToConfig(JsonObject &root) { + Usermod::addToConfig(root); + JsonObject top = root[FPSTR(_name)]; + // we add JSON object: {"Temperature": {"pin": 0, "degC": true}} - JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname - top[FPSTR(_enabled)] = enabled; top["pin"] = temperaturePin; // usermodparam top["degC"] = degC; // usermodparam top[FPSTR(_readInterval)] = readingInterval / 1000; @@ -342,17 +339,18 @@ class UsermodTemperature : public Usermod { * The function should return true if configuration was successfully loaded or false if there was no configuration. */ bool readFromConfig(JsonObject &root) { - // we look for JSON object: {"Temperature": {"pin": 0, "degC": true}} - int8_t newTemperaturePin = temperaturePin; + /*bool configComplete = */Usermod::readFromConfig(root); //WLEDMM: configComplete not implemented here (todo?) + JsonObject top = root[FPSTR(_name)]; DEBUG_PRINT(FPSTR(_name)); - JsonObject top = root[FPSTR(_name)]; + // we look for JSON object: {"Temperature": {"pin": 0, "degC": true}} + int8_t newTemperaturePin = temperaturePin; + if (top.isNull()) { DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); return false; } - enabled = top[FPSTR(_enabled)] | enabled; newTemperaturePin = top["pin"] | newTemperaturePin; degC = top["degC"] | degC; readingInterval = top[FPSTR(_readInterval)] | readingInterval/1000; @@ -385,9 +383,9 @@ class UsermodTemperature : public Usermod { void appendConfigData() { oappend(SET_F("addHB('Temperature');")); // WLEDMM - oappend(SET_F("addInfo('Temperature:parasite-pwr")); + oappend(SET_F("addInfo('Temperature:parasite-pwr")); //WLEDMM use literals oappend(SET_F("',1,'(if no Vcc connected)');")); // 0 is field type, 1 is actual field - oappend(SET_F("addInfo('Temperature:parasite-pwr-pin")); + oappend(SET_F("addInfo('Temperature:parasite-pwr-pin")); //WLEDMM use literals oappend(SET_F("',1,'(for external MOSFET)');")); // 0 is field type, 1 is actual field } @@ -398,8 +396,6 @@ class UsermodTemperature : public Usermod { }; // strings to reduce flash memory usage (used more than twice) -const char UsermodTemperature::_name[] PROGMEM = "Temperature"; -const char UsermodTemperature::_enabled[] PROGMEM = "enabled"; const char UsermodTemperature::_readInterval[] PROGMEM = "read-interval-s"; const char UsermodTemperature::_parasite[] PROGMEM = "parasite-pwr"; const char UsermodTemperature::_parasitePin[] PROGMEM = "parasite-pwr-pin"; diff --git a/usermods/customeffects/arti.h b/usermods/artifx/arti.h similarity index 93% rename from usermods/customeffects/arti.h rename to usermods/artifx/arti.h index 290a7604..b662a8d4 100644 --- a/usermods/customeffects/arti.h +++ b/usermods/artifx/arti.h @@ -55,7 +55,14 @@ #define ARTI_PRINT 1 //will show the printf calls const char spaces[51] PROGMEM = " "; - #define FREE_SIZE esp_get_free_heap_size() + #if defined(ARDUINO_ARCH_ESP32) + #define FREE_SIZE esp_get_free_heap_size() + #define asChar(x) x.as().c_str() //WLEDMM: tbd: find out why char * is causing crashes!!! + #else + #define FREE_SIZE ESP.getFreeHeap() + #define asChar(x) "" + //x.as() //WLEDMM: no arduinojson output for the moment as this is in testing phase + #endif // #define OPTIMIZED_TREE 1 #else //embedded #include "dependencies/ArduinoJson-recent.h" @@ -75,7 +82,8 @@ #include const char spaces[51] = " "; - #define FREE_SIZE (unsigned int)0 + #define FREE_SIZE (unsigned int)0 //No free heap function found on embedded + #define asChar(x) x.as() // #define OPTIMIZED_TREE 1 #endif @@ -88,53 +96,52 @@ void artiPrintf(char const * format, ...) va_start(argp, format); - if (!logToFile) - { - vprintf(format, argp); - } - else - { - #if ARTI_PLATFORM == ARTI_ARDUINO - // rocket science here! As logfile.printf causes crashes/wrong output we create our own printf here - // logFile.printf(format, argp); - for (int i = 0; i < strlen(format); i++) + // if (!logToFile) + // { + // //WLEDMM: not working to use USER_PRINTF here in case of arduino so moving to rocket science part + // vprintf(format, argp); + // } + // else + #if ARTI_PLATFORM == ARTI_ARDUINO + // rocket science here! As logfile.printf causes crashes/wrong output we create our own printf here + // logFile.printf(format, argp); + for (size_t i = 0; i < strlen(format); i++) + { + if (format[i] == '%') { - if (format[i] == '%') + switch (format[i+1]) { - switch (format[i+1]) - { - case 's': - logFile.print(va_arg(argp, const char *)); - break; - case 'u': - logFile.print(va_arg(argp, unsigned int)); - break; - case 'c': - logFile.print((char)va_arg(argp, int)); - break; - case 'f': - logFile.print(va_arg(argp, double)); - break; - case '%': - logFile.print("%"); // in case of %% - break; - default: - va_arg(argp, int); - // logFile.print(x); - logFile.print(format[i]); - logFile.print(format[i+1]); - } - i++; - } - else - { - logFile.print(format[i]); + case 's': + if (logToFile) logFile.print(va_arg(argp, const char *)); else USER_PRINT(va_arg(argp, const char *)); + break; + case 'u': + if (logToFile) logFile.print(va_arg(argp, unsigned int)); else USER_PRINT(va_arg(argp, unsigned int)); + break; + case 'c': + if (logToFile) logFile.print((char)va_arg(argp, int)); else USER_PRINT(va_arg(argp, int)); + break; + case 'f': + if (logToFile) logFile.print(va_arg(argp, double)); else USER_PRINT(va_arg(argp, double)); + break; + case '%': + if (logToFile) logFile.print("%"); else USER_PRINT("%"); // in case of %% + break; + default: + va_arg(argp, int); + // logFile.print(x); + if (logToFile) logFile.print(format[i]); else USER_PRINT(format[i]); + if (logToFile) logFile.print(format[i+1]); else USER_PRINT(format[i+1]); } + i++; + } + else + { + if (logToFile) logFile.print(format[i]); else USER_PRINT(format[i]); } - #else - vfprintf(logFile, format, argp); - #endif - } + } + #else + vfprintf(logFile, format, argp); + #endif va_end(argp); } @@ -560,7 +567,7 @@ class Lexer { { while (strncmp(this->text + this->pos, endTokens, strlen(endTokens)) != 0) this->advance(); - for (int i=0; iadvance(); } @@ -696,7 +703,7 @@ class Lexer { { strcpy(current_token.type, token_type); strcpy(current_token.value, token_value); - for (int i=0; iadvance(); return; } @@ -1072,12 +1079,12 @@ public: float arti_get_external_variable(uint8_t variable, float par1 = floatNull, float par2 = floatNull, float par3 = floatNull); void arti_set_external_variable(float value, uint8_t variable, float par1 = floatNull, float par2 = floatNull, float par3 = floatNull); bool loop(); - + uint8_t parse(JsonVariant parseTree, const char * node_name, char operatorx, JsonVariant expression, uint8_t depth = 0) { if (depth > 50) { - ERROR_ARTI("Error: Parse recursion level too deep at %s (%u)\n", parseTree.as().c_str(), depth); + ERROR_ARTI("Error: Parse recursion level too deep at %s (%u)\n", asChar(parseTree), depth); errorOccurred = true; } if (errorOccurred) return ResultFail; @@ -1101,7 +1108,7 @@ public: nextNode_name = expressionElement; //e.g. block nextExpression = nodeExpression; // e.g. ["LCURL",{"*": ["statement"]},"RCURL"] - // DEBUG_ARTI("%s %s %u\n", spaces+50-depth, nextNode_name, depth); //, parseTree.as().c_str() + // DEBUG_ARTI("%s %s %u\n", spaces+50-depth, nextNode_name, depth); //, asChar(parseTree) if (parseTree.is()) { @@ -1176,7 +1183,7 @@ public: } else { - ERROR_ARTI("%s Programming error: undefined %c %s\n", spaces+50-depth, objectOperator, objectElement.as().c_str()); + ERROR_ARTI("%s Programming error: undefined %c %s\n", spaces+50-depth, objectOperator, asChar(objectElement)); resultChild2 = ResultFail; } counter++; @@ -1185,7 +1192,7 @@ public: } //not or } // element is array else - ERROR_ARTI("%s Definition error: should be an array %s %c %s\n", spaces+50-depth, stringOrEmpty(nextNode_name), operatorx, objectElement.as().c_str()); + ERROR_ARTI("%s Definition error: should be an array %s %c %s\n", spaces+50-depth, stringOrEmpty(nextNode_name), operatorx, asChar(objectElement)); } else if (nextExpression.is()) // e.g. ["LPAREN", "expr*", "RPAREN"] { @@ -1230,9 +1237,9 @@ public: else //expressionElement is not a node, not a token, not an array and not an object { if (lexer->definitionJson.containsKey(nextExpression.as())) - ERROR_ARTI("%s Programming error: %s not a node, token, array or object in %s\n", spaces+50-depth, nextExpression.as().c_str(), stringOrEmpty(nextNode_name)); + ERROR_ARTI("%s Programming error: %s not a node, token, array or object in %s\n", spaces+50-depth, asChar(nextExpression), stringOrEmpty(nextNode_name)); else - ERROR_ARTI("%s Definition error: \"%s\": \"%s\" node should be embedded in array\n", spaces+50-depth, stringOrEmpty(nextNode_name), nextExpression.as().c_str()); + ERROR_ARTI("%s Definition error: \"%s\": \"%s\" node should be embedded in array\n", spaces+50-depth, stringOrEmpty(nextNode_name), asChar(nextExpression)); } //nextExpression is not a token if (!nodeExpression.isNull()) //if node @@ -1254,7 +1261,7 @@ public: } else //success { - DEBUG_ARTI("%s found %s\n", spaces+50-depth, nextNode_name);//, nextParseTree.as().c_str()); + DEBUG_ARTI("%s found %s\n", spaces+50-depth, nextNode_name);//, asChar(nextParseTree)); //parseTree optimization moved to optimize function // if (nextParseTree.is()) @@ -1269,10 +1276,10 @@ public: // { // // JsonObject nextParseTreeObject = nextParseTree.as(); - // DEBUG_ARTI("%s found %s:%s\n", spaces+50-depth, node_name, parseTree.as().c_str()); - // DEBUG_ARTI("%s found replace %s by %s %s\n", spaces+50-depth, nextNode_name, begin->key().c_str(), nextParseTree.as().c_str()); - // DEBUG_ARTI("%s expression %s\n", spaces+50-depth, expression.as().c_str()); - // DEBUG_ARTI("%s found %s\n", spaces+50-depth, nextParseTree[nextNode_name].as().c_str()); + // DEBUG_ARTI("%s found %s:%s\n", spaces+50-depth, node_name, asChar(parseTree)); + // DEBUG_ARTI("%s found replace %s by %s %s\n", spaces+50-depth, nextNode_name, begin->key().c_str(), asChar(nextParseTree)); + // DEBUG_ARTI("%s expression %s\n", spaces+50-depth, asChar(expression)); + // DEBUG_ARTI("%s found %s\n", spaces+50-depth, asChar(nextParseTree[nextNode_name])); // nextParseTree.remove(nextNode_name); // // char temp[charLength]; @@ -1281,11 +1288,11 @@ public: // // strcat(temp, begin->key().c_str()); // nextParseTree[begin->key()] = begin->value(); - // DEBUG_ARTI("%s found %s:%s\n", spaces+50-depth, node_name, parseTree.as().c_str()); + // DEBUG_ARTI("%s found %s:%s\n", spaces+50-depth, node_name, asChar(parseTree)); // } // } // else - // DEBUG_ARTI("%s no jsonobject??? %s\n", spaces+50-depth, parseTree.as().c_str()); + // DEBUG_ARTI("%s no jsonobject??? %s\n", spaces+50-depth, asChar(parseTree)); } } // if node @@ -1318,7 +1325,7 @@ public: } } else { //should never happen - ERROR_ARTI("%s Programming error: no array %s %c %s\n", spaces+50-depth, stringOrEmpty(node_name), operatorx, expression.as().c_str()); + ERROR_ARTI("%s Programming error: no array %s %c %s\n", spaces+50-depth, stringOrEmpty(node_name), operatorx, asChar(expression)); } return result; @@ -1327,10 +1334,10 @@ public: bool analyze(JsonVariant parseTree, const char * treeElement = nullptr, ScopedSymbolTable* current_scope = nullptr, uint8_t depth = 0) { - // ANDBG_ARTI("%s Depth %u %s\n", spaces+50-depth, depth, parseTree.as().c_str()); + // ANDBG_ARTI("%s Depth %u %s\n", spaces+50-depth, depth, asChar(parseTree)); if (depth > 24) //otherwise stack canary errors on Arduino (value determined after testing, should be revisited) { - ERROR_ARTI("Error: Analyze recursion level too deep at %s (%u)\n", parseTree.as().c_str(), depth); + ERROR_ARTI("Error: Analyze recursion level too deep at %s (%u)\n", asChar(parseTree), depth); errorOccurred = true; } if (errorOccurred) return false; @@ -1389,7 +1396,7 @@ public: else if (strcmp(valueStr, "||") == 0) parseTree["token"] = F_or; else - ERROR_ARTI("%s Programming error: token not defined as operator %s %s (%u)\n", spaces+50-depth, key, value.as().c_str(), depth); + ERROR_ARTI("%s Programming error: token not defined as operator %s %s (%u)\n", spaces+50-depth, key, asChar(value), depth); visitedAlready = true; } @@ -1543,7 +1550,7 @@ public: { JsonObject asop = value["assignoperator"]; JsonObject::iterator asopBegin = asop.begin(); - ANDBG_ARTI("%s %s\n", spaces+50-depth, asopBegin->value().as().c_str()); + ANDBG_ARTI("%s %s\n", spaces+50-depth, asChar(asopBegin->value())); if (strcmp(asopBegin->value(), "+=") == 0) value["assignoperator"] = F_plus; else if (strcmp(asopBegin->value(), "-=") == 0) @@ -1623,7 +1630,7 @@ public: { // string element = parseTree; //for some weird reason this causes a crash on esp32 - // ERROR_ARTI("%s Error: parseTree should be array or object %s (%u)\n", spaces+50-depth, parseTree.as().c_str(), depth); + // ERROR_ARTI("%s Error: parseTree should be array or object %s (%u)\n", spaces+50-depth, asChar(parseTree), depth); } return !errorOccurred; @@ -1632,7 +1639,7 @@ public: //https://dev.to/lefebvre/compilers-106---optimizer--ig8 bool optimize(JsonVariant parseTree, uint8_t depth = 0) { - // DEBUG_ARTI("%s optimize %s (%u)\n", spaces+50-depth, parseTree.as().c_str(), depth); + // DEBUG_ARTI("%s optimize %s (%u)\n", spaces+50-depth, asChar(parseTree), depth); if (parseTree.is()) { @@ -1660,7 +1667,7 @@ public: if (value.size() == 0) { parseTree.remove(key); - // DEBUG_ARTI("%s optimize: remove empty %s %s (%u)\n", spaces+50-depth, key, value.as().c_str(), depth); + // DEBUG_ARTI("%s optimize: remove empty %s %s (%u)\n", spaces+50-depth, key, asChar(value), depth); } visitedAlready = true; @@ -1682,7 +1689,7 @@ public: { //- check if a node is not used in analyzer / interpreter and has only one element: go to the parent and replace itself with its child (shrink) - // DEBUG_ARTI("%s node try to shrink %s : %s (%u)\n", spaces+50-depth, key, value.as().c_str(), value.size()); + // DEBUG_ARTI("%s node try to shrink %s : %s (%u)\n", spaces+50-depth, key, asChar(value), value.size()); JsonObject::iterator objectIterator = value.as().begin(); @@ -1694,26 +1701,26 @@ public: // // if (objectIterator2->value().is() ) //&& objectIterator.size() == 1 // { - // DEBUG_ARTI("%s node to shrink %s : %s from %s\n", spaces+50-depth, key, value.as().c_str(), parseTree.as().c_str()); - // DEBUG_ARTI("%s node to shrink %s : %s\n", spaces+50-depth, objectIterator->key().c_str(), objectIterator->value().as().c_str()); - // // DEBUG_ARTI("%s node to shrink %s : %s\n", spaces+50-depth, objectIterator2->key().c_str(), objectIterator2->value().as().c_str()); - // DEBUG_ARTI("%s node to shrink replace %s\n", spaces+50-depth, parseTree[key].as().c_str()); - // DEBUG_ARTI("%s node to shrink by %s\n", spaces+50-depth, objectIterator->value().as().c_str()); + // DEBUG_ARTI("%s node to shrink %s : %s from %s\n", spaces+50-depth, key, asChar(value), asChar(parseTree)); + // DEBUG_ARTI("%s node to shrink %s : %s\n", spaces+50-depth, objectIterator->key().c_str(), asChar(objectIterator->value())); + // // DEBUG_ARTI("%s node to shrink %s : %s\n", spaces+50-depth, objectIterator2->key().c_str(), asChar(objectIterator2->value())); + // DEBUG_ARTI("%s node to shrink replace %s\n", spaces+50-depth, asChar(parseTree[key])); + // DEBUG_ARTI("%s node to shrink by %s\n", spaces+50-depth, asChar(objectIterator->value())); // // parseTree[key][objectIterator->key().c_str()] = objectIterator2->value(); // parseTree[key] = objectIterator->value(); // } // // else // // { - // // DEBUG_ARTI("%s node to shrink2 %s : %s\n", spaces+50-depth, objectIterator2->key().c_str(), objectIterator2->value().as().c_str()); + // // DEBUG_ARTI("%s node to shrink2 %s : %s\n", spaces+50-depth, objectIterator2->key().c_str(), asChar(objectIterator2->value())); // // } // } // else - // DEBUG_ARTI("%s value should be an object %s in %s : %s from %s\n", spaces+50-depth, objectIterator->key().c_str(), key, objectIterator->value().as().c_str(), value.as().c_str()); + // DEBUG_ARTI("%s value should be an object %s in %s : %s from %s\n", spaces+50-depth, objectIterator->key().c_str(), key, asChar(objectIterator->value()), asChar(value)); if (stringToNode(objectIterator->key().c_str()) == F_NoNode) // if key not a node // if (objectIterator->value().size() == 1) { - DEBUG_ARTI("%s node to shrink %s in %s : %s from %s\n", spaces+50-depth, objectIterator->key().c_str(), key, value.as().c_str(), parseTree.as().c_str()); - // DEBUG_ARTI("%s node to shrink %s in %s = %s from %s\n", spaces+50-depth, objectIterator->key().c_str(), key, objectIterator->value().as().c_str(), parseTree[key].as().c_str()); + DEBUG_ARTI("%s node to shrink %s in %s : %s from %s\n", spaces+50-depth, objectIterator->key().c_str(), key, asChar(value), asChar(parseTree)); + // DEBUG_ARTI("%s node to shrink %s in %s = %s from %s\n", spaces+50-depth, objectIterator->key().c_str(), key, asChar(objectIterator->value()), asChar(parseTree[key])); parseTree[key] = objectIterator->value(); // parseTree[key]["old"] = objectIterator->key(); @@ -1730,7 +1737,7 @@ public: visitedAlready = true; } else - ERROR_ARTI("%s Programming Error: key no node and no token %s %s (%u)\n", spaces+50-depth, key, value.as().c_str(), depth); + ERROR_ARTI("%s Programming Error: key no node and no token %s %s (%u)\n", spaces+50-depth, key, asChar(value), depth); if (!visitedAlready && value.size() > 0) // if size == 0 then injected key/value like operator optimize(value, depth + 1); @@ -1747,27 +1754,27 @@ public: { JsonObject::iterator objectIterator = value.as().begin(); - // DEBUG_ARTI("%s try replace %s by %s %s\n", spaces+50-depth, key, objectIterator->key().c_str(), parseTree.as().c_str()); + // DEBUG_ARTI("%s try replace %s by %s %s\n", spaces+50-depth, key, objectIterator->key().c_str(), asChar(parseTree)); if (strcmp(objectIterator->key().c_str(), "ID") != 0) //&& definitionJson.containsKey(objectIterator->key())??? { - // DEBUG_ARTI("%s found %s:%s\n", spaces+50-depth, node_name, parseTree.as().c_str()); - // DEBUG_ARTI("%s found replace %s by %s %s\n", spaces+50-depth, key, objectIterator->key().c_str(), parseTree.as().c_str()); - // DEBUG_ARTI("%s found %s\n", spaces+50-depth, value.as().c_str()); + // DEBUG_ARTI("%s found %s:%s\n", spaces+50-depth, node_name, asChar(parseTree)); + // DEBUG_ARTI("%s found replace %s by %s %s\n", spaces+50-depth, key, objectIterator->key().c_str(), asChar(parseTree)); + // DEBUG_ARTI("%s found %s\n", spaces+50-depth, asChar(value)); - // DEBUG_ARTI("%s found %s\n", spaces+50-depth, parseTree.as().c_str()); - DEBUG_ARTI("%s replace %s by %s %s\n", spaces+50-depth, key, objectIterator->key().c_str(), parseTree.as().c_str()); + // DEBUG_ARTI("%s found %s\n", spaces+50-depth, asChar(parseTree)); + DEBUG_ARTI("%s replace %s by %s %s\n", spaces+50-depth, key, objectIterator->key().c_str(), asChar(parseTree)); parseTree.remove(key); // parseTree[key] = value; parseTree[objectIterator->key()] = objectIterator->value(); - // DEBUG_ARTI("%s found %s:%s\n", spaces+50-depth, node_name, parseTree.as().c_str()); + // DEBUG_ARTI("%s found %s:%s\n", spaces+50-depth, node_name, asChar(parseTree)); } // else // { - // DEBUG_ARTI("%s not shrinkable %s %s\n", spaces+50-depth, key, value.as().c_str()); + // DEBUG_ARTI("%s not shrinkable %s %s\n", spaces+50-depth, key, asChar(value)); // if (depth > 12) { // // parseTree.remove(key); // char temp[charLength]; @@ -1783,8 +1790,8 @@ public: if (stringToNode(objectIterator->key().c_str()) == F_NoNode) // if key not a node // if (objectIterator->value().size() == 1) { - DEBUG_ARTI("%s node to shrink %s in %s : %s from %s\n", spaces+50-depth, objectIterator->key().c_str(), key, value.as().c_str(), parseTree.as().c_str()); - // DEBUG_ARTI("%s node to shrink %s in %s = %s from %s\n", spaces+50-depth, objectIterator->key().c_str(), key, objectIterator->value().as().c_str(), parseTree[key].as().c_str()); + DEBUG_ARTI("%s node to shrink %s in %s : %s from %s\n", spaces+50-depth, objectIterator->key().c_str(), key, asChar(value), asChar(parseTree)); + // DEBUG_ARTI("%s node to shrink %s in %s = %s from %s\n", spaces+50-depth, objectIterator->key().c_str(), key, asChar(objectIterator->value()), asChar(parseTree[key])); parseTree[key] = objectIterator->value(); // parseTree[key]["old"] = objectIterator->key(); @@ -1829,10 +1836,10 @@ public: { // string element = parseTree; //for some weird reason this causes a crash on esp32 - ERROR_ARTI("%s Error: parseTree should be array or object %s (%u)\n", spaces+50-depth, parseTree.as().c_str(), depth); + ERROR_ARTI("%s Error: parseTree should be array or object %s (%u)\n", spaces+50-depth, asChar(parseTree), depth); } - // DEBUG_ARTI("%s optimized %s (%u)\n", spaces+50-depth, parseTree.as().c_str(), depth); + // DEBUG_ARTI("%s optimized %s (%u)\n", spaces+50-depth, asChar(parseTree), depth); return !errorOccurred; } //optimize @@ -1841,11 +1848,11 @@ public: bool interpret(JsonVariant parseTree, const char * treeElement = nullptr, ScopedSymbolTable* current_scope = nullptr, uint8_t depth = 0) { - // RUNLOG_ARTI("%s Interpret %s %s (%u)\n", spaces+50-depth, stringOrEmpty(treeElement), parseTree.as().c_str(), depth); + // RUNLOG_ARTI("%s Interpret %s %s (%u)\n", spaces+50-depth, stringOrEmpty(treeElement), asChar(parseTree), depth); if (depth >= 50) { - ERROR_ARTI("Error: Interpret recursion level too deep at %s (%u)\n", parseTree.as().c_str(), depth); + ERROR_ARTI("Error: Interpret recursion level too deep at %s (%u)\n", asChar(parseTree), depth); errorOccurred = true; } if (errorOccurred) return false; @@ -1858,7 +1865,7 @@ public: JsonVariant value = parseTreePair.value(); if (treeElement == nullptr || strcmp(treeElement, key) == 0) { - // RUNLOG_ARTI("%s Interpret object element %s %s\n", spaces+50-depth, key, value.as().c_str()); + // RUNLOG_ARTI("%s Interpret object element %s %s\n", spaces+50-depth, key, asChar(value)); bool visitedAlready = false; @@ -1870,7 +1877,7 @@ public: visitedAlready = true; else if (parseTree.containsKey("token")) //key is token { - // RUNLOG_ARTI("%s Token %s %s %s\n", spaces+50-depth, key, valueStr, parseTree.as().c_str()); + // RUNLOG_ARTI("%s Token %s %s %s\n", spaces+50-depth, key, valueStr, asChar(parseTree)); const char * valueStr = value; @@ -2183,7 +2190,7 @@ public: { uint8_t oldIndex = valueStack->stack_index; - // RUNLOG_ARTI("%s before expr term interpret %s %s\n", spaces+50-depth, key, value.as().c_str()); + // RUNLOG_ARTI("%s before expr term interpret %s %s\n", spaces+50-depth, key, asChar(value)); interpret(value, nullptr, current_scope, depth + 1); //pushes results // RUNLOG_ARTI("%s %s interpret > (%u - %u = %u)\n", spaces+50-depth, key, valueStack->stack_index, oldIndex, valueStack->stack_index - oldIndex); @@ -2414,7 +2421,7 @@ public: } } else { //not array - ERROR_ARTI("%s Error: parseTree should be array or object %s (%u)\n", spaces+50-depth, parseTree.as().c_str(), depth); + ERROR_ARTI("%s Error: parseTree should be array or object %s (%u)\n", spaces+50-depth, asChar(parseTree), depth); } return !errorOccurred; @@ -2443,6 +2450,12 @@ public: errorOccurred = false; frameCounter = 0; + // softhack007 check that programName has max 43 chars: fileNameLength -7 ("/" +Name + ".wled\0") + if ((programName == NULL) || (strlen(programName) < 1) || (strlen(programName) > (fileNameLength-7))) { + ERROR_ARTI("Program name '%s' is invalid. Program Name must be less than %d chars.\n", programName, fileNameLength-7); + return false; + } + logToFile = true; //open logFile if (logToFile) @@ -2450,7 +2463,7 @@ public: #if ARTI_PLATFORM == ARTI_ARDUINO strcpy(logFileName, "/"); #endif - strcpy(logFileName, programName); + strcat(logFileName, programName); // softhack007 this may overflow logFileName, in case programName has more than 44 chars strcat(logFileName, ".log"); #if ARTI_PLATFORM == ARTI_ARDUINO @@ -2519,22 +2532,29 @@ public: return false; } + char programFileName[fileNameLength]; + #if ARTI_PLATFORM == ARTI_ARDUINO + strcpy(programFileName, "/"); + #endif + strcat(programFileName, programName); // softhack007 this may overflow programFileName, in case programName has more than 43 chars + strcat(programFileName, ".wled"); + #if ARTI_PLATFORM == ARTI_ARDUINO File programFile; - programFile = WLED_FS.open(programName, "r"); + programFile = WLED_FS.open(programFileName, "r"); #else std::fstream programFile; - programFile.open(programName, std::ios::in); + programFile.open(programFileName, std::ios::in); #endif - MEMORY_ARTI("open %s %u ✓\n", programName, FREE_SIZE); + MEMORY_ARTI("open %s %u ✓\n", programFileName, FREE_SIZE); if (!programFile) { - ERROR_ARTI("Program file %s not found\n", programName); + ERROR_ARTI("Program file %s not found\n", programFileName); return false; } //open programFile - char * programText; + char * programText = nullptr; uint16_t programFileSize; #if ARTI_PLATFORM == ARTI_ARDUINO programFileSize = programFile.size(); @@ -2550,11 +2570,6 @@ public: #endif programFile.close(); - char parseTreeName[fileNameLength]; - strcpy(parseTreeName, programName); - // if (loadParseTreeFile) - // strcpy(parseTreeName, "Gen"); - strcat(parseTreeName, ".json"); #if ARTI_PLATFORM == ARTI_ARDUINO parseTreeJsonDoc = new PSRAMDynamicJsonDocument(32768); //less memory on arduino: 32 vs 64 bit? #else @@ -2566,6 +2581,15 @@ public: //parse #ifdef ARTI_DEBUG // only read write file if debug is on + char parseTreeName[fileNameLength]; + #if ARTI_PLATFORM == ARTI_ARDUINO + strcpy(parseTreeName, "/"); + #endif + strcat(parseTreeName, programName); + // if (loadParseTreeFile) + // strcpy(parseTreeName, "Gen"); + strcat(parseTreeName, ".json"); + #if ARTI_PLATFORM == ARTI_ARDUINO File parseTreeFile; parseTreeFile = WLED_FS.open(parseTreeName, loadParseTreeFile?"r":"w"); @@ -2575,7 +2599,11 @@ public: #endif #endif - if (stages < 1) {close(); return true;} + if (stages < 1) { + if (nullptr != programText) free(programText); // softhack007 prevent memory leak + close(); + return true; + } if (!loadParseTreeFile) { @@ -2584,18 +2612,24 @@ public: lexer = new Lexer(programText, definitionJson); lexer->get_next_token(); - if (stages < 2) {close(); return true;} + if (stages < 2) { + //if (nullptr != programText) free(programText); // softhack007 needed to prevent memory leak? lexer has a pointer to programText so its still in use maybe? + close(); + return true; + } uint8_t result = parse(parseTreeJson, startNode, '&', lexer->definitionJson[startNode], 0); if (this->lexer->pos != strlen(this->lexer->text)) { ERROR_ARTI("Node %s Program not entirely parsed (%u,%u) %u of %u\n", startNode, this->lexer->lineno, this->lexer->column, this->lexer->pos, (unsigned int)strlen(this->lexer->text)); + //if (nullptr != programText) free(programText); // softhack007 needed to prevent memory leak? lexer has a pointer to programText so its still in use maybe? return false; } else if (result == ResultFail) { ERROR_ARTI("Node %s Program parsing failed (%u,%u) %u of %u\n", startNode, this->lexer->lineno, this->lexer->column, this->lexer->pos, (unsigned int)strlen(this->lexer->text)); + //if (nullptr != programText) free(programText); // softhack007 needed to prevent memory leak? lexer has a pointer to programText so its still in use maybe? return false; } else diff --git a/usermods/customeffects/arti_wled.h b/usermods/artifx/arti_wled.h similarity index 93% rename from usermods/customeffects/arti_wled.h rename to usermods/artifx/arti_wled.h index 1b78af63..f905f743 100644 --- a/usermods/customeffects/arti_wled.h +++ b/usermods/artifx/arti_wled.h @@ -15,7 +15,8 @@ #define ARTI_ARDUINO 1 #define ARTI_EMBEDDED 2 -#ifdef ESP32 //ESP32 is set in wled context: small trick to set WLED context +// #ifdef ESP32 //ESP32 is set in wled context: small trick to set WLED context +#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_ESP8266) //ESP32 is set in wled context: small trick to set WLED context #define ARTI_PLATFORM ARTI_ARDUINO // else on Windows/Linux/Mac... #endif @@ -86,12 +87,16 @@ enum Externals F_square, F_clamp, - F_printf + F_print, + F_jsonToPixels, //reorder only when creating new wledvxyz.json + F_frameTime, + F_soundPressure }; #if ARTI_PLATFORM != ARTI_ARDUINO #define PI 3.141592654 #endif +uint32_t frameTime = 0; float ARTI::arti_external_function(uint8_t function, float par1, float par2, float par3, float par4, float par5) { @@ -189,10 +194,12 @@ float ARTI::arti_external_function(uint8_t function, float par1, float par2, flo SEGMENT.drawLine(par1, par2, par3, par4, par5); return floatNull; case F_drawArc: - if (par5 == floatNull) - SEGMENT.drawArc(par1, par2, par3, par4); - else - SEGMENT.drawArc(par1, par2, par3, par4, par5); //fillColor + #ifndef WLED_DISABLE_2D + if (par5 == floatNull) + SEGMENT.drawArc(par1, par2, par3, par4); + else + SEGMENT.drawArc(par1, par2, par3, par4, par5); //fillColor + #endif return floatNull; case F_constrain: return constrain(par1, par2, par3); @@ -207,6 +214,10 @@ float ARTI::arti_external_function(uint8_t function, float par1, float par2, flo case F_millis: return millis(); + case F_jsonToPixels: + SEGMENT.jsonToPixels(SEGMENT.name,(uint8_t)par1); + return floatNull; + default: {} } #else // not arduino @@ -271,6 +282,9 @@ float ARTI::arti_external_function(uint8_t function, float par1, float par2, flo case F_millis: return 1000; + + case F_jsonToPixels: + return par1; } #endif @@ -313,7 +327,7 @@ float ARTI::arti_external_function(uint8_t function, float par1, float par2, flo return t > par3 ? par3 : t; } - case F_printf: { + case F_print: { if (par3 == floatNull) { if (par2 == floatNull) { PRINT_ARTI("%f\n", par1); @@ -368,15 +382,17 @@ float ARTI::arti_get_external_variable(uint8_t variable, float par1, float par2, case F_custom3Slider: return SEGMENT.custom3; case F_volume: + case F_soundPressure: { um_data_t *um_data; if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { // add support for no audio um_data = simulateSound(SEGMENT.soundSim); } - float volumeSmth = *(float*) um_data->u_data[0]; - - return volumeSmth; + if (variable == F_volume) + return *(float*) um_data->u_data[0]; //volumeSmth + else + return *(float*) um_data->u_data[9]; //soundPressure } case F_hour: return ((float)hour(localTime)); @@ -419,6 +435,8 @@ float ARTI::arti_get_external_variable(uint8_t variable, float par1, float par2, return F_custom3Slider; case F_volume: return F_volume; + case F_soundPressure: + return F_soundPressure; case F_hour: return F_hour; @@ -437,7 +455,7 @@ float ARTI::arti_get_external_variable(uint8_t variable, float par1, float par2, void ARTI::arti_set_external_variable(float value, uint8_t variable, float par1, float par2, float par3) { #if ARTI_PLATFORM == ARTI_ARDUINO - // MEMORY_ARTI("%s %s %u %u (%u)\n", spaces+50-depth, variable_name, par1, par2, esp_get_free_heap_size()); + // MEMORY_ARTI("%s %s %u %u (%u)\n", spaces+50-depth, variable_name, par1, par2, FREE_SIZE); switch (variable) { case F_leds: @@ -452,6 +470,9 @@ void ARTI::arti_set_external_variable(float value, uint8_t variable, float par1, SEGMENT.setPixelColorXY((uint16_t)par1%SEGMENT.virtualWidth(), (uint16_t)par2%SEGMENT.virtualHeight(), value); //2D value!! return; + case F_frameTime: + frameTime = (uint16_t)value; + return; } #else switch (variable) @@ -468,6 +489,9 @@ void ARTI::arti_set_external_variable(float value, uint8_t variable, float par1, RUNLOG_ARTI("arti_set_external_variable: leds(%f, %f) := %f\n", par1, par2, value); return; + case F_frameTime: + frameTime = (uint16_t)value; + return; } #endif diff --git a/usermods/customeffects/customeffects.css b/usermods/artifx/artifx.css similarity index 100% rename from usermods/customeffects/customeffects.css rename to usermods/artifx/artifx.css diff --git a/usermods/customeffects/customeffects.js b/usermods/artifx/artifx.js similarity index 62% rename from usermods/customeffects/customeffects.js rename to usermods/artifx/artifx.js index e9f51307..892afb5c 100644 --- a/usermods/customeffects/customeffects.js +++ b/usermods/artifx/artifx.js @@ -9,7 +9,7 @@ function toggleCEEditor(name, segID) { d.getElementById('ceEditor').style.transform = (isCEEditor) ? "translateY(0px)":"translateY(100%)"; } -function fetchAndExecute(url, name, callback) +function fetchAndExecute(url, name, callback, callError) { fetch (url+name, { @@ -17,8 +17,8 @@ function fetchAndExecute(url, name, callback) }) .then(res => { if (!res.ok) { - showToast("File " + name + " not found", true); - return ""; + callError("File " + name + " not found"); + return ""; } return res.text(); }) @@ -26,10 +26,7 @@ function fetchAndExecute(url, name, callback) callback(text); }) .catch(function (error) { - showToast("Error getting " + name, true); - // showToast(error, true); - // console.log(error); - presetError(false); + callError("Error getting " + name); }) .finally(() => { // if (callback) setTimeout(callback,99); @@ -53,6 +50,9 @@ function loadLogFile(name, attempt) { } else ceLogArea.value = logtext; + }, function(error){ + showToast(error); + console.log(error); }); } @@ -72,11 +72,11 @@ function uploadFileWithText(name, text) } function saveCE(name, segID) { - showToast("Saving " + name); + showToast("Saving " + name + ".wled"); var ceProgramArea = d.getElementById("ceProgramArea"); - uploadFileWithText("/" + name, ceProgramArea.value); + uploadFileWithText("/" + name + ".wled", ceProgramArea.value); var obj = {"seg": {"id": segID, "reset": true}}; requestJson(obj); @@ -93,36 +93,45 @@ function populateCEEditor(name, segID) { fetchAndExecute((loc?`http://${locip}`:'.') + "/", name + ".wled", function(text) { - var cn=`Custom Effects Editor
+ var cn=`ARTI-FX Editor
${name}.wled

-
- +
+
- -
- -
+ +
+ +

Compile and Run Log

- Run log > 3 seconds is send to Serial Ouput.`; + Run log > 3 seconds is send to Serial Ouput.
+ 🥚 + 🥚 + 🥚`; d.getElementById('kceEditor').innerHTML = cn; var ceLogArea = d.getElementById("ceLogArea"); ceLogArea.value = "."; - loadLogFile(name + ".wled.log", 1); + loadLogFile(name + ".log", 1); + }, function(error){ + showToast(error); + console.log(error); }); } -function downloadCEFile(name) { - var url = "https://raw.githubusercontent.com/MoonModules/WLED-Effects/master/CustomEffects/wled/"; +function downloadGHFile(url, name, save=false, warn=false) { //Githubfile + if (url == "CE") url = "https://raw.githubusercontent.com/MoonModules/WLED-Effects/master/ARTIFX/wled/"; + if (url == "HBB") url = "https://raw.githubusercontent.com/MoonModules/WLED-Effects/master/Presets/HB_PresetPack210808_32x32_16seg/Base%20pack/"; + if (url == "HBE") url = "https://raw.githubusercontent.com/MoonModules/WLED-Effects/master/Presets/HB_PresetPack210808_32x32_16seg/Effects%20pack/"; + if (url == "LM") url = "https://raw.githubusercontent.com/MoonModules/WLED-Effects/master/Ledmaps/"; fetchAndExecute(url, name, function(text) { - if (name == "wledv033.json" || name == "presets.json") { - if (!confirm('Are you sure to download/overwrite ' + name + '?')) + if (save) { + if (warn && !confirm('Are you sure to download/overwrite ' + name + '?')) return; uploadFileWithText("/" + name, text); } @@ -131,6 +140,9 @@ function downloadCEFile(name) { var ceProgramArea = d.getElementById("ceProgramArea"); ceProgramArea.value = text; } + }, function(error){ + showToast(error); + console.log(url + name,error); }); return; @@ -155,7 +167,7 @@ function downloadCEFile(name) { function loadCETemplate(name) { var ceProgramArea = d.getElementById("ceProgramArea"); ceProgramArea.value = `/* - Custom Effects Template + ARTIFX Template */ program ${name} { diff --git a/usermods/customeffects/usermod_v2_customeffects.h b/usermods/artifx/usermod_v2_artifx.h similarity index 82% rename from usermods/customeffects/usermod_v2_customeffects.h rename to usermods/artifx/usermod_v2_artifx.h index fec31195..ded3bfe4 100644 --- a/usermods/customeffects/usermod_v2_customeffects.h +++ b/usermods/artifx/usermod_v2_artifx.h @@ -1,6 +1,6 @@ /* - @title Usermod Custom Effects (CE) - @file usermod_v2_customeffects.h + @title Usermod ARTIFX (AF) + @file usermod_v2_artifx.h @date 20220818 @author Ewoud Wijma @Copyright (c) 2023 Ewoud Wijma @@ -17,7 +17,7 @@ ARTI * arti; //effect function -uint16_t mode_customEffect(void) { +uint16_t mode_ARTIFX(void) { //tbd: move statics to SEGMENT.data static bool succesful; static bool notEnoughHeap; @@ -46,12 +46,7 @@ uint16_t mode_customEffect(void) { // artiWrapper = reinterpret_cast(SEGENV.data); arti = new ARTI(); - char programFileName[fileNameLength]; - strcpy(programFileName, "/"); - strcat(programFileName, currentEffect); - strcat(programFileName, ".wled"); - - succesful = arti->setup("/wledv033.json", programFileName); + succesful = arti->setup("/wledv033.json", currentEffect); if (!succesful) ERROR_ARTI("Setup not succesful\n"); @@ -60,9 +55,9 @@ uint16_t mode_customEffect(void) { { if (succesful) // && SEGENV.call < 250 for each frame { - if (esp_get_free_heap_size() <= 20000) + if (FREE_SIZE <= 20000) { - ERROR_ARTI("Not enough free heap (%u <= 30000)\n", esp_get_free_heap_size()); + ERROR_ARTI("Not enough free heap (%u <= 30000)\n", FREE_SIZE); notEnoughHeap = true; succesful = false; } @@ -72,7 +67,7 @@ uint16_t mode_customEffect(void) { // static int previousCall; // if (millis() - previousMillis > 5000) { //tried SEGENV.aux0 but that looks to be overwritten!!! (dangling pointer???) // previousMillis = millis(); - // MEMORY_ARTI("Heap renderFrame %u %u fps\n", esp_get_free_heap_size(), (SEGENV.call - previousCall)/5); + // MEMORY_ARTI("Heap renderFrame %u %u fps\n", FREE_SIZE, (SEGENV.call - previousCall)/5); // previousCall = SEGENV.call; // } @@ -82,8 +77,8 @@ uint16_t mode_customEffect(void) { else { arti->closeLog(); - if (notEnoughHeap && esp_get_free_heap_size() > 20000) { - ERROR_ARTI("Again enough free heap, restart effect (%u > 30000)\n", esp_get_free_heap_size()); + if (notEnoughHeap && FREE_SIZE > 20000) { + ERROR_ARTI("Again enough free heap, restart effect (%u > 30000)\n", FREE_SIZE); succesful = true; notEnoughHeap = false; strcpy(previousEffect, ""); // force new create @@ -96,12 +91,12 @@ uint16_t mode_customEffect(void) { } } - return FRAMETIME; + return MAX(frameTime,FRAMETIME); } -static const char _data_FX_MODE_CUSTOMEFFECT[] PROGMEM = "⚙️ Custom Effect@Speed,Intensity,Custom 1, Custom 2, Custom 3;!;!;1;mp12=0"; +static const char _data_FX_MODE_ARTIFX[] PROGMEM = "⚙️ ARTI-FX ☾@Speed,Intensity,Custom 1, Custom 2, Custom 3;!;!;1;mp12=0"; -class CustomEffectsUserMod : public Usermod { +class ARTIFXUserMod : public Usermod { private: // strings to reduce flash memory usage (used more than twice) static const char _name[]; //usermod name @@ -115,7 +110,7 @@ class CustomEffectsUserMod : public Usermod { void setup() { if (!initDone) - strip.addEffect(FX_MODE_CUSTOMEFFECT, &mode_customEffect, _data_FX_MODE_CUSTOMEFFECT); + strip.addEffect(FX_MODE_ARTIFX, &mode_ARTIFX, _data_FX_MODE_ARTIFX); initDone = true; enabled = true; } @@ -200,9 +195,9 @@ class CustomEffectsUserMod : public Usermod { */ uint16_t getId() { - return USERMOD_ID_CUSTOMEFFECTS; + return USERMOD_ID_ARTIFX; } }; // strings to reduce flash memory usage (used more than twice) -const char CustomEffectsUserMod::_name[] PROGMEM = "CustomEffects"; +const char ARTIFXUserMod::_name[] PROGMEM = "ARTIFX"; diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 182e8d7a..05305671 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -29,9 +29,9 @@ // #define SR_DEBUG // generic SR DEBUG messages #ifdef SR_DEBUG - #define DEBUGSR_PRINT(x) DEBUGOUT.print(x) - #define DEBUGSR_PRINTLN(x) DEBUGOUT.println(x) - #define DEBUGSR_PRINTF(x...) DEBUGOUT.printf(x) + #define DEBUGSR_PRINT(x) DEBUGOUT(x) + #define DEBUGSR_PRINTLN(x) DEBUGOUTLN(x) + #define DEBUGSR_PRINTF(x...) DEBUGOUTF(x) #else #define DEBUGSR_PRINT(x) #define DEBUGSR_PRINTLN(x) @@ -55,10 +55,10 @@ #endif #if defined(MIC_LOGGER) || defined(FFT_SAMPLING_LOG) - #define PLOT_PRINT(x) DEBUGOUT.print(x) - #define PLOT_PRINTLN(x) DEBUGOUT.println(x) - #define PLOT_PRINTF(x...) DEBUGOUT.printf(x) - #define PLOT_FLUSH() DEBUGOUT.flush() + #define PLOT_PRINT(x) DEBUGOUT(x) + #define PLOT_PRINTLN(x) DEBUGOUTLN(x) + #define PLOT_PRINTF(x...) DEBUGOUTF(x) + #define PLOT_FLUSH() DEBUGOUTFlush() #else #define PLOT_PRINT(x) #define PLOT_PRINTLN(x) @@ -121,7 +121,16 @@ const float agcSampleSmooth[AGC_NUM_PRESETS] = { 1/12.f, 1/6.f, 1/16.f}; // static AudioSource *audioSource = nullptr; static volatile bool disableSoundProcessing = false; // if true, sound processing (FFT, filters, AGC) will be suspended. "volatile" as its shared between tasks. -static bool useBandPassFilter = false; // if true, enables a bandpass filter 80Hz-16Khz to remove noise. Applies before FFT. +static uint8_t useInputFilter = 0; // enables low-cut filtering. Applies before FFT. + +//WLEDMM add experimental settings +static uint8_t micLevelMethod = 0; // 0=old "floating" miclev, 1=new "freeze" mode, 2=fast freeze mode (mode 2 may not work for you) +#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) +static uint8_t averageByRMS = false; // false: use mean value, true: use RMS (root mean squared). use simpler method on slower MCUs. +#else +static uint8_t averageByRMS = true; // false: use mean value, true: use RMS (root mean squared). use better method on fast MCUs. +#endif +static uint8_t freqDist = 0; // 0=old 1=rightshift mode // audioreactive variables shared with FFT task static float micDataReal = 0.0f; // MicIn data with full 24bit resolution - lowest 8bit after decimal point @@ -136,7 +145,7 @@ static float volumeSmth = 0.0f; // either sampleAvg or sampleAgc depending // peak detection static bool samplePeak = false; // Boolean flag for peak - used in effects. Responding routine may reset this flag. Auto-reset after strip.getMinShowDelay() -static uint8_t maxVol = 10; // Reasonable value for constant volume for 'peak detector', as it won't always trigger (deprecated) +static uint8_t maxVol = 31; // (was 10) Reasonable value for constant volume for 'peak detector', as it won't always trigger (deprecated) static uint8_t binNum = 8; // Used to select the bin for FFT based beat detection (deprecated) static bool udpSamplePeak = false; // Boolean flag for peak. Set at the same tiem as samplePeak, but reset by transmitAudioData static unsigned long timeOfPeak = 0; // time of last sample peak detection. @@ -174,7 +183,7 @@ static TaskHandle_t FFT_Task = nullptr; static const float fftResultPink[MAX_PINK+1][NUM_GEQ_CHANNELS] = { { 1.70f, 1.71f, 1.73f, 1.78f, 1.68f, 1.56f, 1.55f, 1.63f, 1.79f, 1.62f, 1.80f, 2.06f, 2.47f, 3.35f, 6.83f, 9.55f }, // 0 default from SR WLED // { 1.30f, 1.32f, 1.40f, 1.46f, 1.52f, 1.57f, 1.68f, 1.80f, 1.89f, 2.00f, 2.11f, 2.21f, 2.30f, 2.39f, 3.09f, 4.34f }, // - Line-In Generic -> pink noise adjustment only - { 1.24f, 1.20f, 1.30f, 1.40f, 1.48f, 1.57f, 1.68f, 1.80f, 1.89f, 2.00f, 2.14f, 2.26f, 2.60f, 3.00f, 3.70f, 5.20f }, // 1 Line-In CS5343 + { 2.35f, 1.32f, 1.32f, 1.40f, 1.48f, 1.57f, 1.68f, 1.80f, 1.89f, 1.95f, 2.14f, 2.26f, 2.50f, 2.90f, 4.20f, 6.50f }, // 1 Line-In CS5343 + DC blocker { 1.82f, 1.72f, 1.70f, 1.50f, 1.52f, 1.57f, 1.68f, 1.80f, 1.89f, 2.00f, 2.11f, 2.21f, 2.30f, 2.90f, 3.86f, 6.29f}, // 2 IMNP441 datasheet response profile * pink noise { 2.80f, 2.20f, 1.30f, 1.15f, 1.55f, 2.45f, 4.20f, 2.80f, 3.20f, 3.60f, 4.20f, 4.90f, 5.70f, 6.05f,10.50f,14.85f}, // 3 IMNP441 - big speaker, strong bass @@ -182,7 +191,7 @@ static const float fftResultPink[MAX_PINK+1][NUM_GEQ_CHANNELS] = { { 12.0f, 6.60f, 2.60f, 1.15f, 1.35f, 2.05f, 2.85f, 2.50f, 2.85f, 3.30f, 2.25f, 4.35f, 3.80f, 3.75f, 6.50f, 9.00f}, // 4 IMNP441 - voice, or small speaker { 2.75f, 1.60f, 1.40f, 1.46f, 1.52f, 1.57f, 1.68f, 1.80f, 1.89f, 2.00f, 2.11f, 2.21f, 2.30f, 1.75f, 2.55f, 3.60f }, // 5 ICS-43434 datasheet response * pink noise - { 2.25f, 1.20f, 1.00f, 1.20f, 1.80f, 3.20f, 5.10f, 5.50f, 4.00f, 4.80f, 6.70f, 6.40f, 5.80f, 3.90f, 6.00f, 5.10f }, // 6 ICS-43434 - big speaker, strong bass + { 2.90f, 1.25f, 0.75f, 1.08f, 2.35f, 3.55f, 3.60f, 3.40f, 2.75f, 3.45f, 4.40f, 6.35f, 6.80f, 6.80f, 8.50f,10.64f }, // 6 ICS-43434 - big speaker, strong bass { 1.65f, 1.00f, 1.05f, 1.30f, 1.48f, 1.30f, 1.80f, 3.00f, 1.50f, 1.65f, 2.56f, 3.00f, 2.60f, 2.30f, 5.00f, 3.00f }, // 7 SPM1423 { 2.25f, 1.60f, 1.30f, 1.60f, 2.20f, 3.20f, 3.06f, 2.60f, 2.85f, 3.50f, 4.10f, 4.80f, 5.70f, 6.05f,10.50f,14.85f }, // 8 userdef #1 for ewowi (enhance median/high freqs) @@ -223,6 +232,7 @@ static const float fftResultPink[MAX_PINK+1][NUM_GEQ_CHANNELS] = { // globals and FFT Output variables shared with animations static float FFT_MajorPeak = 1.0f; // FFT: strongest (peak) frequency static float FFT_Magnitude = 0.0f; // FFT: volume (magnitude) of peak frequency +static float FFT_MajPeakSmth = 1.0f; // FFT: (peak) frequency, smooth static uint8_t fftResult[NUM_GEQ_CHANNELS]= {0};// Our calculated freq. channel result table to be used by effects #if defined(WLED_DEBUG) || defined(SR_DEBUG) || defined(SR_STATS) static float fftTaskCycle = 0; // avg cycle time for FFT task @@ -264,7 +274,8 @@ constexpr uint16_t samplesFFT = 512; // Samples in an FFT batch - Thi constexpr uint16_t samplesFFT_2 = 256; // meaningfull part of FFT results - only the "lower half" contains useful information. // the following are observed values, supported by a bit of "educated guessing" //#define FFT_DOWNSCALE 0.65f // 20kHz - downscaling factor for FFT results - "Flat-Top" window @20Khz, old freq channels -#define FFT_DOWNSCALE 0.46f // downscaling factor for FFT results - for "Flat-Top" window @22Khz, new freq channels +//#define FFT_DOWNSCALE 0.46f // downscaling factor for FFT results - for "Flat-Top" window @22Khz, new freq channels +#define FFT_DOWNSCALE 0.40f // downscaling factor for FFT results, RMS averaging #define LOG_256 5.54517744f // log(256) // These are the input and output vectors. Input vectors receive computed results from FFT. @@ -303,29 +314,51 @@ static float mapf(float x, float in_min, float in_max, float out_min, float out_ } // compute average of several FFT resut bins -#if 1 // linear average -static float fftAddAvg(int from, int to) { +// linear average +static float fftAddAvgLin(int from, int to) { float result = 0.0f; for (int i = from; i <= to; i++) { result += vReal[i]; } return result / float(to - from + 1); } -#else // RMS average -static float fftAddAvg(int from, int to) { +// RMS average +static float fftAddAvgRMS(int from, int to) { double result = 0.0; for (int i = from; i <= to; i++) { result += vReal[i] * vReal[i]; } return sqrtf(result / float(to - from + 1)); } -#endif + +static float fftAddAvg(int from, int to) { + if (from == to) return vReal[from]; // small optimization + if (averageByRMS) return fftAddAvgRMS(from, to); // use SMS + else return fftAddAvgLin(from, to); // use linear average +} #if defined(CONFIG_IDF_TARGET_ESP32C3) constexpr bool skipSecondFFT = true; #else constexpr bool skipSecondFFT = false; #endif + +// High-Pass "DC blocker" filter +// see https://www.dsprelated.com/freebooks/filters/DC_Blocker.html +static void runDCBlocker(uint_fast16_t numSamples, float *sampleBuffer) { + constexpr float filterR = 0.990f; // around 40hz + static float xm1 = 0.0f; + static float ym1 = 0.0f; + + for (unsigned i=0; i < numSamples; i++) { + float value = sampleBuffer[i]; + float filtered = value-xm1 + filterR*ym1; + xm1 = value; + ym1 = filtered; + sampleBuffer[i] = filtered; + } +} + // // FFT main task // @@ -395,7 +428,12 @@ void FFTcode(void * parameter) // band pass filter - can reduce noise floor by a factor of 50 // downside: frequencies below 100Hz will be ignored - if (useBandPassFilter) runMicFilter(samplesFFT, vReal); + if ((useInputFilter > 0) && (useInputFilter < 99)) { + switch(useInputFilter) { + case 1: runMicFilter(samplesFFT, vReal); break; // PDM microphone bandpass + case 2: runDCBlocker(samplesFFT, vReal); break; // generic Low-Cut + DC blocker (~40hz cut-off) + } + } // find highest sample in the batch float maxSample = 0.0f; // max sample from FFT batch @@ -461,6 +499,7 @@ void FFTcode(void * parameter) 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_MajPeakSmth = FFT_MajPeakSmth + 0.42 * (FFT_MajorPeak - FFT_MajPeakSmth); // I like this "swooping peak" look } else { // skip second run --> clear fft results, keep peaks memset(vReal, 0, sizeof(vReal)); @@ -512,35 +551,67 @@ void FFTcode(void * parameter) fftCalc[14] = fftAddAvg(147,194); // 2940 - 3900 fftCalc[15] = fftAddAvg(194,250); // 3880 - 5000 // avoid the last 5 bins, which are usually inaccurate #else - /* new mapping, optimized for 22050 Hz by softhack007 */ + //WLEDMM: different distributions + if (freqDist == 0) { + /* new mapping, optimized for 22050 Hz by softhack007 --- update: removed overlap */ // bins frequency range - if (useBandPassFilter) { + if (useInputFilter==1) { // skip frequencies below 100hz - fftCalc[ 0] = 0.8f * fftAddAvg(3,4); - fftCalc[ 1] = 0.9f * fftAddAvg(4,5); - fftCalc[ 2] = fftAddAvg(5,6); - fftCalc[ 3] = fftAddAvg(6,7); + fftCalc[ 0] = 0.8f * fftAddAvg(3,3); + fftCalc[ 1] = 0.9f * fftAddAvg(4,4); + fftCalc[ 2] = fftAddAvg(5,5); + fftCalc[ 3] = fftAddAvg(6,6); // don't use the last bins from 206 to 255. fftCalc[15] = fftAddAvg(165,205) * 0.75f; // 40 7106 - 8828 high -- with some damping } else { - fftCalc[ 0] = fftAddAvg(1,2); // 1 43 - 86 sub-bass - fftCalc[ 1] = fftAddAvg(2,3); // 1 86 - 129 bass - fftCalc[ 2] = fftAddAvg(3,5); // 2 129 - 216 bass - fftCalc[ 3] = fftAddAvg(5,7); // 2 216 - 301 bass + midrange + fftCalc[ 0] = fftAddAvg(1,1); // 1 43 - 86 sub-bass + fftCalc[ 1] = fftAddAvg(2,2); // 1 86 - 129 bass + fftCalc[ 2] = fftAddAvg(3,4); // 2 129 - 216 bass + fftCalc[ 3] = fftAddAvg(5,6); // 2 216 - 301 bass + midrange // don't use the last bins from 216 to 255. They are usually contaminated by aliasing (aka noise) fftCalc[15] = fftAddAvg(165,215) * 0.70f; // 50 7106 - 9259 high -- with some damping } - fftCalc[ 4] = fftAddAvg(7,10); // 3 301 - 430 midrange - fftCalc[ 5] = fftAddAvg(10,13); // 3 430 - 560 midrange - fftCalc[ 6] = fftAddAvg(13,19); // 5 560 - 818 midrange - fftCalc[ 7] = fftAddAvg(19,26); // 7 818 - 1120 midrange -- 1Khz should always be the center ! - fftCalc[ 8] = fftAddAvg(26,33); // 7 1120 - 1421 midrange - fftCalc[ 9] = fftAddAvg(33,44); // 9 1421 - 1895 midrange - fftCalc[10] = fftAddAvg(44,56); // 12 1895 - 2412 midrange + high mid - fftCalc[11] = fftAddAvg(56,70); // 14 2412 - 3015 high mid - fftCalc[12] = fftAddAvg(70,86); // 16 3015 - 3704 high mid - fftCalc[13] = fftAddAvg(86,104); // 18 3704 - 4479 high mid - fftCalc[14] = fftAddAvg(104,165) * 0.88f; // 61 4479 - 7106 high mid + high -- with slight damping + fftCalc[ 4] = fftAddAvg(7,9); // 3 301 - 430 midrange + fftCalc[ 5] = fftAddAvg(10,12); // 3 430 - 560 midrange + fftCalc[ 6] = fftAddAvg(13,18); // 5 560 - 818 midrange + fftCalc[ 7] = fftAddAvg(19,25); // 7 818 - 1120 midrange -- 1Khz should always be the center ! + fftCalc[ 8] = fftAddAvg(26,32); // 7 1120 - 1421 midrange + fftCalc[ 9] = fftAddAvg(33,43); // 9 1421 - 1895 midrange + fftCalc[10] = fftAddAvg(44,55); // 12 1895 - 2412 midrange + high mid + fftCalc[11] = fftAddAvg(56,69); // 14 2412 - 3015 high mid + fftCalc[12] = fftAddAvg(70,85); // 16 3015 - 3704 high mid + fftCalc[13] = fftAddAvg(86,103); // 18 3704 - 4479 high mid + fftCalc[14] = fftAddAvg(104,164) * 0.88f; // 61 4479 - 7106 high mid + high -- with slight damping + } + else if (freqDist == 1) { //WLEDMM: Rightshft: note ewowi: frequencies in comments are not correct + if (useInputFilter==1) { + // skip frequencies below 100hz + fftCalc[ 0] = 0.8f * fftAddAvg(1,1); + fftCalc[ 1] = 0.9f * fftAddAvg(2,2); + fftCalc[ 2] = fftAddAvg(3,3); + fftCalc[ 3] = fftAddAvg(4,4); + // don't use the last bins from 206 to 255. + fftCalc[15] = fftAddAvg(165,205) * 0.75f; // 40 7106 - 8828 high -- with some damping + } else { + fftCalc[ 0] = fftAddAvg(1,1); // 1 43 - 86 sub-bass + fftCalc[ 1] = fftAddAvg(2,2); // 1 86 - 129 bass + fftCalc[ 2] = fftAddAvg(3,3); // 2 129 - 216 bass + fftCalc[ 3] = fftAddAvg(4,4); // 2 216 - 301 bass + midrange + // don't use the last bins from 216 to 255. They are usually contaminated by aliasing (aka noise) + fftCalc[15] = fftAddAvg(165,215) * 0.70f; // 50 7106 - 9259 high -- with some damping + } + fftCalc[ 4] = fftAddAvg(5,6); // 3 301 - 430 midrange + fftCalc[ 5] = fftAddAvg(7,8); // 3 430 - 560 midrange + fftCalc[ 6] = fftAddAvg(9,10); // 5 560 - 818 midrange + fftCalc[ 7] = fftAddAvg(11,13); // 7 818 - 1120 midrange -- 1Khz should always be the center ! + fftCalc[ 8] = fftAddAvg(14,18); // 7 1120 - 1421 midrange + fftCalc[ 9] = fftAddAvg(19,25); // 9 1421 - 1895 midrange + fftCalc[10] = fftAddAvg(26,36); // 12 1895 - 2412 midrange + high mid + fftCalc[11] = fftAddAvg(37,45); // 14 2412 - 3015 high mid + fftCalc[12] = fftAddAvg(46,66); // 16 3015 - 3704 high mid + fftCalc[13] = fftAddAvg(67,97); // 18 3704 - 4479 high mid + fftCalc[14] = fftAddAvg(98,164) * 0.88f; // 61 4479 - 7106 high mid + high -- with slight damping + } #endif } else { // noise gate closed - just decay old values isFirstRun = false; @@ -849,6 +920,8 @@ class AudioReactive : public Usermod { // variables used in effects int16_t volumeRaw = 0; // either sampleRaw or rawSampleAgc depending on soundAgc float my_magnitude =0.0f; // FFT_Magnitude, scaled by multAgc + float agcSensitivity = 128; // AGC sensitivity estimation, based on agc gain (multAgc). calculated by getSensitivity(). range 0..255 + float soundPressure = 0; // Sound Pressure estimation, based on microphone raw readings. 0 ->5db, 255 ->105db // used to feed "Info" Page unsigned long last_UDPTime = 0; // time of last valid UDP sound sync datapacket @@ -861,7 +934,9 @@ class AudioReactive : public Usermod { static const char _name[]; static const char _enabled[]; static const char _inputLvl[]; +#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) static const char _analogmic[]; +#endif static const char _digitalmic[]; static const char UDP_SYNC_HEADER[]; static const char UDP_SYNC_HEADER_v1[]; @@ -1057,6 +1132,10 @@ class AudioReactive : public Usermod { const float weighting = 0.2f; // Exponential filter weighting. Will be adjustable in a future release. const float weighting2 = 0.073f; // Exponential filter weighting, for rising signal (a bit more robust against spikes) const int AGC_preset = (soundAgc > 0)? (soundAgc-1): 0; // make sure the _compiler_ knows this value will not change while we are inside the function + static bool isFrozen = false; + static bool haveSilence = true; + static unsigned long lastSoundTime = 0; // for delaying un-freeze + static unsigned long startuptime = 0; // "fast freeze" mode: do not interfere during first 12 seconds (filter startup time) #ifdef WLED_DISABLE_SOUND micIn = inoise8(millis(), millis()); // Simulated analog read @@ -1079,8 +1158,15 @@ class AudioReactive : public Usermod { #endif #endif - micLev += (micDataReal-micLev) / 12288.0f; - if(micIn < micLev) micLev = ((micLev * 31.0f) + micDataReal) / 32.0f; // align MicLev to lowest input signal + if (startuptime == 0) startuptime = millis(); // fast freeze mode - remember filter startup time + if ((micLevelMethod < 1) || !isFrozen) { // following the input level, UNLESS mic Level was frozen + micLev += (micDataReal-micLev) / 12288.0f; + } + + if(micDataReal < (micLev-0.24)) { // MicLev above input signal: + micLev = ((micLev * 31.0f) + micDataReal) / 32.0f; // always align MicLev to lowest input signal + if (!haveSilence) isFrozen = true; // freeze mode: freeze micLevel so it cannot rise again + } micIn -= micLev; // Let's center it to 0 now // Using an exponential filter to smooth out the signal. We'll add controls for this in a future release. @@ -1093,10 +1179,26 @@ class AudioReactive : public Usermod { expAdjF = fabsf(expAdjF); // Now (!) take the absolute value + if ((micLevelMethod == 2) && !haveSilence && (expAdjF >= (1.5f * float(soundSquelch)))) + isFrozen = true; // fast freeze mode: freeze micLevel once the volume rises 50% above squelch + //expAdjF = (micInNoDC <= soundSquelch) ? 0: expAdjF; // simple noise gate - experimental expAdjF = (expAdjF <= soundSquelch) ? 0: expAdjF; // simple noise gate if ((soundSquelch == 0) && (expAdjF < 0.25f)) expAdjF = 0; // do something meaningfull when "squelch = 0" + if (expAdjF <= 0.5f) + haveSilence = true; + else { + lastSoundTime = millis(); + haveSilence = false; + } + + // un-freeze micLev + if (micLevelMethod == 0) isFrozen = false; + if ((micLevelMethod == 1) && isFrozen && haveSilence && ((millis() - lastSoundTime) > 4000)) isFrozen = false; // normal freeze: 4 seconds silence needed + if ((micLevelMethod == 2) && isFrozen && haveSilence && ((millis() - lastSoundTime) > 6000)) isFrozen = false; // fast freeze: 6 seconds silence needed + if ((micLevelMethod == 2) && (millis() - startuptime < 12000)) isFrozen = false; // fast freeze: no freeze in first 12 seconds (filter startup phase) + tmpSample = expAdjF; micIn = abs(micIn); // And get the absolute value of each sample @@ -1130,6 +1232,60 @@ class AudioReactive : public Usermod { } // getSample() + // current sensitivity, based on AGC gain (multAgc) + float getSensitivity() + { + // start with AGC gain factor + float tmpSound = multAgc; + // experimental: this gives you a calculated "real gain" + // if ((sampleAvg> 1.0) && (sampleReal > 0.05)) tmpSound = (float)sampleRaw / sampleReal; // calculate gain from sampleReal + // else tmpSound = ((float)sampleGain/40.0f * (float)inputLevel/128.0f) + 1.0f/16.0f; // silence --> use values from user settings + + if (soundAgc == 0) + tmpSound = ((float)sampleGain/40.0f * (float)inputLevel/128.0f) + 1.0f/16.0f; // AGC off -> use non-AGC gain from presets + else + tmpSound /= (float)sampleGain/40.0f + 1.0f/16.0f; // AGC ON -> scale value so 1 = middle value + + // scale to 0..255. Actually I'm not absolutely happy with this, but it works + if (tmpSound > 1.0) tmpSound = sqrtf(tmpSound); + if (tmpSound > 1.25) tmpSound = ((tmpSound-1.25f)/3.42f) +1.25f; + // we have a value now that should be between 0 and 4 (representing gain 1/16 ... 16.0) + return fminf(fmaxf(128.0*tmpSound -6.0f, 0), 255.0); // return scaled non-inverted value // "-6" to ignore values below 1/24 + } + + // estimate sound pressure, based on some assumptions : + // * sample max = 32676 -> Acoustic overload point --> 105db ==> 255 + // * sample < squelch -> just above hearing level --> 5db ==> 0 + // see https://en.wikipedia.org/wiki/Sound_pressure#Examples_of_sound_pressure + // use with I2S digital microphones. Expect stupid values for analog in, and with Line-In !! + float estimatePressure() { + // some constants + constexpr float logMinSample = 0.8329091229351f; // ln(2.3) + constexpr float sampleMin = 2.3f; + constexpr float logMaxSample = 10.1895683436f; // ln(32767 - 6144) + constexpr float sampleMax = 32767.0f - 6144.0f; + + // take the max sample from last I2S batch. + float micSampleMax = fabsf(sampleReal); // from getSample() - nice results, however a bit distorted by MicLev processing + //float micSampleMax = fabsf(micDataReal); // from FFTCode() - better source, but more flickering + if (dmType == 0) micSampleMax *= 2.0f; // correction for ADC analog + //if (dmType == 4) micSampleMax *= 16.0f; // correction for I2S Line-In + if (dmType == 5) micSampleMax *= 2.0f; // correction for PDM + if (dmType == 4) { // I2S Line-In. This is a dirty trick to make sound pressure look interesting for line-in (which doesn't have "sound pressure" as its not a microphone) + micSampleMax /= 11.0f; // reduce to max 128 + micSampleMax *= micSampleMax; // blow up --> max 16000 + } + // make sure we are in expected ranges + if(micSampleMax <= sampleMin) return 0.0f; + if(micSampleMax >= sampleMax) return 255.0f; + + // apply logarithmic scaling + float scaledvalue = logf(micSampleMax); + scaledvalue = (scaledvalue - logMinSample) / (logMaxSample - logMinSample); // 0...1 + return fminf(fmaxf(256.0*scaledvalue, 0), 255.0); // scaled value + } + + /* Limits the dynamics of volumeSmth (= sampleAvg or sampleAgc). * does not affect FFTResult[] or volumeRaw ( = sample or rawSampleAgc) */ @@ -1324,12 +1480,12 @@ class AudioReactive : public Usermod { // usermod exchangeable data // we will assign all usermod exportable data here as pointers to original variables or arrays and allocate memory for pointers um_data = new um_data_t; - um_data->u_size = 8; + um_data->u_size = 11; um_data->u_type = new um_types_t[um_data->u_size]; um_data->u_data = new void*[um_data->u_size]; um_data->u_data[0] = &volumeSmth; //*used (New) um_data->u_type[0] = UMT_FLOAT; - um_data->u_data[1] = &volumeRaw; // used (New) + um_data->u_data[1] = &volumeRaw; // used (New) um_data->u_type[1] = UMT_UINT16; um_data->u_data[2] = fftResult; //*used (Blurz, DJ Light, Noisemove, GEQ_base, 2D Funky Plank, Akemi) um_data->u_type[2] = UMT_BYTE_ARR; @@ -1337,12 +1493,18 @@ class AudioReactive : public Usermod { um_data->u_type[3] = UMT_BYTE; um_data->u_data[4] = &FFT_MajorPeak; //*used (Ripplepeak, Freqmap, Freqmatrix, Freqpixels, Freqwave, Gravfreq, Rocktaves, Waterfall) um_data->u_type[4] = UMT_FLOAT; - um_data->u_data[5] = &my_magnitude; // used (New) + um_data->u_data[5] = &my_magnitude; // used (New) um_data->u_type[5] = UMT_FLOAT; um_data->u_data[6] = &maxVol; // assigned in effect function from UI element!!! (Puddlepeak, Ripplepeak, Waterfall) um_data->u_type[6] = UMT_BYTE; um_data->u_data[7] = &binNum; // assigned in effect function from UI element!!! (Puddlepeak, Ripplepeak, Waterfall) um_data->u_type[7] = UMT_BYTE; + um_data->u_data[8] = &FFT_MajPeakSmth; // new + um_data->u_type[8] = UMT_FLOAT; + um_data->u_data[9] = &soundPressure; // used (New) + um_data->u_type[9] = UMT_FLOAT; + um_data->u_data[10] = &agcSensitivity; // used (New) + um_data->u_type[10] = UMT_FLOAT; } // Reset I2S peripheral for good measure @@ -1353,13 +1515,14 @@ class AudioReactive : public Usermod { #endif delay(100); // Give that poor microphone some time to setup. - useBandPassFilter = false; + useInputFilter = 2; // default: DC blocker switch (dmType) { #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) // stub cases for not-yet-supported I2S modes on other ESP32 chips case 0: //ADC analog #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) case 5: //PDM Microphone + case 51: //legacy PDM Microphone #endif #endif case 1: @@ -1370,7 +1533,7 @@ class AudioReactive : public Usermod { break; case 2: DEBUGSR_PRINTLN(F("AR: ES7243 Microphone (right channel only).")); - //useBandPassFilter = true; + //useInputFilter = 0; // in case you need to disable low-cut software filtering audioSource = new ES7243(SAMPLE_RATE, BLOCK_SIZE); delay(100); if (audioSource) audioSource->initialize(sdaPin, sclPin, i2swsPin, i2ssdPin, i2sckPin, mclkPin); @@ -1383,7 +1546,6 @@ class AudioReactive : public Usermod { break; case 4: DEBUGSR_PRINT(F("AR: Generic I2S Microphone with Master Clock - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT)); - //useBandPassFilter = true; audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE, 1.0f/24.0f); //audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE, 1.0f/24.0f, false); // I2S SLAVE mode - does not work, unfortunately delay(100); @@ -1393,17 +1555,32 @@ class AudioReactive : public Usermod { case 5: DEBUGSR_PRINT(F("AR: I2S PDM Microphone - ")); DEBUGSR_PRINTLN(F(I2S_PDM_MIC_CHANNEL_TEXT)); audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE, 1.0f/4.0f); - useBandPassFilter = true; // this reduces the noise floor on SPM1423 from 5% Vpp (~380) down to 0.05% Vpp (~5) + useInputFilter = 1; // PDM bandpass filter - this reduces the noise floor on SPM1423 from 5% Vpp (~380) down to 0.05% Vpp (~5) + delay(100); + if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin); + break; + case 51: + DEBUGSR_PRINT(F("AR: Legacy PDM Microphone - ")); DEBUGSR_PRINTLN(F(I2S_PDM_MIC_CHANNEL_TEXT)); + audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE, 1.0f); + useInputFilter = 1; // PDM bandpass filter delay(100); if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin); break; #endif + case 6: + DEBUGSR_PRINTLN(F("AR: ES8388 Source")); + audioSource = new ES8388Source(SAMPLE_RATE, BLOCK_SIZE, 1.0f); + //useInputFilter = 0; // to disable low-cut software filtering and restore previous behaviour + delay(100); + if (audioSource) audioSource->initialize(sdaPin, sclPin, i2swsPin, i2ssdPin, i2sckPin, mclkPin); + break; + #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) // ADC over I2S is only possible on "classic" ESP32 case 0: default: DEBUGSR_PRINTLN(F("AR: Analog Microphone (left channel only).")); - useBandPassFilter = true; + useInputFilter = 1; // PDM bandpass filter seems to work well for analog, too audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE); delay(100); if (audioSource) audioSource->initialize(audioPin); @@ -1530,7 +1707,7 @@ class AudioReactive : public Usermod { #if defined(WLED_DEBUG) || defined(SR_DEBUG) || defined(SR_STATS) // complain when audio userloop has been delayed for long time. Currently we need userloop running between 500 and 1500 times per second. - if ((userloopDelay > /*23*/ 30) && !disableSoundProcessing && (audioSyncEnabled == 0)) { + if ((userloopDelay > /*23*/ 65) && !disableSoundProcessing && (audioSyncEnabled == 0)) { USER_PRINTF("[AR userLoop] hickup detected -> was inactive for last %d millis!\n", userloopDelay); } #endif @@ -1553,6 +1730,16 @@ class AudioReactive : public Usermod { if (soundAgc) my_magnitude *= multAgc; if (volumeSmth < 1 ) my_magnitude = 0.001f; // noise gate closed - mute + // get AGC sensitivity and sound pressure + static unsigned long lastEstimate = 0; + if (millis() - lastEstimate > 12) { + lastEstimate = millis(); + agcSensitivity = getSensitivity(); + if (limiterOn) + soundPressure = soundPressure + 0.38f * (estimatePressure() - soundPressure); // dynamics limiter on -> some smoothing + else + soundPressure = soundPressure + 0.95f * (estimatePressure() - soundPressure); // dynamics limiter on -> raw value + } limitSampleDynamics(); } // if (!disableSoundProcessing) @@ -1666,14 +1853,15 @@ class AudioReactive : public Usermod { connected(); // resume UDP } else // xTaskCreatePinnedToCore( - xTaskCreate( // no need to "pin" this task to core #0 +// xTaskCreate( // no need to "pin" this task to core #0 + xTaskCreateUniversal( FFTcode, // Function to implement the task "FFT", // Name of the task 5000, // Stack size in words NULL, // Task input parameter 1, // Priority of the task &FFT_Task // Task handle -// , 0 // Core where the task should run + , 0 // Core where the task should run ); } micDataReal = 0.0f; // just to be sure @@ -1773,7 +1961,10 @@ class AudioReactive : public Usermod { if (audioSource->getType() == AudioSource::Type_I2SAdc) { infoArr.add(F("ADC analog")); } else { - infoArr.add(F("I2S digital")); + if (dmType != 51) + infoArr.add(F("I2S digital")); + else + infoArr.add(F("legacy I2S PDM")); } // input level or "silence" if (maxSample5sec > 1.0) { @@ -1786,7 +1977,7 @@ class AudioReactive : public Usermod { } else { // error during audio source setup infoArr.add(F("not initialized")); - infoArr.add(F(" - check GPIO config")); + infoArr.add(F(" - check pin settings")); } } @@ -1950,6 +2141,12 @@ class AudioReactive : public Usermod { cfg[F("gain")] = sampleGain; cfg[F("AGC")] = soundAgc; + //WLEDMM: experimental settings + JsonObject poweruser = top.createNestedObject("experiments"); + poweruser[F("micLev")] = micLevelMethod; + poweruser[F("freqDist")] = freqDist; + poweruser[F("freqRMS")] = averageByRMS; + JsonObject dynLim = top.createNestedObject("dynamics"); dynLim[F("limiter")] = limiterOn; dynLim[F("rise")] = attackTime; @@ -1998,7 +2195,11 @@ class AudioReactive : public Usermod { if (dmType == 0) dmType = SR_DMTYPE; // MCU does not support analog #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) if (dmType == 5) dmType = SR_DMTYPE; // MCU does not support PDM + if (dmType == 51) dmType = SR_DMTYPE; // MCU does not support legacy PDM #endif + #else + if (dmType == 5) useInputFilter = 1; // enable filter for PDM + if (dmType == 51) useInputFilter = 1; // switch on filter for legacy PDM #endif configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][0], i2ssdPin); @@ -2012,6 +2213,11 @@ class AudioReactive : public Usermod { configComplete &= getJsonValue(top["config"][F("gain")], sampleGain); configComplete &= getJsonValue(top["config"][F("AGC")], soundAgc); + //WLEDMM: experimental settings + configComplete &= getJsonValue(top["experiments"][F("micLev")], micLevelMethod); + configComplete &= getJsonValue(top["experiments"][F("freqDist")], freqDist); + configComplete &= getJsonValue(top["experiments"][F("freqRMS")], averageByRMS); + configComplete &= getJsonValue(top["dynamics"][F("limiter")], limiterOn); configComplete &= getJsonValue(top["dynamics"][F("rise")], attackTime); configComplete &= getJsonValue(top["dynamics"][F("fall")], decayTime); @@ -2031,52 +2237,65 @@ class AudioReactive : public Usermod { oappend(SET_F("addInfo('AudioReactive:help',0,'');")); //WLEDMM: add defaults - #ifdef AUDIOPIN - oappend(SET_F("xOption('AudioReactive:analogmic:pin',1,' ⎌',")); oappendi(AUDIOPIN); oappend(");"); - #endif + #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) // -S3/-S2/-C3 don't support analog audio + #ifdef AUDIOPIN + oappend(SET_F("xOpt('AudioReactive:analogmic:pin',1,' ⎌',")); oappendi(AUDIOPIN); oappend(");"); + #endif + oappend(SET_F("aOpt('AudioReactive:analogmic:pin',1);")); //only analog options + #endif oappend(SET_F("dd=addDropdown('AudioReactive','digitalmic:type');")); - #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) - #if SR_DMTYPE==0 - oappend(SET_F("addOption(dd,'Generic Analog (⎌)',0);")); - #else - oappend(SET_F("addOption(dd,'Generic Analog',0);")); - #endif - #endif - #if SR_DMTYPE==1 - oappend(SET_F("addOption(dd,'Generic I2S (⎌)',1);")); - #else - oappend(SET_F("addOption(dd,'Generic I2S',1);")); - #endif - #if SR_DMTYPE==2 - oappend(SET_F("addOption(dd,'ES7243 (⎌)',2);")); - #else - oappend(SET_F("addOption(dd,'ES7243',2);")); - #endif - #if SR_DMTYPE==3 - oappend(SET_F("addOption(dd,'SPH0654 (⎌)',3);")); - #else - oappend(SET_F("addOption(dd,'SPH0654',3);")); - #endif - #if SR_DMTYPE==4 - oappend(SET_F("addOption(dd,'Generic I2S with Mclk (⎌)',4);")); - #else - oappend(SET_F("addOption(dd,'Generic I2S with Mclk',4);")); - #endif - #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) - #if SR_DMTYPE==5 - oappend(SET_F("addOption(dd,'Generic I2S PDM (⎌)',5);")); - #else - oappend(SET_F("addOption(dd,'Generic I2S PDM',5);")); - #endif - #endif + #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) + #if SR_DMTYPE==0 + oappend(SET_F("addOption(dd,'Generic Analog (⎌)',0);")); + #else + oappend(SET_F("addOption(dd,'Generic Analog',0);")); + #endif + #endif + #if SR_DMTYPE==1 + oappend(SET_F("addOption(dd,'Generic I2S (⎌)',1);")); + #else + oappend(SET_F("addOption(dd,'Generic I2S',1);")); + #endif + #if SR_DMTYPE==2 + oappend(SET_F("addOption(dd,'ES7243 (⎌)',2);")); + #else + oappend(SET_F("addOption(dd,'ES7243',2);")); + #endif + #if SR_DMTYPE==3 + oappend(SET_F("addOption(dd,'SPH0654 (⎌)',3);")); + #else + oappend(SET_F("addOption(dd,'SPH0654',3);")); + #endif + #if SR_DMTYPE==4 + oappend(SET_F("addOption(dd,'Generic I2S with Mclk (⎌)',4);")); + #else + oappend(SET_F("addOption(dd,'Generic I2S with Mclk',4);")); + #endif + #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) + #if SR_DMTYPE==5 + oappend(SET_F("addOption(dd,'Generic I2S PDM (⎌)',5);")); + #else + oappend(SET_F("addOption(dd,'Generic I2S PDM',5);")); + #endif + #if SR_DMTYPE==51 + oappend(SET_F("addOption(dd,'.Legacy I2S PDM ☾ (⎌)',51);")); + #else + oappend(SET_F("addOption(dd,'.Legacy I2S PDM ☾',51);")); + #endif + #endif + #if SR_DMTYPE==6 + oappend(SET_F("addOption(dd,'ES8388 ☾ (⎌)',6);")); + #else + oappend(SET_F("addOption(dd,'ES8388 ☾',6);")); + #endif - #ifdef SR_SQUELCH - oappend(SET_F("addInfo('AudioReactive:config:squelch',1,'⎌ ")); oappendi(SR_SQUELCH); oappend("');"); // 0 is field type, 1 is actual field - #endif - #ifdef SR_GAIN - oappend(SET_F("addInfo('AudioReactive:config:gain',1,'⎌ ")); oappendi(SR_GAIN); oappend("');"); // 0 is field type, 1 is actual field - #endif + #ifdef SR_SQUELCH + oappend(SET_F("addInfo('AudioReactive:config:squelch',1,'⎌ ")); oappendi(SR_SQUELCH); oappend("');"); // 0 is field type, 1 is actual field + #endif + #ifdef SR_GAIN + oappend(SET_F("addInfo('AudioReactive:config:gain',1,'⎌ ")); oappendi(SR_GAIN); oappend("');"); // 0 is field type, 1 is actual field + #endif oappend(SET_F("dd=addDropdown('AudioReactive','config:AGC');")); oappend(SET_F("addOption(dd,'Off',0);")); @@ -2084,6 +2303,23 @@ class AudioReactive : public Usermod { oappend(SET_F("addOption(dd,'Vivid',2);")); oappend(SET_F("addOption(dd,'Lazy',3);")); + //WLEDMM: experimental settings + oappend(SET_F("dd=addDropdown('AudioReactive','experiments:micLev');")); + oappend(SET_F("addOption(dd,'Floating (⎌)',0);")); + oappend(SET_F("addOption(dd,'Freeze',1);")); + oappend(SET_F("addOption(dd,'Fast Freeze',2);")); + oappend(SET_F("addInfo('AudioReactive:experiments:micLev',1,'☾');")); + + oappend(SET_F("dd=addDropdown('AudioReactive','experiments:freqDist');")); + oappend(SET_F("addOption(dd,'Normal (⎌)',0);")); + oappend(SET_F("addOption(dd,'RightShift',1);")); + oappend(SET_F("addInfo('AudioReactive:experiments:freqDist',1,'☾');")); + + oappend(SET_F("dd=addDropdown('AudioReactive','experiments:freqRMS');")); + oappend(SET_F("addOption(dd,'Off (⎌)',0);")); + oappend(SET_F("addOption(dd,'On',1);")); + oappend(SET_F("addInfo('AudioReactive:experiments:freqRMS',1,'☾');")); + oappend(SET_F("dd=addDropdown('AudioReactive','dynamics:limiter');")); oappend(SET_F("addOption(dd,'Off',0);")); oappend(SET_F("addOption(dd,'On',1);")); @@ -2099,92 +2335,108 @@ class AudioReactive : public Usermod { //WLEDMM add defaults oappend(SET_F("dd=addDropdown('AudioReactive','frequency:profile');")); - #if SR_FREQ_PROF==0 - oappend(SET_F("addOption(dd,'Generic Microphone (⎌)',0);")); - #else - oappend(SET_F("addOption(dd,'Generic Microphone',0);")); - #endif - #if SR_FREQ_PROF==1 - oappend(SET_F("addOption(dd,'Generic Line-In (⎌)',1);")); - #else - oappend(SET_F("addOption(dd,'Generic Line-In',1);")); - #endif - #if SR_FREQ_PROF==5 - oappend(SET_F("addOption(dd,'ICS-43434 (⎌)',5);")); - #else - oappend(SET_F("addOption(dd,'ICS-43434',5);")); - #endif - #if SR_FREQ_PROF==6 - oappend(SET_F("addOption(dd,'ICS-43434 - big speakers (⎌)',6);")); - #else - oappend(SET_F("addOption(dd,'ICS-43434 - big speakers',6);")); - #endif - #if SR_FREQ_PROF==7 - oappend(SET_F("addOption(dd,'SPM1423 (⎌)',7);")); - #else - oappend(SET_F("addOption(dd,'SPM1423',7);")); - #endif - #if SR_FREQ_PROF==2 - oappend(SET_F("addOption(dd,'IMNP441 (⎌)',2);")); - #else - oappend(SET_F("addOption(dd,'IMNP441',2);")); - #endif - #if SR_FREQ_PROF==3 - oappend(SET_F("addOption(dd,'IMNP441 - big speakers (⎌)',3);")); - #else - oappend(SET_F("addOption(dd,'IMNP441 - big speakers',3);")); - #endif - #if SR_FREQ_PROF==4 - oappend(SET_F("addOption(dd,'IMNP441 - small speakers (⎌)',4);")); - #else - oappend(SET_F("addOption(dd,'IMNP441 - small speakers',4);")); - #endif - #if SR_FREQ_PROF==10 - oappend(SET_F("addOption(dd,'flat - no adjustments (⎌)',10);")); - #else - oappend(SET_F("addOption(dd,'flat - no adjustments',10);")); - #endif - #if SR_FREQ_PROF==8 - oappend(SET_F("addOption(dd,'userdefined #1 (⎌)',8);")); - #else - oappend(SET_F("addOption(dd,'userdefined #1',8);")); - #endif - #if SR_FREQ_PROF==9 - oappend(SET_F("addOption(dd,'userdefined #2 (⎌)',9);")); - #else - oappend(SET_F("addOption(dd,'userdefined #2',9);")); - #endif + #if SR_FREQ_PROF==0 + oappend(SET_F("addOption(dd,'Generic Microphone (⎌)',0);")); + #else + oappend(SET_F("addOption(dd,'Generic Microphone',0);")); + #endif + #if SR_FREQ_PROF==1 + oappend(SET_F("addOption(dd,'Generic Line-In (⎌)',1);")); + #else + oappend(SET_F("addOption(dd,'Generic Line-In',1);")); + #endif + #if SR_FREQ_PROF==5 + oappend(SET_F("addOption(dd,'ICS-43434 (⎌)',5);")); + #else + oappend(SET_F("addOption(dd,'ICS-43434',5);")); + #endif + #if SR_FREQ_PROF==6 + oappend(SET_F("addOption(dd,'ICS-43434 - big speakers (⎌)',6);")); + #else + oappend(SET_F("addOption(dd,'ICS-43434 - big speakers',6);")); + #endif + #if SR_FREQ_PROF==7 + oappend(SET_F("addOption(dd,'SPM1423 (⎌)',7);")); + #else + oappend(SET_F("addOption(dd,'SPM1423',7);")); + #endif + #if SR_FREQ_PROF==2 + oappend(SET_F("addOption(dd,'IMNP441 (⎌)',2);")); + #else + oappend(SET_F("addOption(dd,'IMNP441',2);")); + #endif + #if SR_FREQ_PROF==3 + oappend(SET_F("addOption(dd,'IMNP441 - big speakers (⎌)',3);")); + #else + oappend(SET_F("addOption(dd,'IMNP441 - big speakers',3);")); + #endif + #if SR_FREQ_PROF==4 + oappend(SET_F("addOption(dd,'IMNP441 - small speakers (⎌)',4);")); + #else + oappend(SET_F("addOption(dd,'IMNP441 - small speakers',4);")); + #endif + #if SR_FREQ_PROF==10 + oappend(SET_F("addOption(dd,'flat - no adjustments (⎌)',10);")); + #else + oappend(SET_F("addOption(dd,'flat - no adjustments',10);")); + #endif + #if SR_FREQ_PROF==8 + oappend(SET_F("addOption(dd,'userdefined #1 (⎌)',8);")); + #else + oappend(SET_F("addOption(dd,'userdefined #1',8);")); + #endif + #if SR_FREQ_PROF==9 + oappend(SET_F("addOption(dd,'userdefined #2 (⎌)',9);")); + #else + oappend(SET_F("addOption(dd,'userdefined #2',9);")); + #endif + oappend(SET_F("addInfo('AudioReactive:frequency:profile',1,'☾');")); oappend(SET_F("dd=addDropdown('AudioReactive','sync:mode');")); oappend(SET_F("addOption(dd,'Off',0);")); oappend(SET_F("addOption(dd,'Send',1);")); oappend(SET_F("addOption(dd,'Receive',2);")); oappend(SET_F("addInfo('AudioReactive:digitalmic:type',1,'requires reboot!');")); // 0 is field type, 1 is actual field + oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',0,'sd/data/dout','I2S SD');")); - #ifdef I2S_SDPIN - oappend(SET_F("xOption('AudioReactive:digitalmic:pin[]',0,' ⎌',")); oappendi(I2S_SDPIN); oappend(");"); - #endif + #ifdef I2S_SDPIN + oappend(SET_F("xOpt('AudioReactive:digitalmic:pin[]',0,' ⎌',")); oappendi(I2S_SDPIN); oappend(");"); + #endif + oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',1,'ws/clk/lrck','I2S WS');")); - oappend(SET_F("disableRO('AudioReactive:digitalmic:pin[]',1);")); - #ifdef I2S_WSPIN - oappend(SET_F("xOption('AudioReactive:digitalmic:pin[]',1,' ⎌',")); oappendi(I2S_WSPIN); oappend(");"); - #endif + oappend(SET_F("dRO('AudioReactive:digitalmic:pin[]',1);")); // disable read only pins + #ifdef I2S_WSPIN + oappend(SET_F("xOpt('AudioReactive:digitalmic:pin[]',1,' ⎌',")); oappendi(I2S_WSPIN); oappend(");"); + #endif + oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',2,'sck/bclk','I2S SCK');")); - oappend(SET_F("disableRO('AudioReactive:digitalmic:pin[]',2);")); - #ifdef I2S_CKPIN - oappend(SET_F("xOption('AudioReactive:digitalmic:pin[]',2,' ⎌',")); oappendi(I2S_CKPIN); oappend(");"); - #endif + oappend(SET_F("dRO('AudioReactive:digitalmic:pin[]',2);")); // disable read only pins + #ifdef I2S_CKPIN + oappend(SET_F("xOpt('AudioReactive:digitalmic:pin[]',2,' ⎌',")); oappendi(I2S_CKPIN); oappend(");"); + #endif + oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',3,'master clock','I2S MCLK');")); - oappend(SET_F("disableRO('AudioReactive:digitalmic:pin[]',3);")); - #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) - oappend(SET_F("dOptions('AudioReactive:digitalmic:pin[]',3,2,2);")); //only use -1, 0, 1 or 3 - oappend(SET_F("dOptions('AudioReactive:digitalmic:pin[]',3,4,39);")); //only use -1, 0, 1 or 3 - #endif + oappend(SET_F("dRO('AudioReactive:digitalmic:pin[]',3);")); // disable read only pins + #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) + oappend(SET_F("dOpt('AudioReactive:digitalmic:pin[]',3,2,2);")); //only use -1, 0, 1 or 3 + oappend(SET_F("dOpt('AudioReactive:digitalmic:pin[]',3,4,39);")); //only use -1, 0, 1 or 3 + #endif + #ifdef MCLK_PIN + oappend(SET_F("xOpt('AudioReactive:digitalmic:pin[]',3,' ⎌',")); oappendi(MCLK_PIN); oappend(");"); + #endif + oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',4,'','I2C SDA');")); - oappend(SET_F("rOption('AudioReactive:digitalmic:pin[]',4,'use global (")); oappendi(i2c_sda); oappend(")',-1);"); + oappend(SET_F("rOpt('AudioReactive:digitalmic:pin[]',4,'use global (")); oappendi(i2c_sda); oappend(")',-1);"); + #ifdef ES7243_SDAPIN + oappend(SET_F("xOpt('AudioReactive:digitalmic:pin[]',4,' ⎌',")); oappendi(ES7243_SDAPIN); oappend(");"); + #endif + oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',5,'','I2C SCL');")); - oappend(SET_F("rOption('AudioReactive:digitalmic:pin[]',5,'use global (")); oappendi(i2c_scl); oappend(")',-1);"); - oappend(SET_F("disableRO('AudioReactive:digitalmic:pin[]',5);")); + oappend(SET_F("rOpt('AudioReactive:digitalmic:pin[]',5,'use global (")); oappendi(i2c_scl); oappend(")',-1);"); + #ifdef ES7243_SCLPIN + oappend(SET_F("xOpt('AudioReactive:digitalmic:pin[]',5,' ⎌',")); oappendi(ES7243_SCLPIN); oappend(");"); + #endif + oappend(SET_F("dRO('AudioReactive:digitalmic:pin[]',5);")); // disable read only pins } @@ -2213,7 +2465,9 @@ class AudioReactive : public Usermod { const char AudioReactive::_name[] PROGMEM = "AudioReactive"; const char AudioReactive::_enabled[] PROGMEM = "enabled"; const char AudioReactive::_inputLvl[] PROGMEM = "inputLevel"; +#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) const char AudioReactive::_analogmic[] PROGMEM = "analogmic"; +#endif const char AudioReactive::_digitalmic[] PROGMEM = "digitalmic"; const char AudioReactive::UDP_SYNC_HEADER[] PROGMEM = "00002"; // new sync header version, as format no longer compatible with previous structure const char AudioReactive::UDP_SYNC_HEADER_v1[] PROGMEM = "00001"; // old sync header version - need to add backwards-compatibility feature diff --git a/usermods/audioreactive/audio_source.h b/usermods/audioreactive/audio_source.h index 276711de..088574d3 100644 --- a/usermods/audioreactive/audio_source.h +++ b/usermods/audioreactive/audio_source.h @@ -489,6 +489,106 @@ public: int8_t pin_ES7243_SCL; }; +/* ES8388 Sound Modude + This is an I2S sound processing unit that requires ininitialization over + I2C before I2S data can be received. +*/ +class ES8388Source : public I2SSource { + private: + // I2C initialization functions for ES8388 + void _es8388I2cBegin() { + bool i2c_initialized = Wire.begin(pin_ES8388_SDA, pin_ES8388_SCL, 100000U); + if (i2c_initialized == false) { + ERRORSR_PRINTLN(F("AR: ES8388 failed to initialize I2C bus driver.")); + } + } + + void _es8388I2cWrite(uint8_t reg, uint8_t val) { +#ifndef ES8388_ADDR + Wire.beginTransmission(0x10); + #define ES8388_ADDR 0x10 // default address +#else + Wire.beginTransmission(ES8388_ADDR); +#endif + Wire.write((uint8_t)reg); + Wire.write((uint8_t)val); + uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK + if (i2cErr != 0) { + DEBUGSR_PRINTF("AR: ES8388 I2C write failed with error=%d (addr=0x%X, reg 0x%X, val 0x%X).\n", i2cErr, ES8388_ADDR, reg, val); + } + } + + void _es8388InitAdc() { + // https://dl.radxa.com/rock2/docs/hw/ds/ES8388%20user%20Guide.pdf Section 10.1 + // https://docs.google.com/spreadsheets/d/1CN3MvhkcPVESuxKyx1xRYqfUit5hOdsG45St9BCUm-g/edit#gid=0 generally + // Sets ADC to around what AudioReactive expects, and loops line-in to line-out/headphone for monitoring. + _es8388I2cBegin(); + _es8388I2cWrite(0x08,0x00); // I2S to slave + _es8388I2cWrite(0x02,0xf3); // Power down DEM and STM + _es8388I2cWrite(0x2b,0x80); // Set same LRCK + _es8388I2cWrite(0x00,0x05); // Set chip to Play & Record Mode + _es8388I2cWrite(0x0d,0x02); // Set MCLK/LRCK ratio to 256 + _es8388I2cWrite(0x01,0x40); // Power up analog and lbias + _es8388I2cWrite(0x03,0x00); // Power up ADC, Analog Input, and Mic Bias + _es8388I2cWrite(0x0a,0x50); // Use Lin2/Rin2 for ADC input ("line-in") + _es8388I2cWrite(0x09,0x00); // Select Analog Input PGA Gain for ADC to 0dB (L+R) + _es8388I2cWrite(0x10,0b01000000); // Set ADC digital volume attenuation to -32dB (left) + _es8388I2cWrite(0x11,0b01000000); // Set ADC digital volume attenuation to -32dB (right) + _es8388I2cWrite(0x04,0x0c); // Turn on LOUT2 and ROUT2 power + _es8388I2cWrite(0x02,0b01000000); // Power up DEM and STM and undocumented bit for "turn on line-out amp" + _es8388I2cWrite(0x26,0x09); // Mixer - route LIN2/RIN2 to output + _es8388I2cWrite(0x27,0b01010000); // Mixer - route LIN to left mixer, 0dB gain + _es8388I2cWrite(0x2a,0b01010000); // Mixer - route RIN to right mixer, 0dB gain + _es8388I2cWrite(0x30,0b00011110); // LOUT2VOL - 0 = -45dB, 0b00011110 = +0dB + _es8388I2cWrite(0x31,0b00011110); // ROUT2VOL - 0 = -45dB, 0b00011110 = +0dB + } + + public: + ES8388Source(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f, bool i2sMaster=true) : + I2SSource(sampleRate, blockSize, sampleScale, i2sMaster) { + _config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT; + }; + + void initialize(int8_t sdaPin, int8_t sclPin, int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) { + + // check that pins are valid + if ((sdaPin < 0) || (sclPin < 0)) { + ERRORSR_PRINTF("\nAR: invalid ES8388 I2C pins: SDA=%d, SCL=%d\n", sdaPin, sclPin); + return; + } + + if ((i2sckPin < 0) || (mclkPin < 0)) { + ERRORSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin); + return; + } + + // Reserve SDA and SCL pins of the I2C interface + PinManagerPinType es8388Pins[2] = { { sdaPin, true }, { sclPin, true } }; + if (!pinManager.allocateMultiplePins(es8388Pins, 2, PinOwner::HW_I2C)) { + pinManager.deallocateMultiplePins(es8388Pins, 2, PinOwner::HW_I2C); + ERRORSR_PRINTF("\nAR: Failed to allocate ES8388 I2C pins: SDA=%d, SCL=%d\n", sdaPin, sclPin); + return; + } + + pin_ES8388_SDA = sdaPin; + pin_ES8388_SCL = sclPin; + + // First route mclk, then configure ADC over I2C, then configure I2S + _es8388InitAdc(); + I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin); + } + + void deinitialize() { + // Release SDA and SCL pins of the I2C interface + PinManagerPinType es8388Pins[2] = { { pin_ES8388_SDA, true }, { pin_ES8388_SCL, true } }; + pinManager.deallocateMultiplePins(es8388Pins, 2, PinOwner::HW_I2C); + I2SSource::deinitialize(); + } + + private: + int8_t pin_ES8388_SDA; + int8_t pin_ES8388_SCL; +}; #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) #if !defined(SOC_I2S_SUPPORTS_ADC) && !defined(SOC_I2S_SUPPORTS_ADC_DAC) diff --git a/usermods/blynk_relay_control/README.md b/usermods/blynk_relay_control/README.md deleted file mode 100644 index f4832c08..00000000 --- a/usermods/blynk_relay_control/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# Blynk controllable relay -Enables controlling a relay state via user variables. Allows the user variables to be set via Blynk. - -Optionally, the servo can have a reset timer to return to its default state after a user definable interval. The interval is set via userVar1. - -## Instalation - -Replace the WLED06_usermod.ino file in Aircoookies WLED folder, with the one here. - -## Customizations - -Update the following parameters in WLED06_usermod.ino to configure the mod's behavior: - -```cpp -//Which pin is the relay connected to -#define RELAY_PIN 5 -//Which pin state should the relay default to -#define RELAY_PIN_DEFAULT LOW -//If >0 The controller returns to RELAY_PIN_DEFAULT after this time, in milliseconds -#define RELAY_PIN_TIMER_DEFAULT 3000 - -//Blynk virtual pin for controlling relay -#define BLYNK_USER_VAR0_PIN V9 -//Blynk virtual pin for controlling relay timer -#define BLYNK_USER_VAR1_PIN V10 -//Number of milliseconds between Blynk updates -#define BLYNK_RELAY_UPDATE_INTERVAL 5000 -``` diff --git a/usermods/blynk_relay_control/wled06_usermod.ino b/usermods/blynk_relay_control/wled06_usermod.ino deleted file mode 100644 index d4028ea5..00000000 --- a/usermods/blynk_relay_control/wled06_usermod.ino +++ /dev/null @@ -1,96 +0,0 @@ -/* - * This file allows you to add own functionality to WLED more easily - * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality - * EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in wled_eeprom.h) - * bytes 2400+ are currently ununsed, but might be used for future wled features - */ - -//Use userVar0 (API calls &U0=, uint16_t) to set relay state -#define relayPinState userVar0 -//Use userVar1 (API calls &U1=, uint16_t) to set relay timer duration -//Ignored if 0, otherwise number of milliseconds to allow relay to stay in -//non default state. -#define relayTimerInterval userVar1 - -//Which pin is the relay connected to -#define RELAY_PIN 5 -//Which pin state should the relay default to -#define RELAY_PIN_DEFAULT LOW -//If >0 The controller returns to RELAY_PIN_DEFAULT after this time in milliseconds -#define RELAY_PIN_TIMER_DEFAULT 3000 - -//Blynk virtual pin for controlling relay -#define BLYNK_USER_VAR0_PIN V9 -//Blynk virtual pin for controlling relay timer -#define BLYNK_USER_VAR1_PIN V10 -//Number of milliseconds between updating blynk -#define BLYNK_RELAY_UPDATE_INTERVAL 5000 - -//Is the timer for resetting the relay active -bool relayTimerStarted = false; -//millis() time after which relay will be reset -unsigned long relayTimeToDefault = 0; -//millis() time after which relay vars in Blynk will be sent -unsigned long relayBlynkUpdateTime = 0; - -//gets called once at boot. Do all initialization that doesn't depend on network here -void userSetup() -{ - relayPinState = RELAY_PIN_DEFAULT; - relayTimerInterval = RELAY_PIN_TIMER_DEFAULT; - pinMode(RELAY_PIN, OUTPUT); - digitalWrite(RELAY_PIN, relayPinState); -} - -//gets called every time WiFi is (re-)connected. Initialize own network interfaces here -void userConnected() -{ -} - -//loop. You can use "if (WLED_CONNECTED)" to check for successful connection -void userLoop() -{ - //Normalize relayPinState to an accepted value - if (relayPinState != HIGH && relayPinState != LOW) { - relayPinState = RELAY_PIN_DEFAULT; - } - //If relay changes and relayTimerInterval is set, start a timer to change back - if (relayTimerInterval != 0 && - relayPinState != RELAY_PIN_DEFAULT && - !relayTimerStarted ) { - relayTimerStarted = true; - relayTimeToDefault = millis() + relayTimerInterval; - } - //If manually changed back to default, cancel timer - if (relayTimerStarted && relayPinState == RELAY_PIN_DEFAULT ) { - relayTimerStarted = false; - } - //If timer completes, set relay back to default - if (relayTimerStarted && millis() > relayTimeToDefault) { - relayPinState = RELAY_PIN_DEFAULT; - relayTimerStarted = false; - } - digitalWrite(RELAY_PIN, relayPinState); - updateRelayBlynk(); -} - -//Update Blynk with state of userVars at BLYNK_RELAY_UPDATE_INTERVAL -void updateRelayBlynk() -{ - if (!WLED_CONNECTED) return; - if (relayBlynkUpdateTime > millis()) return; - Blynk.virtualWrite(BLYNK_USER_VAR0_PIN, userVar0); - Blynk.virtualWrite(BLYNK_USER_VAR1_PIN, userVar1); - relayBlynkUpdateTime = millis() + BLYNK_RELAY_UPDATE_INTERVAL; -} - -//Add Blynk callback for setting userVar0 -BLYNK_WRITE(BLYNK_USER_VAR0_PIN) -{ - userVar0 = param.asInt(); -} -//Add Blynk callback for setting userVar1 -BLYNK_WRITE(BLYNK_USER_VAR1_PIN) -{ - userVar1 = param.asInt(); -} diff --git a/usermods/mpu6050_imu/usermod_mpu6050_imu.h b/usermods/mpu6050_imu/usermod_mpu6050_imu.h index d839c0df..e923e1f9 100644 --- a/usermods/mpu6050_imu/usermod_mpu6050_imu.h +++ b/usermods/mpu6050_imu/usermod_mpu6050_imu.h @@ -65,9 +65,9 @@ #undef DEBUG_PRINTF #ifdef WLED_DEBUG - #define DEBUG_PRINT(x) DEBUGOUT.print(x) - #define DEBUG_PRINTLN(x) DEBUGOUT.println(x) - #define DEBUG_PRINTF(x...) DEBUGOUT.printf(x) + #define DEBUG_PRINT(x) DEBUGOUT(x) + #define DEBUG_PRINTLN(x) DEBUGOUTLN(x) + #define DEBUG_PRINTF(x...) DEBUGOUTF(x) #else #define DEBUG_PRINT(x) #define DEBUG_PRINTLN(x) @@ -93,18 +93,22 @@ void IRAM_ATTR dmpDataReady() { class MPU6050Driver : public Usermod { private: MPU6050 mpu; - bool enabled = true; unsigned long lastUMRun = millis(); // MPU control/status vars - bool dmpReady = false; // set true if DMP init was successful uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU uint8_t devStatus; // return status after each device operation (0 = success, !0 = error) uint16_t packetSize; // expected DMP packet size (default is 42 bytes) uint16_t fifoCount; // count of all bytes currently in FIFO uint8_t fifoBuffer[64]; // FIFO storage buffer + // strings to reduce flash memory usage (used more than twice) + static const char _INT_pin[]; + public: + MPU6050Driver(const char *name, bool enabled):Usermod(name, enabled) {} //WLEDMM: this shouldn't be necessary (passthrough of constructor), maybe because Usermod is an abstract class + + bool dmpReady = false; // set true if DMP init was successful // WLEDMM expose this info in public interface // orientation/motion vars Quaternion qat; // [w, x, y, z] quaternion container VectorInt16 aa; // [x, y, z] accel sensor measurements @@ -122,7 +126,11 @@ class MPU6050Driver : public Usermod { #endif void setup() { - // WLEDMM begin + // WLEDMM begin + if (!enabled) { + dmpReady = false; + return; + } USER_PRINTLN(F("mpu setup")); PinManagerPinType pins[2] = { { i2c_scl, true }, { i2c_sda, true } }; if ((i2c_scl < 0) || (i2c_sda < 0)) { @@ -171,16 +179,26 @@ class MPU6050Driver : public Usermod { // initialize device DEBUG_PRINT_IMULN(F("Initializing I2C devices...")); // WLEDMM begin - if (!pinManager.allocatePin(INTERRUPT_PIN, false, PinOwner::UM_Unspecified)) + if ((INTERRUPT_PIN < 0) || (!pinManager.isPinINT(INTERRUPT_PIN))) { + //enabled = false; + USER_PRINTF("mpu6050: warning - interrupt GPIO %d does not support interrupts.\n", INTERRUPT_PIN); + //INTERRUPT_PIN = -1; + //return; + } + if ((INTERRUPT_PIN >= 0) && (pinManager.getPinOwner(INTERRUPT_PIN) != PinOwner::UM_IMU) // only allocate pin if we don't ownn it already + && !pinManager.allocatePin(INTERRUPT_PIN, false, PinOwner::UM_IMU)) { //enabled = false; USER_PRINTF("mpu6050: warning - failed to allocate interrupt GPIO %d\n", INTERRUPT_PIN); + //INTERRUPT_PIN = -1; //return; } // WLEDMM end mpu.initialize(); - pinMode(INTERRUPT_PIN, INPUT); + if (INTERRUPT_PIN >= 0) { // WLEDMM only if pin is valid + pinMode(INTERRUPT_PIN, INPUT); + } // verify connection DEBUG_PRINT_IMULN(F("Testing device connections...")); @@ -214,11 +232,13 @@ class MPU6050Driver : public Usermod { DEBUG_PRINT_IMULN(F("Enabling DMP...")); mpu.setDMPEnabled(true); - // enable Arduino interrupt detection - DEBUG_PRINT_IMU(F("Enabling interrupt detection (Arduino external interrupt ")); - DEBUG_PRINT_IMU(digitalPinToInterrupt(INTERRUPT_PIN)); - DEBUG_PRINT_IMULN(F(")...")); - attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING); + if (INTERRUPT_PIN >= 0) { + // enable Arduino interrupt detection + DEBUG_PRINT_IMU(F("Enabling interrupt detection (Arduino external interrupt ")); + DEBUG_PRINT_IMU(digitalPinToInterrupt(INTERRUPT_PIN)); + DEBUG_PRINT_IMULN(F(")...")); + attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING); + } mpuIntStatus = mpu.getIntStatus(); // set our DMP Ready flag so the main loop() function knows it's okay to use it @@ -235,7 +255,9 @@ class MPU6050Driver : public Usermod { DEBUG_PRINT(F("DMP Initialization failed (code ")); DEBUG_PRINT(devStatus); DEBUG_PRINTLN(")"); + dmpReady = false; } + initDone = true; } void connected() { @@ -244,6 +266,7 @@ class MPU6050Driver : public Usermod { void loop() { // if programming failed, don't try to do anything + if (!initDone) return; if (!enabled || (strip.isUpdating() && (millis() - lastUMRun < 2))) return; // be nice, but not too nice lastUMRun = millis(); // update time keeping @@ -265,12 +288,15 @@ class MPU6050Driver : public Usermod { void addToJsonInfo(JsonObject& root) { + if (!initDone) return; + if (!enabled && !dmpReady) return; // WLEDMM no info when usermod disabled JsonObject user = root["u"]; if (user.isNull()) user = root.createNestedObject("u"); StaticJsonDocument<800> doc; //measured 528 // WLEDMM added some margin (was 600) JsonObject imu_meas = doc.createNestedObject("IMU"); + //JsonObject imu_meas = user.createNestedObject("IMU"); #ifdef WLED_DEBUG JsonArray quat_json = imu_meas.createNestedArray("Quat"); quat_json.add(qat.w); @@ -328,29 +354,64 @@ class MPU6050Driver : public Usermod { //{ //} - // void addToConfig(JsonObject& root) - // { - // JsonObject top = root.createNestedObject("MPU6050"); - // top[FPSTR("enabled")] = enabled; + // void addToConfig(JsonObject& root) + // { + // Usermod::addToConfig(root); + // JsonObject top = root[FPSTR(_name)]; + // // //JsonObject interruptPin = top.createNestedObject(FPSTR(_INT_pin)); + // // //interruptPin["pin"] = INTERRUPT_PIN; + // // DEBUG_PRINTLN(F("MPU6050 IMU config saved.")); + // } - // JsonObject interruptPin = top.createNestedObject(FPSTR("interruptPin")); - // interruptPin["pin"] = interruptPin; - // } + //WLEDMM: add appendConfigData + void appendConfigData() + { + oappend(SET_F("addHB('mpu6050-IMU');")); + /* + #ifdef MPU6050_INT_GPIO + oappend(SET_F("xOpt('mpu6050-IMU:interrupt_pin',0,' ⎌',")); oappendi(MPU6050_INT_GPIO); oappend(");"); + #endif + //WLEDMM add errorMessage to um settings + if (strcmp(errorMessage, "") != 0) { + oappend(SET_F("addInfo('errorMessage', 0, 'error: ")); oappend(errorMessage); oappend("! Correct and reboot');"); + } + */ + } - // bool readFromConfig(JsonObject& root) - // { - // JsonObject top = root[FPSTR("MPU6050")]; - // bool configComplete = !top.isNull(); + bool readFromConfig(JsonObject& root) + { + bool configComplete = Usermod::readFromConfig(root); + JsonObject top = root[FPSTR(_name)]; - // configComplete &= getJsonValue(top[FPSTR("enabled")], enabled); - // configComplete &= getJsonValue(top[FPSTR("interruptPin")]["pin"], interruptPin); + if (top.isNull()) { + DEBUG_PRINT(FPSTR(_name)); + DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); + return false; + } - // return configComplete; - // } + //configComplete &= getJsonValue(top[FPSTR(_INT_pin)]["pin"], INTERRUPT_PIN); + + DEBUG_PRINT(FPSTR(_name)); + if (!initDone) { + // first run: reading from cfg.json + DEBUG_PRINTLN(F(" config loaded.")); + } else { + DEBUG_PRINTLN(F(" config (re)loaded.")); + if (enabled || dmpReady) setup(); // re-run setup if user has checked "enabled" + if (!enabled) dmpReady = false; // not enabled inplies "no DMP data ready" + } + + return configComplete; + // use "return !top["newestParameter"].isNull();" when updating Usermod with new features + //return !top[FPSTR(_INT_pin)].isNull(); + } uint16_t getId() { return USERMOD_ID_IMU; } -}; \ No newline at end of file +}; + +// strings to reduce flash memory usage (used more than twice) +const char MPU6050Driver::_INT_pin[] PROGMEM = "interrupt_pin"; diff --git a/usermods/multi_relay/usermod_multi_relay.h b/usermods/multi_relay/usermod_multi_relay.h index 7d0a7a7d..749b9c75 100644 --- a/usermods/multi_relay/usermod_multi_relay.h +++ b/usermods/multi_relay/usermod_multi_relay.h @@ -184,7 +184,7 @@ class MultiRelay : public Usermod { */ MultiRelay() { const int8_t defPins[] = {MULTI_RELAY_PINS}; - for (int i=0; i ")); + DEBUG_PRINTLN(presetNameBuffer); savePreset(autoSavePreset, presetNameBuffer); } @@ -86,7 +90,7 @@ class AutoSaveUsermod : public Usermod { #ifdef USERMOD_FOUR_LINE_DISPLAY if (display != nullptr) { display->wakeDisplay(); - display->overlay("Settings", "Auto Saved", 1500); + if (display->canDraw()) display->overlay("Settings", "Auto Saved", 1500); // WLEDMM bugfix } #endif } diff --git a/usermods/usermod_v2_fastled/readme.md b/usermods/usermod_v2_fastled/readme.md new file mode 100644 index 00000000..93707655 --- /dev/null +++ b/usermods/usermod_v2_fastled/readme.md @@ -0,0 +1,10 @@ +# Usermods API v2 example usermod + +In this usermod file you can find the documentation on how to take advantage of the new version 2 usermods! + +## Installation + +Copy `usermod_v2_fastled.h` to the wled00 directory. +Uncomment the corresponding lines in `usermods_list.cpp` and compile! +_(You shouldn't need to actually install this, it does nothing useful)_ + diff --git a/usermods/usermod_v2_fastled/usermod_v2_fastled.h b/usermods/usermod_v2_fastled/usermod_v2_fastled.h new file mode 100644 index 00000000..c105396b --- /dev/null +++ b/usermods/usermod_v2_fastled/usermod_v2_fastled.h @@ -0,0 +1,649 @@ +#pragma once + +#include "wled.h" + +#warning WLEDMM usermod: CC BY-NC 3.0 licensed effects by Stefan Petrick, include this usermod only if you accept the terms! +//======================================================================================================================== +//======================================================================================================================== +//======================================================================================================================== + + +// Polar basics demo for the +// FastLED Podcast #2 +// https://www.youtube.com/watch?v=KKjFRZFBUrQ +// +// VO.1 preview version +// by Stefan Petrick 2023 +// This code is licenced under a +// Creative Commons Attribution +// License CC BY-NC 3.0 + +//based on: https://gist.github.com/StefanPetrick/9c091d9a28a902af5a7b540e40442c64 + +class StefanPetrickCore { + private: + + public: + float runtime; // elapse ms since startup + float newdist, newangle; // parameters for image reconstruction + float z; // 3rd dimension for the 3d noise function + float offset_x, offset_y; // wanna shift the cartesians during runtime? + float scale_x, scale_y; // cartesian scaling in 2 dimensions + float dist, angle; // the actual polar coordinates + + int x, y; // the cartesian coordiantes + int num_x;// = WIDTH; // horizontal pixel count + int num_y;// = HEIGHT; // vertical pixel count + + float center_x;// = (num_x / 2) - 0.5; // the reference point for polar coordinates + float center_y;// = (num_y / 2) - 0.5; // (can also be outside of the actual xy matrix) + //float center_x = 20; // the reference point for polar coordinates + //float center_y = 20; + + //WLEDMM: assign 32x32 fixed for the time being + float theta [60] [32]; // look-up table for all angles WLEDMM: 60x32 to support WLED Effects ledmaps + float distance[60] [32]; // look-up table for all distances + + // std::vector> theta; // look-up table for all angles + // std::vector> distance; // look-up table for all distances + // std::vector> vignette; + // std::vector> inverse_vignette; + + float spd; // can be used for animation speed manipulation during runtime + + float show1, show2, show3, show4, show5; // to save the rendered values of all animation layers + float red, green, blue; // for the final RGB results after the colormapping + + float c, d, e, f; // factors for oscillators + float linear_c, linear_d, linear_e, linear_f; // linear offsets + float angle_c, angle_d, angle_e, angle_f; // angle offsets + float noise_angle_c, noise_angle_d, noise_angle_e, noise_angle_f; // angles based on linear noise travel + float dir_c, dir_d, dir_e, dir_f; // direction multiplicators + + StefanPetrickCore() { + USER_PRINTLN("StefanPetrickCore constructor"); + } + ~StefanPetrickCore() { + USER_PRINTLN("StefanPetrickCore destructor"); + } + + void init() { + num_x = SEGMENT.virtualWidth(); // horizontal pixel count + num_y = SEGMENT.virtualHeight(); // vertical pixel count + center_x = (num_x / 2) - 0.5; // the reference point for polar coordinates + center_y = (num_y / 2) - 0.5; // (can also be outside of the actual xy matrix) + + //allocate memory for the 2D arrays + // theta.resize(num_x, std::vector(num_y, 0)); + // distance.resize(num_x, std::vector(num_y, 0)); + // vignette.resize(num_x, std::vector(num_y, 0)); + // inverse_vignette.resize(num_x, std::vector(num_y, 0)); + + render_polar_lookup_table(); // precalculate all polar coordinates + // to improve the framerate + } + + void calculate_oscillators() { + + runtime = millis(); // save elapsed ms since start up + + runtime = runtime * spd; // global anaimation speed + + linear_c = runtime * c; // some linear rising offsets 0 to max + linear_d = runtime * d; + linear_e = runtime * e; + linear_f = runtime * f; + + angle_c = fmodf(linear_c, 2 * PI); // some cyclic angle offsets 0 to 2*PI + angle_d = fmodf(linear_d, 2 * PI); + angle_e = fmodf(linear_e, 2 * PI); + angle_f = fmodf(linear_f, 2 * PI); + + dir_c = sinf(angle_c); // some direction oscillators -1 to 1 + dir_d = sinf(angle_d); + dir_e = sinf(angle_e); + dir_f = sinf(angle_f); + } + + // given a static polar origin we can precalculate + // all the (expensive) polar coordinates + + void render_polar_lookup_table() { + + for (int xx = 0; xx < num_x; xx++) { + for (int yy = 0; yy < num_y; yy++) { + + float dx = xx - center_x; + float dy = yy - center_y; + + distance[xx] [yy] = hypotf(dx, dy); + theta[xx] [yy] = atan2f(dy, dx); + + } + } + } + + // float mapping maintaining 32 bit precision + // we keep values with high resolution for potential later usage + + float map_float(float x, float in_min, float in_max, float out_min, float out_max) { + + float result = (x-in_min) * (out_max-out_min) / (in_max-in_min) + out_min; + if (result < out_min) result = out_min; + if( result > out_max) result = out_max; + + return result; + } + + // Avoid any possible color flicker by forcing the raw RGB values to be 0-255. + // This enables to play freely with random equations for the colormapping + // without causing flicker by accidentally missing the valid target range. + + void rgb_sanity_check() { + // rescue data if possible: when negative return absolute value + if (red < 0) red = abs(red); + if (green < 0) green = abs(green); + if (blue < 0) blue = abs(blue); + + // discard everything above the valid 0-255 range + if (red > 255) red = 255; + if (green > 255) green = 255; + if (blue > 255) blue = 255; + } + + void write_pixel_to_framebuffer() { + // the final color values shall not exceed 255 (to avoid flickering pixels caused by >255 = black...) + // negative values * -1 + + rgb_sanity_check(); + + CRGB finalcolor = CRGB(red, green, blue); + + // write the rendered pixel into the framebutter + SEGMENT.setPixelColorXY(x,y,finalcolor); + } + + // Show the current framerate & rendered pixels per second in the serial monitor. + + void report_performance() { + + int fps = FastLED.getFPS(); // frames per second + int kpps = (fps * SEGMENT.virtualLength()) / 1000; // kilopixel per second + + USER_PRINT(kpps); USER_PRINT(" kpps ... "); + USER_PRINT(fps); USER_PRINT(" fps @ "); + USER_PRINT(SEGMENT.virtualLength()); USER_PRINTLN(" LEDs "); + + } +}; + +class PolarBasics:public StefanPetrickCore { + private: + + public: + // Background for setting the following 2 numbers: the FastLED inoise16() function returns + // raw values ranging from 0-65535. In order to improve contrast we filter this output and + // stretch the remains. In histogram (photography) terms this means setting a blackpoint and + // a whitepoint. low_limit MUST be smaller than high_limit. + + uint16_t low_limit = 30000; // everything lower drawns in black + // higher numer = more black & more contrast present + uint16_t high_limit = 50000; // everything higher gets maximum brightness & bleeds out + // lower number = the result will be more bright & shiny + + // float vignette[60] [32]; + // float inverse_vignette[60] [32]; + + PolarBasics() { + USER_PRINTLN("constructor"); + } + ~PolarBasics() { + USER_PRINTLN("destructor"); + } + + void speedratiosAndOscillators() { + // set speedratios for the offsets & oscillators + + spd = 0.05 ; + c = 0.013 ; + d = 0.017 ; + e = 0.2 ; + f = 0.007 ; + + low_limit = 30000; + high_limit = 50000; + + calculate_oscillators(); // get linear offsets and oscillators going + } + + void forLoop() { + // ...and now let's generate a frame + + for (x = 0; x < num_x; x++) { + for (y = 0; y < num_y; y++) { + // pick polar coordinates from look the up table + + dist = distance [x] [y]; + angle = theta [y] [x]; + + // Generation of one layer. Explore the parameters and what they do. + + scale_x = 10000; // smaller value = zoom in, bigger structures, less detail + scale_y = 10000; // higher = zoom out, more pixelated, more detail + z = linear_c * SEGMENT.custom3; // must be >= 0 + newangle = 5*SEGMENT.intensity/255 * angle + angle_c - 3 * SEGMENT.speed/255 * (dist/10*dir_c); + newdist = dist; + offset_x = SEGMENT.custom1; // must be >=0 + offset_y = SEGMENT.custom2; // must be >=0 + + show1 = render_pixel(); + + // newangle = 5*SEGMENT.intensity/255 * angle + angle_d - 3 * SEGMENT.speed/255 * (dist/10*dir_d); + // z = linear_d * SEGMENT.custom3; // must be >= 0 + // show2 = render_pixel(); + + // newangle = 5*SEGMENT.intensity/255 * angle + angle_e - 3 * SEGMENT.speed/255 * (dist/10*dir_e); + // z = linear_e * SEGMENT.custom3; // must be >= 0 + // show3 = render_pixel(); + + // Colormapping - Assign rendered values to colors + + red = show1; + green = show2; + blue = show3; + + // Check the final results. + // Discard faulty RGB values & write the valid results into the framebuffer. + + write_pixel_to_framebuffer(); + } + } + } + + void calculate_oscillators() { + + StefanPetrickCore::calculate_oscillators(); + + uint16_t noi; + noi = inoise16(10000 + linear_c * 100000); // some noise controlled angular offsets + noise_angle_c = map_float(noi, 0, 65535 , 0, 4*PI); + noi = inoise16(20000 + linear_d * 100000); + noise_angle_d = map_float(noi, 0, 65535 , 0, 4*PI); + noi = inoise16(30000 + linear_e * 100000); + noise_angle_e = map_float(noi, 0, 65535 , 0, 4*PI); + noi = inoise16(40000 + linear_f * 100000); + noise_angle_f = map_float(noi, 0, 65535 , 0, 4*PI); + } + + + // convert polar coordinates back to cartesian + // & render noise value there + + float render_pixel() { + + // convert polar coordinates back to cartesian ones + + float newx = (offset_x + center_x - (cosf(newangle) * newdist)) * scale_x; + float newy = (offset_y + center_y - (sinf(newangle) * newdist)) * scale_y; + + // render noisevalue at this new cartesian point + + uint16_t raw_noise_field_value = inoise16(newx, newy, z); + + // a lot is happening here, namely + // A) enhance histogram (improve contrast) by setting the black and white point + // B) scale the result to a 0-255 range + // it's the contrast boosting & the "colormapping" (technically brightness mapping) + + if (raw_noise_field_value < low_limit) raw_noise_field_value = low_limit; + if (raw_noise_field_value > high_limit) raw_noise_field_value = high_limit; + + float scaled_noise_value = map_float(raw_noise_field_value, low_limit, high_limit, 0, 255); + + return scaled_noise_value; + + // done, we've just rendered one color value for one single pixel + } + + // // precalculate a radial brightness mask + + // void render_vignette_table(float filter_radius) { + + // for (int xx = 0; xx < num_x; xx++) { + // for (int yy = 0; yy < num_y; yy++) { + + // vignette[xx] [yy] = (filter_radius - distance[xx] [yy]) / filter_radius; + // if (vignette[xx] [yy] < 0) vignette[xx] [yy] = 0; + // } + // } + // } + +}; + + /* + Ken Perlins improved noise - http://mrl.nyu.edu/~perlin/noise/ + C-port: http://www.fundza.com/c4serious/noise/perlin/perlin.html + by Malcolm Kesson; arduino port by Peter Chiochetti, Sep 2007 : + - make permutation constant byte, obsoletes init(), lookup % 256 + */ + + static const byte p[] = { 151,160,137,91,90, 15,131, 13,201,95,96, + 53,194,233, 7,225,140,36,103,30,69,142, 8,99,37,240,21,10,23,190, 6, + 148,247,120,234,75, 0,26,197,62,94,252,219,203,117, 35,11,32,57,177, + 33,88,237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,134,139, + 48,27,166, 77,146,158,231,83,111,229,122, 60,211,133,230,220,105,92, + 41,55,46,245,40,244,102,143,54,65,25,63,161, 1,216,80,73,209,76,132, + 187,208, 89, 18,169,200,196,135,130,116,188,159, 86,164,100,109,198, + 173,186, 3,64,52,217,226,250,124,123,5,202,38,147,118,126,255,82,85, + 212,207,206, 59,227, 47,16,58,17,182,189, 28,42,223,183,170,213,119, + 248,152,2,44,154,163,70,221,153,101,155,167,43,172, 9,129,22,39,253, + 19,98,108,110,79,113,224,232,178,185,112,104,218,246, 97,228,251,34, + 242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239,107, + 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4, + 150,254,138,236,205, 93,222,114, 67,29,24, 72,243,141,128,195,78,66, + 215,61,156,180 + }; + +// Circular Blobs +// +// VO.2 preview version +// by Stefan Petrick 2023 +// This code is licenced under a +// Creative Commons Attribution +// License CC BY-NC 3.0 +// +// In order to run this on your own setup you might want to check and change +// line 22 & 23 according to your matrix size and +// line 75 to suit your LED interface type. +// +// In case you want to run this code on a different LED driver library +// (like SmartMatrix, OctoWS2812, ESP32 16x parallel output) you will need to change +// line 52 to your own framebuffer and line 276+279 to your own setcolor function. +// In line 154 the framebuffer gets pushed to the LEDs. +// The whole report_performance function you can just comment out. It gets called +// in line 157. +// +// With this adaptions it should be easy to use this code with +// any given LED driver & interface you might prefer. + +//based on https://gist.github.com/StefanPetrick/35ffd8467df22a77067545cfb889aa4f +//and Fastled podcast nr 3: https://www.youtube.com/watch?v=3tfjP7GJnZo + +class CircularBlobs:public StefanPetrickCore { + private: + + float fade(float t){ return t * t * t * (t * (t * 6 - 15) + 10); } + float lerp(float t, float a, float b){ return a + t * (b - a); } + float grad(int hash, float x, float y, float z) + { + int h = hash & 15; /* CONVERT LO 4 BITS OF HASH CODE */ + float u = h < 8 ? x : y, /* INTO 12 GRADIENT DIRECTIONS. */ + v = h < 4 ? y : h==12||h==14 ? x : z; + return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v); + } + + #define P(x) p[(x) & 255] + + float pnoise(float x, float y, float z) { + int X = (int)floorf(x) & 255, /* FIND UNIT CUBE THAT */ + Y = (int)floorf(y) & 255, /* CONTAINS POINT. */ + Z = (int)floorf(z) & 255; + x -= floorf(x); /* FIND RELATIVE X,Y,Z */ + y -= floorf(y); /* OF POINT IN CUBE. */ + z -= floorf(z); + float u = fade(x), /* COMPUTE FADE CURVES */ + v = fade(y), /* FOR EACH OF X,Y,Z. */ + w = fade(z); + int A = P(X)+Y, + AA = P(A)+Z, + AB = P(A+1)+Z, /* HASH COORDINATES OF */ + B = P(X+1)+Y, + BA = P(B)+Z, + BB = P(B+1)+Z; /* THE 8 CUBE CORNERS, */ + + return lerp(w,lerp(v,lerp(u, grad(P(AA ), x, y, z), /* AND ADD */ + grad(P(BA ), x-1, y, z)), /* BLENDED */ + lerp(u, grad(P(AB ), x, y-1, z), /* RESULTS */ + grad(P(BB ), x-1, y-1, z))), /* FROM 8 */ + lerp(v, lerp(u, grad(P(AA+1), x, y, z-1), /* CORNERS */ + grad(P(BA+1), x-1, y, z-1)), /* OF CUBE */ + lerp(u, grad(P(AB+1), x, y-1, z-1), + grad(P(BB+1), x-1, y-1, z-1)))); + } + + + public: + + // Background for setting the following 2 numbers: the pnoise() function returns + // raw values ranging from -1 to +1. In order to improve contrast we filter this output and + // stretch the remains. In histogram (photography) terms this means setting a blackpoint and + // a whitepoint. low_limit MUST be smaller than high_limit. + float low_limit = 0; // everything lower drawns in black + // higher numer = more black & more contrast present + float high_limit = 0.5; // everything higher gets maximum brightness & bleeds out + // lower number = the result will be more bright & shiny + float offset_z; // wanna shift the cartesians during runtime? + float scale_z; // cartesian scaling in 3 dimensions + + void speedratiosAndOscillators() { + + // set speedratios for the offsets & oscillators + + spd = 0.001 ; // higher = faster + c = 0.05 ; + d = 0.07 ; + e = 0.09 ; + f = 0.01 ; + + low_limit = 0; + high_limit = 0.5; + + calculate_oscillators(); // get linear offsets and oscillators going + } + + void forLoop() { + // ...and now let's generate a frame + + for (x = 0; x < num_x; x++) { + for (y = 0; y < num_y; y++) { + + dist = distance[x][y]; // pick precalculated polar data + angle = theta[x][y]; + + // define first animation layer + scale_x = 0.11; // smaller value = zoom in + scale_y = 0.1; // higher = zoom out + scale_z = 0.1; + + newangle = angle + 5*SEGMENT.speed/255 * noise_angle_c + 5*SEGMENT.speed/255 * noise_angle_f; + newdist = 5*SEGMENT.intensity/255 * dist; + offset_z = linear_c * 100; + z = -5 * sqrtf(dist) ; + show1 = render_pixel_faster(); + + // repeat for the 2nd layer, every parameter you don't change stays as it was set + // in the previous layer. + + offset_z = linear_d * 100; + newangle = angle + 5*SEGMENT.speed/255 * noise_angle_d + 5*SEGMENT.speed/255 * noise_angle_f; + show2 = render_pixel_faster(); + + // 3d layer + + offset_z = linear_e*100; + newangle = angle + 5*SEGMENT.speed/255 * noise_angle_e + 5*SEGMENT.speed/255 * noise_angle_f; + show3 = render_pixel_faster(); + + // create some interference between the layers + + show3 = show3-show2-show1; + if (show3 < 0) show3 = 0; + + // Colormapping - Assign rendered values to colors + + red = show1-show2/2; + if (red < 0) red=0; + green = (show1-show2)/2; + if (green < 0) green=0; + blue = show3-show1/2; + if (blue < 0) blue=0; + + // Check the final results and store them. + // Discard faulty RGB values & write the remaining valid results into the framebuffer. + + write_pixel_to_framebuffer(); + } + } + } + + void calculate_oscillators() { + + StefanPetrickCore::calculate_oscillators(); + + float n; + + n = 1 + pnoise(linear_c , 10, 10); // some noise controlled angular offsets 0 to PI + noise_angle_c = n * PI; + n = 1 + pnoise(linear_d , 20, 20); + noise_angle_d = n * PI; + n = 1 + pnoise(linear_e , 30, 30); + noise_angle_e = n * PI; + n = 1 + pnoise(linear_f , 40, 40); + noise_angle_f = n * PI; + } + + // Convert the polar 2 coordinates back to cartesian ones & also apply all 3d transitions. + // Calculate the noise value at this point after the 5 dimensional manipulation of + // the underlaying coordinates. + // + // Now I use a 32 bit float noise function which is more precise AND also more FPU friendly. + // This results in a better render qualitiy in edgecases AND also in a 15% better performance. + // Hurray! + + float render_pixel_faster() { + + // convert polar coordinates back to cartesian ones + + float newx = (offset_x + center_x - (cosf(newangle) * newdist)) * scale_x; + float newy = (offset_y + center_y - (sinf(newangle) * newdist)) * scale_y; + float newz = (offset_z + z) * scale_z; + + // render noisevalue at this new cartesian point + + float raw_noise_field_value = pnoise(newx, newy, newz); + + + // a lot is happening here, namely + // A) enhance histogram (improve contrast) by setting the black and white point + // B) scale the result to a 0-255 range + // it's the contrast boosting & the "colormapping" (technically brightness mapping) + + if (raw_noise_field_value < low_limit) raw_noise_field_value = low_limit; + if (raw_noise_field_value > high_limit) raw_noise_field_value = high_limit; + + float scaled_noise_value = map_float(raw_noise_field_value, low_limit, high_limit, 0, 255); + + return scaled_noise_value; + + // done, we've just rendered one color value for one single pixel + } +}; + +//effect functions +uint16_t mode_PolarBasics(void) { + + PolarBasics* spe; + + + if(!SEGENV.allocateData(sizeof(PolarBasics))) {SEGMENT.fill(SEGCOLOR(0)); return 350;} //mode_static(); //allocation failed + + spe = reinterpret_cast(SEGENV.data); + + //first time init + if (SEGENV.call == 0) { + + USER_PRINTF("mode_PolarBasics %d\n", sizeof(PolarBasics)); + // if (SEGENV.call == 0) SEGMENT.setUpLeds(); + + spe->init(); + + // spe->render_vignette_table(9.5); // the number is the desired radius in pixel + // WIDTH/2 generates a circle + } + + spe->speedratiosAndOscillators(); + + spe->forLoop(); + + // FastLED.show(); + + // EVERY_N_MILLIS(500) spe->report_performance(); + + return FRAMETIME; +} +static const char _data_FX_mode_PolarBasics[] PROGMEM = "💡Polar Basics ☾@AngleDist,AngleMult;;!;2;sx=0,ix=51,c1=0,c2=0,c3=0"; + + +uint16_t mode_CircularBlobs(void) { + CircularBlobs* spe; + + + if(!SEGENV.allocateData(sizeof(CircularBlobs))) {SEGMENT.fill(SEGCOLOR(0)); return 350;} //mode_static(); //allocation failed + + spe = reinterpret_cast(SEGENV.data); + + //first time init + if (SEGENV.call == 0) { + + USER_PRINTF("mode_CircularBlobs %d\n", sizeof(CircularBlobs)); + // if (SEGENV.call == 0) SEGMENT.setUpLeds(); + + spe->init(); + + } + + spe->speedratiosAndOscillators(); + + spe->forLoop(); + + // FastLED.show(); + + // EVERY_N_MILLIS(500) spe->report_performance(); + + return FRAMETIME; +} +static const char _data_FX_mode_CircularBlobs[] PROGMEM = "💡CircularBlobs ☾@AngleDist,AngleMult;;!;2;sx=51,ix=51,c1=0,c2=0,c3=0"; + + +class FastledUsermod : public Usermod { + + public: + + FastledUsermod(const char *name, bool enabled):Usermod(name, enabled) {} //WLEDMM + + void setup() { + strip.addEffect(255, &mode_PolarBasics, _data_FX_mode_PolarBasics); + strip.addEffect(255, &mode_CircularBlobs, _data_FX_mode_CircularBlobs); + + initDone = true; + } + + void loop() { + if (!enabled || strip.isUpdating()) return; + + // do your magic here + if (millis() - lastTime > 1000) { + //USER_PRINTLN("I'm alive!"); + lastTime = millis(); + } + } + + uint16_t getId() + { + return USERMOD_ID_FASTLED; + } + +}; + + + diff --git a/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h b/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h index 7f9ada8a..85862888 100644 --- a/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h +++ b/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h @@ -2,64 +2,61 @@ #include // WLEDMM: make sure that I2C drivers have the "right" Wire Object #include +#include #undef U8X8_NO_HW_I2C // WLEDMM: we do want I2C hardware drivers - if possible -//#define WIRE_INTERFACES_COUNT 2 // experimental - tell U8x8Lib that there is a econd Wire unit +//#define WIRE_INTERFACES_COUNT 2 // experimental - tell U8x8Lib that there is a second Wire unit #include "wled.h" #include // from https://github.com/olikraus/u8g2/ #include "4LD_wled_fonts.c" +#ifndef FLD_ESP32_NO_THREADS +#define FLD_ESP32_USE_THREADS // comment out to use 0.13.x behaviour without parallel update task - slower, but more robust. May delay other tasks like LEDs or audioreactive!! +#endif + +//#define OLD_4LD_FONTS // comment out if you prefer the "classic" look with blocky fonts (saves 1K flash) + // -// Insired by the usermod_v2_four_line_display +// Inspired by the usermod_v2_four_line_display // // v2 usermod for using 128x32 or 128x64 i2c // OLED displays to provide a four line display // for WLED. // // Dependencies -// * This usermod REQURES the ModeSortUsermod -// * This Usermod works best, by far, when coupled -// with RotaryEncoderUIUsermod. +// * This usermod does not REQUIRE the ModeSortUsermod any more +// * This usermod works best, by far, when coupled +// with RotaryEncoderUI_ALT usermod. // // Make sure to enable NTP and set your time zone in WLED Config | Time. // // REQUIREMENT: You must add the following requirements to // REQUIREMENT: "lib_deps" within platformio.ini / platformio_override.ini -// REQUIREMENT: * U8g2 (the version already in platformio.ini is fine) -// REQUIREMENT: * Wire +// REQUIREMENT: olikraus/U8g2@ ^2.34.15 (the version already in platformio.ini is fine) // //The SCL and SDA pins are defined here. #ifndef FLD_PIN_SCL - #define FLD_PIN_SCL i2c_scl + #define FLD_PIN_SCL -1 #endif #ifndef FLD_PIN_SDA - #define FLD_PIN_SDA i2c_sda + #define FLD_PIN_SDA -1 #endif #ifndef FLD_PIN_CLOCKSPI - #define FLD_PIN_CLOCKSPI spi_sclk + #define FLD_PIN_CLOCKSPI -1 #endif - #ifndef FLD_PIN_MOSISPI //WLEDMM renamed from HW_PIN_DATASPI - #define FLD_PIN_MOSISPI spi_mosi +#ifndef FLD_PIN_MOSISPI //WLEDMM renamed from HW_PIN_DATASPI + #define FLD_PIN_MOSISPI -1 #endif #ifndef FLD_PIN_CS - #define FLD_PIN_CS spi_cs + #define FLD_PIN_CS -1 #endif -#ifdef ARDUINO_ARCH_ESP32 - #ifndef FLD_PIN_DC - #define FLD_PIN_DC 19 - #endif - #ifndef FLD_PIN_RESET - #define FLD_PIN_RESET 26 - #endif -#else - #ifndef FLD_PIN_DC - #define FLD_PIN_DC 12 - #endif - #ifndef FLD_PIN_RESET - #define FLD_PIN_RESET 16 - #endif +#ifndef FLD_PIN_DC + #define FLD_PIN_DC -1 +#endif +#ifndef FLD_PIN_RESET + #define FLD_PIN_RESET -1 #endif #ifndef FLD_TYPE @@ -97,7 +94,9 @@ typedef enum { SSD1305, // U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C SSD1305_64, // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI - SSD1306_SPI64 // U8X8_SSD1306_128X64_NONAME_HW_SPI + SSD1306_SPI64=7, // U8X8_SSD1306_128X64_NONAME_HW_SPI + SSD1309_SPI64=8, // U8X8_SSD1309_128X64_NONAME0_4W_HW_SPI + SSD1327_SPI128=9 // U8X8_SSD1327_WS_128X128_4W_SW_SPI } DisplayType; @@ -112,13 +111,31 @@ class FourLineDisplayUsermod : public Usermod { static FourLineDisplayUsermod *instance; bool initDone = false; - volatile bool drawing = false; + volatile bool drawing = false; // true of overlay drawing is active + volatile bool reDrawing = false; // true if redraw ongoing (on esp32, this happens in a separate task) char errorMessage[100] = ""; //WLEDMM: show error in um settings if occurred // HW interface & configuration U8X8 *u8x8 = nullptr; // pointer to U8X8 display object +#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS) + // semaphores - needed on ESP32 only, as we use a separate task to update the display + SemaphoreHandle_t drawMux = xSemaphoreCreateBinary(); // for drawstring and drawglyph functions (to prevent concurrent access to HW) + SemaphoreHandle_t drawMuxBig = xSemaphoreCreateBinary(); // for draw2x2GlyphIcons() and showCurrentEffectOrPalette() - more complex and not thread-safe + const TickType_t maxWait = 300 * portTICK_PERIOD_MS; // wait max. 300ms (drawstring semaphore) + const TickType_t maxWaitLong = 800 * portTICK_PERIOD_MS; // wait max. 800ms (big drawing semaphore) + #define FLD_SemaphoreTake(x,t) xSemaphoreTake((x),(t)) + #define FLD_SemaphoreGive(x) xSemaphoreGive(x) +#else + // 8266 or no tasks - no semaphores + #define FLD_SemaphoreTake(x,t) pdTRUE + #define FLD_SemaphoreGive(x) + #if !defined(ARDUINO_ARCH_ESP32) && !defined(pdTRUE) + #define pdTRUE true + #endif +#endif + #ifndef FLD_SPI_DEFAULT int8_t ioPin[5] = {FLD_PIN_SCL, FLD_PIN_SDA, -1, -1, -1}; // I2C pins: SCL, SDA uint32_t ioFrequency = 400000; // in Hz (minimum is 100000, baseline is 400000 and maximum should be 3400000) @@ -128,7 +145,7 @@ class FourLineDisplayUsermod : public Usermod { #endif DisplayType type = FLD_TYPE; // display type - bool typeOK = true; //WLEDMM: instead of type == NULL and type=NULL + bool typeOK = false; //WLEDMM: instead of type == NULL and type=NULL. Initially false, as display was not initialized yet bool flip = false; // flip display 180° uint8_t contrast = 10; // screen contrast uint8_t lineHeight = 1; // 1 row or 2 rows @@ -147,6 +164,8 @@ class FourLineDisplayUsermod : public Usermod { bool enabled = true; #endif bool contrastFix = false; + bool driverHW = false; + bool driverSPI = false; // Next variables hold the previous known values to determine if redraw is // required. @@ -196,6 +215,9 @@ class FourLineDisplayUsermod : public Usermod { // some displays need this to properly apply contrast void setVcomh(bool highContrast) { + if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it + if (u8x8 == nullptr) return; + u8x8_t *u8x8_struct = u8x8->getU8x8(); u8x8_cad_StartTransfer(u8x8_struct); u8x8_cad_SendCmd(u8x8_struct, 0x0db); //address of value @@ -207,34 +229,78 @@ class FourLineDisplayUsermod : public Usermod { * Wrappers for screen drawing */ void setFlipMode(uint8_t mode) { - if (!typeOK || !enabled) return; + if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it + if (u8x8 == nullptr) return; u8x8->setFlipMode(mode); } void setContrast(uint8_t contrast) { - if (!typeOK || !enabled) return; + if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it + if (u8x8 == nullptr) return; u8x8->setContrast(contrast); } void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false) { - if (!typeOK || !enabled) return; + if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it + if (u8x8 == nullptr) return; + if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex + +#if defined(ARDUINO_ARCH_ESP32) && !defined(OLD_4LD_FONTS) // WLEDMM use nicer 2x2 font on ESP32 + if (!ignoreLH && lineHeight>1) { + if(strlen(string) > 3) // WLEDMM little hack - less than 3 chars -> show in bold + u8x8->setFont(u8x8_font_7x14_1x2_r); // normal + else + u8x8->setFont(u8x8_font_8x13B_1x2_r); // bold + u8x8->drawString(col, row, string); + } + else { + u8x8->setFont(u8x8_font_chroma48medium8_r); + u8x8->drawString(col, row, string); + } +#else u8x8->setFont(u8x8_font_chroma48medium8_r); - if (!ignoreLH && lineHeight==2) u8x8->draw1x2String(col, row, string); - else u8x8->drawString(col, row, string); + if (!ignoreLH && lineHeight>1) u8x8->draw1x2String(col, row, string); + else u8x8->drawString(col, row, string); +#endif + FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex } void draw2x2String(uint8_t col, uint8_t row, const char *string) { if (!typeOK || !enabled) return; + if (u8x8 == nullptr) return; + if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex +#if defined(ARDUINO_ARCH_ESP32) && !defined(OLD_4LD_FONTS) // WLEDMM use nicer 2x2 font on ESP32 + if (lineHeight>1) { // WLEDMM use 2x3 on 128x64 displays + //u8x8->setFont(u8x8_font_profont29_2x3_r); // sans serif 2x3 + u8x8->setFont(u8x8_font_courB18_2x3_r); // courier bold 2x3 + u8x8->drawString(col, row + (row >3? 1:0), string); + } else { + //u8x8->setFont(u8x8_font_lucasarts_scumm_subtitle_o_2x2_r); + //u8x8->setFont(u8x8_font_lucasarts_scumm_subtitle_r_2x2_r); + u8x8->setFont(u8x8_font_px437wyse700b_2x2_r); + u8x8->drawString(col, row, string); + } +#else u8x8->setFont(u8x8_font_chroma48medium8_r); - u8x8->draw2x2String(col, row, string); + if (lineHeight>1) { // WLEDMM use 2x3 on 128x64 displays + u8x8->draw2x2String(col, row + (row >3? 1:0), string); + } else { + u8x8->draw2x2String(col, row, string); + } +#endif + FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex } void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH=false) { if (!typeOK || !enabled) return; + if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex u8x8->setFont(font); - if (!ignoreLH && lineHeight==2) u8x8->draw1x2Glyph(col, row, glyph); + if (!ignoreLH && lineHeight>1) u8x8->draw1x2Glyph(col, row, glyph); else u8x8->drawGlyph(col, row, glyph); + FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex } void draw2x2Glyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font) { if (!typeOK || !enabled) return; + if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex u8x8->setFont(font); u8x8->draw2x2Glyph(col, row, glyph); + FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex } uint8_t getCols() { if (!typeOK || !enabled) return 0; @@ -242,10 +308,14 @@ class FourLineDisplayUsermod : public Usermod { } void clear() { if (!typeOK || !enabled) return; - u8x8->clear(); + if (nullptr == u8x8) return; // prevents some crashes + if (FLD_SemaphoreTake(drawMux, maxWaitLong ) != pdTRUE) return; // WLEDMM acquire draw mutex - clear() can take very long in software I2C mode + u8x8->clear(); // crashes randomly on ESP32 + FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex } void setPowerSave(uint8_t save) { - if (!typeOK || !enabled) return; + if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it + if (u8x8 == nullptr) return; u8x8->setPowerSave(save); } @@ -256,7 +326,9 @@ class FourLineDisplayUsermod : public Usermod { } void draw2x2GlyphIcons() { - if (lineHeight == 2) { + if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it + if (FLD_SemaphoreTake(drawMuxBig, maxWaitLong) != pdTRUE) return; // WLEDMM acquire BIG draw mutex + if (lineHeight > 1) { drawGlyph( 1, 0, 1, u8x8_4LineDisplay_WLED_icons_2x2, true); //brightness icon drawGlyph( 5, 0, 2, u8x8_4LineDisplay_WLED_icons_2x2, true); //speed icon drawGlyph( 9, 0, 3, u8x8_4LineDisplay_WLED_icons_2x2, true); //intensity icon @@ -269,6 +341,7 @@ class FourLineDisplayUsermod : public Usermod { drawGlyph(15, 2, 4, u8x8_4LineDisplay_WLED_icons_1x1); //palette icon drawGlyph(15, 3, 5, u8x8_4LineDisplay_WLED_icons_1x1); //effect icon } + FLD_SemaphoreGive(drawMuxBig); // WLEDMM release BIG draw mutex } /** @@ -283,7 +356,7 @@ class FourLineDisplayUsermod : public Usermod { while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing drawing = true; - char lineBuffer[LINE_BUFFER_SIZE]; + char lineBuffer[LINE_BUFFER_SIZE] = { '\0' }; static byte lastSecond; byte secondCurrent = second(localTime); byte minuteCurrent = minute(localTime); @@ -299,23 +372,41 @@ class FourLineDisplayUsermod : public Usermod { } if (knownHour != hourCurrent) { // only update date when hour changes - sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(month(localTime)), day(localTime)); + snprintf_P(lineBuffer, LINE_BUFFER_SIZE, PSTR("%s %2d "), monthShortStr(month(localTime)), day(localTime)); draw2x2String(2, lineHeight==1 ? 0 : lineHeight, lineBuffer); // adjust for 8 line displays, draw month and day } - sprintf_P(lineBuffer,PSTR("%2d:%02d"), (useAMPM ? AmPmHour : hourCurrent), minuteCurrent); + snprintf_P(lineBuffer,LINE_BUFFER_SIZE, PSTR("%2d:%02d"), (useAMPM ? AmPmHour : hourCurrent), minuteCurrent); draw2x2String(2, lineHeight*2, lineBuffer); //draw hour, min. blink ":" depending on odd/even seconds - if (useAMPM) drawString(12, lineHeight*2, (isitAM ? "AM" : "PM"), true); //draw am/pm if using 12 time + if (useAMPM) drawString(12, lineHeight*2 + (lineHeight-1), (isitAM ? "AM" : "PM"), true); //draw am/pm if using 12 time drawStatusIcons(); //icons power, wifi, timer, etc + if (lineHeight > 1) { // WLEDMM use extra space for useful information + #if defined(WLED_DEBUG) || defined(SR_DEBUG) || defined(SR_STATS) + snprintf_P(lineBuffer, LINE_BUFFER_SIZE, PSTR(" %-3.3s %-2.2s "), driverSPI? "SPI" : "I2C", driverHW? "hw" : "sw"); // WLEDMM driver info + #else + strncpy_P(lineBuffer, PSTR(" "), LINE_BUFFER_SIZE); + #endif + if (apActive) strncpy_P(lineBuffer, PSTR(" AP mode "), LINE_BUFFER_SIZE); + else if (!WLED_CONNECTED) strncpy_P(lineBuffer, PSTR(" NO NET "), LINE_BUFFER_SIZE); + if (WLED_MQTT_CONNECTED) lineBuffer[9] = 'M'; // "MQTT" + if (realtimeMode && !realtimeOverride) lineBuffer[10] = 'X'; // "eXternal control" + //if (transitionActive) lineBuffer[11] = 'T'; + //if (stateChanged) lineBuffer[12] = 'C'; + drawString(1, 0, lineBuffer, false); + } + knownMinute = minuteCurrent; knownHour = hourCurrent; } if (showSeconds && secondCurrent != lastSecond) { lastSecond = secondCurrent; draw2x2String(6, lineHeight*2, secondCurrent%2 ? " " : ":"); - sprintf_P(lineBuffer, PSTR("%02d"), secondCurrent); - drawString(12, lineHeight*2+1, lineBuffer, true); // even with double sized rows print seconds in 1 line + snprintf_P(lineBuffer, LINE_BUFFER_SIZE, PSTR("%02d"), secondCurrent); + if (useAMPM) + drawString(12, lineHeight*2+1 + (lineHeight-1), lineBuffer, true); // even with double sized rows print seconds in 1 line // WLEDMM move it a bit lower + else + drawString(12, lineHeight*2+1, lineBuffer, true); // even with double sized rows print seconds in 1 line } drawing = false; } @@ -325,16 +416,26 @@ class FourLineDisplayUsermod : public Usermod { // gets called once at boot. Do all initialization that doesn't depend on // network here void setup() { - if (!typeOK || !enabled) return; + if (!enabled) return; // typeOK = true will be set after successful setup - bool isHW, isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64); + bool isHW, isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64 || type > 7); PinOwner po = PinOwner::UM_FourLineDisplay; if (isSPI) { if (ioPin[0] < 0 || ioPin[1] < 0) { ioPin[0] = spi_sclk; ioPin[1] = spi_mosi; + } else { + if ((spi_sclk < 0) && (spi_mosi < 0)) { // WLEDMM UM pins are valid, but global = -1 --> copy pins to "global" + spi_sclk = ioPin[0]; + spi_mosi = ioPin[1]; + } } + if ((ioPin[0] < 0 || ioPin[1] < 0) && (spi_sclk < 0 || spi_mosi < 0)) { // invalid pins, or "use global" and global pins not defined + typeOK=false; strcpy(errorMessage, PSTR("SPI No Pins defined")); return; } //WLEDMM bugfix - ensure that "final" GPIO are valid + isHW = (ioPin[0]==spi_sclk && ioPin[1]==spi_mosi); + if ((ioPin[0] == -1) || (ioPin[1] == -1)) isHW = true; // WLEDMM "use global" = hardware + if ((spi_sclk <0) || (spi_mosi < 0)) isHW = false; // no global pins - use software emulation PinManagerPinType cspins[3] = { { ioPin[2], true }, { ioPin[3], true }, { ioPin[4], true } }; if (!pinManager.allocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay)) { typeOK=false; strcpy(errorMessage, PSTR("SPI3 alloc pins failed")); return; } if (isHW) po = PinOwner::HW_SPI; // allow multiple allocations of HW I2C bus pins @@ -345,25 +446,36 @@ class FourLineDisplayUsermod : public Usermod { strcpy(errorMessage, PSTR("SPI2 alloc pins failed")); return; } + // start SPI now! +#ifdef ARDUINO_ARCH_ESP32 + if (isHW) SPI.begin(spi_sclk, spi_miso, spi_mosi); // ESP32 - will silently fail if SPI alread active. +#else + if (isHW) SPI.begin(); // ESP8266 - SPI pins are fixed +#endif + } else { - if (ioPin[0] < 0 || ioPin[1] < 0) { - ioPin[0] = i2c_scl; - ioPin[1] = i2c_sda; - } + //if (ioPin[0] < 0 || ioPin[1] < 0) { //WLEDMM do _not_ copy global pins !! + // ioPin[0] = i2c_scl; + // ioPin[1] = i2c_sda; + //} isHW = (ioPin[0]==i2c_scl && ioPin[1]==i2c_sda); + if ((ioPin[0] == -1) || (ioPin[1] == -1)) isHW = true; // WLEDMM "use global" = hardware // isHW = true; if (isHW) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins PinManagerPinType pins[2] = { {ioPin[0], true }, { ioPin[1], true } }; - if (ioPin[0] < 0 || ioPin[1] < 0) { typeOK=false; strcpy(errorMessage, PSTR("I2C No Pins defined")); return; } //WLEDMM bugfix - ensure that "final" GPIO are valid + if ((ioPin[0] < 0 || ioPin[1] < 0) && (i2c_scl < 0 || i2c_sda < 0)) { // invalid pins, or "use global" and global pins not defined + typeOK=false; strcpy(errorMessage, PSTR("I2C No Pins defined")); return; } //WLEDMM bugfix - ensure that "final" GPIO are valid if (isHW) { - if (!pinManager.joinWire(ioPin[1], ioPin[0])) { typeOK=false; strcpy(errorMessage, PSTR("I2C init failed")); return; } // WLEDMM join the HW bus + if (!pinManager.joinWire(i2c_sda, i2c_scl)) { typeOK=false; strcpy(errorMessage, PSTR("I2C HW init failed")); return; } // WLEDMM join the HW bus } else { if (!pinManager.allocateMultiplePins(pins, 2, po)) { typeOK=false; strcpy(errorMessage, PSTR("I2C Alloc pins failed")); return; } // WLEDMM use software bus } } + driverHW = isHW; + driverSPI= isSPI; DEBUG_PRINTLN(F("Allocating display.")); /* // At some point it may be good to not new/delete U8X8 object but use this instead @@ -435,29 +547,57 @@ class FourLineDisplayUsermod : public Usermod { if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]); else u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset break; + case SSD1309_SPI64: + // u8x8 uses global SPI variable that is attached to VSPI bus on ESP32 + if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1309_128X64_NONAME0_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]); + else u8x8 = (U8X8 *) new U8X8_SSD1309_128X64_NONAME0_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset + break; + case SSD1327_SPI128: + // u8x8 uses global SPI variable that is attached to VSPI bus on ESP32 + if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1327_WS_128X128_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]); + else u8x8 = (U8X8 *) new U8X8_SSD1327_WS_128X128_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset + break; default: u8x8 = nullptr; } if (nullptr == u8x8) { USER_PRINTLN(F("Display init failed.")); - pinManager.deallocateMultiplePins((const uint8_t*)ioPin, isSPI ? 5 : 2, po); + if (!isHW || !isSPI) pinManager.deallocateMultiplePins((const uint8_t*)ioPin, isSPI ? 5 : 2, po); // WLEDMM do not de-alloc global pins typeOK=false; strcpy(errorMessage, PSTR("Display init failed")); return; } lineHeight = u8x8->getRows() > 4 ? 2 : 1; - USER_PRINTLN(F("Starting display.")); + if (u8x8->getRows() > 8) lineHeight =3; + //if (u8x8->getRows() > 12) lineHeight =4; + if (isSPI) { + USER_PRINTLN(isHW ? F("Starting display (SPI HW).") : F("Starting display (SPI Soft).")); + } else { + USER_PRINTLN(isHW ? F("Starting display (I2C HW).") : F("Starting display (I2C Soft).")); + } u8x8->setBusClock(ioFrequency); // can be used for SPI too u8x8->begin(); + typeOK = true; + + reDrawing = false; + drawing = true; setFlipMode(flip); setVcomh(contrastFix); setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255 setPowerSave(0); + drawing = false; + + // init semaphores to allow drawing + FLD_SemaphoreGive(drawMux); + FLD_SemaphoreGive(drawMuxBig); + + onUpdateBegin(false); // create Display task // WLEDMM bugfix: before drawing anything + delay(200); + //drawString(0, 0, "Loading..."); overlayLogo(3500); - onUpdateBegin(false); // create Display task initDone = true; } @@ -473,12 +613,17 @@ class FourLineDisplayUsermod : public Usermod { * Da loop. */ void loop() { - #ifndef ARDUINO_ARCH_ESP32 - if (!enabled || !typeOK || strip.isUpdating()) return; + #if !defined(ARDUINO_ARCH_ESP32) || !defined(FLD_ESP32_USE_THREADS) + static unsigned long lastRunTime = 0; unsigned long now = millis(); + if (!enabled || !typeOK || (strip.isUpdating() && (now - lastRunTime < 50))) return; + lastRunTime = now; + if (now < nextUpdate) return; nextUpdate = now + ((displayTurnedOff && clockMode && showSeconds) ? 1000 : refreshRate); + reDrawing = true; redraw(false); + reDrawing = false; #endif } @@ -487,15 +632,48 @@ class FourLineDisplayUsermod : public Usermod { lastRedraw = millis(); } + //function to to check if a redraw or overlay draw is active. Needed for UM Rotary, to avoid random/concurrent drawing + bool canDraw(void) { + if (!typeOK || !enabled || !initDone) return(false); // WLEDMM make sure the display is initialized before we try to draw on it + #if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS) // only necessary on ESP32 + if (drawing) return(false); // overlay draws someting + if (reDrawing) return(false); // redraw task draws something + #endif + return(true); + } + /** * Redraw the screen (but only if things have changed * or if forceRedraw). */ void redraw(bool forceRedraw) { + #if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS) + // use a wrapper onESP32, to ensure the functions is not running several times in parallel ! + static bool doForceRedraw = false; // for delaying "force redraw" + + if ((overlayUntil > 0) && (millis() >= overlayUntil)) { + forceRedraw = true; // Time to display the overlay has elapsed, force redraw needed + } + + if (forceRedraw) doForceRedraw = true; + if (reDrawing) return; // redraw already active + if (drawing) return; // overlay draw active + + reDrawing = true; // set redraw lock + if (doForceRedraw) forceRedraw = true; + redraw_core(forceRedraw); + if (overlayUntil == 0) doForceRedraw = false; // redraw was skipped if overlay is still visible + reDrawing = false; // reset activity flag, as redraw has too many early returns that don't take care of this + } + + void redraw_core(bool forceRedraw) { +#endif bool needRedraw = false; unsigned long now = millis(); + + if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it + if (nullptr == u8x8) return; // prevent crash in case u8x8 is re-initialized (du to user changing setings) - if (!typeOK || !enabled) return; if (overlayUntil > 0) { if (now >= overlayUntil) { // Time to display the overlay has elapsed. @@ -564,7 +742,7 @@ class FourLineDisplayUsermod : public Usermod { // and turn it back on if it changed. clear(); sleepOrClock(true); - } else if (displayTurnedOff && ntpEnabled) { + } else if (displayTurnedOff) { // WLEDMM removed "&& ntpEnabled" showTime(); } return; @@ -584,6 +762,8 @@ class FourLineDisplayUsermod : public Usermod { knownnightlight = nightlightActive; wificonnected = interfacesInited; + while (drawing && millis()-now < 150) delay(8); // wait if someone else is drawing + // Do the actual drawing // First row: Icons draw2x2GlyphIcons(); @@ -607,7 +787,7 @@ class FourLineDisplayUsermod : public Usermod { if (overlayUntil == 0) { brightness100 = ((uint16_t)bri*100)/255; char lineBuffer[4]; - sprintf_P(lineBuffer, PSTR("%-3d"), brightness100); + snprintf_P(lineBuffer, 4, PSTR("%-3d"), brightness100); drawString(1, lineHeight, lineBuffer); //lastRedraw = millis(); } @@ -618,7 +798,7 @@ class FourLineDisplayUsermod : public Usermod { if (overlayUntil == 0) { fxspeed100 = ((uint16_t)effectSpeed*100)/255; char lineBuffer[4]; - sprintf_P(lineBuffer, PSTR("%-3d"), fxspeed100); + snprintf_P(lineBuffer, 4, PSTR("%-3d"), fxspeed100); drawString(5, lineHeight, lineBuffer); //lastRedraw = millis(); } @@ -629,7 +809,7 @@ class FourLineDisplayUsermod : public Usermod { if (overlayUntil == 0) { fxintensity100 = ((uint16_t)effectIntensity*100)/255; char lineBuffer[4]; - sprintf_P(lineBuffer, PSTR("%-3d"), fxintensity100); + snprintf_P(lineBuffer, 4, PSTR("%-3d"), fxintensity100); drawString(9, lineHeight, lineBuffer); //lastRedraw = millis(); } @@ -639,10 +819,10 @@ class FourLineDisplayUsermod : public Usermod { uint8_t col = 15; uint8_t row = 0; drawGlyph(col, row, (wificonnected ? 20 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // wifi icon - if (lineHeight==2) { col--; } else { row++; } + if (lineHeight>1) { col--; } else { row++; } drawGlyph(col, row, (bri > 0 ? 9 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // power icon - if (lineHeight==2) { col--; } else { col = row = 0; } - drawGlyph(col, row, (nightlightActive ? 6 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // moon icon for nighlight mode + if (lineHeight>1) { col--; } else { col = row = 0; } + drawGlyph(col, row, (nightlightActive ? 6 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // moon icon for nightlight mode } /** @@ -655,7 +835,7 @@ class FourLineDisplayUsermod : public Usermod { markColNum = newMarkColNum; } - //Draw the arrow for the current setting beiong changed + //Draw the arrow for the current setting being changed void drawArrow() { if (markColNum != 255 && markLineNum !=255) drawGlyph(markColNum, markLineNum*lineHeight, 21, u8x8_4LineDisplay_WLED_icons_1x1); } @@ -663,10 +843,14 @@ class FourLineDisplayUsermod : public Usermod { //Display the current effect or palette (desiredEntry) // on the appropriate line (row). void showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row) { - char lineBuffer[MAX_JSON_CHARS]; + char lineBuffer[MAX_JSON_CHARS] = { '\0' }; + if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it + if (FLD_SemaphoreTake(drawMuxBig, maxWaitLong) != pdTRUE) return; // WLEDMM acquire BIG draw mutex + if (overlayUntil == 0) { // Find the mode name in JSON uint8_t printedChars = extractModeName(inputEffPal, qstring, lineBuffer, MAX_JSON_CHARS-1); + if (printedChars < 2) strcpy(lineBuffer, "invalid"); // catch possible error if (lineBuffer[0]=='*' && lineBuffer[1]==' ') { // remove "* " from dynamic palettes for (byte i=2; i<=printedChars; i++) lineBuffer[i-2] = lineBuffer[i]; //include '\0' @@ -676,9 +860,9 @@ class FourLineDisplayUsermod : public Usermod { for (byte i=5; i<=printedChars; i++) lineBuffer[i-5] = lineBuffer[i]; //include '\0' printedChars -= 5; } - if (lineHeight == 2) { // use this code for 8 line display - char smallBuffer1[MAX_MODE_LINE_SPACE]; - char smallBuffer2[MAX_MODE_LINE_SPACE]; + if (lineHeight > 1) { // use this code for 8 line display + char smallBuffer1[MAX_MODE_LINE_SPACE+1] = { '\0' }; + char smallBuffer2[MAX_MODE_LINE_SPACE+1] = { '\0' }; uint8_t smallChars1 = 0; uint8_t smallChars2 = 0; if (printedChars < MAX_MODE_LINE_SPACE) { // use big font if the text fits @@ -705,19 +889,23 @@ class FourLineDisplayUsermod : public Usermod { } while (smallChars1 < (MAX_MODE_LINE_SPACE-1)) smallBuffer1[smallChars1++]=' '; smallBuffer1[smallChars1] = 0; + smallBuffer1[MAX_MODE_LINE_SPACE -1] = '\0'; // ensure the string ends where it should (while loop above can overshoot by 1) drawString(1, row*lineHeight, smallBuffer1, true); while (smallChars2 < (MAX_MODE_LINE_SPACE-1)) smallBuffer2[smallChars2++]=' '; smallBuffer2[smallChars2] = 0; + smallBuffer2[MAX_MODE_LINE_SPACE -1] = '\0'; // ensure the string ends where it should (while loop above can overshoot by 1) drawString(1, row*lineHeight+1, smallBuffer2, true); } } else { // use this code for 4 ling displays - char smallBuffer3[MAX_MODE_LINE_SPACE+1]; // uses 1x1 icon for mode/palette + char smallBuffer3[MAX_MODE_LINE_SPACE+1] = {'\0'}; // uses 1x1 icon for mode/palette uint8_t smallChars3 = 0; for (uint8_t i = 0; i < MAX_MODE_LINE_SPACE; i++) smallBuffer3[smallChars3++] = (i >= printedChars) ? ' ' : lineBuffer[i]; smallBuffer3[smallChars3] = 0; drawString(1, row*lineHeight, smallBuffer3, true); } } + + FLD_SemaphoreGive(drawMuxBig); // WLEDMM release BIG draw mutex } /** @@ -728,6 +916,7 @@ class FourLineDisplayUsermod : public Usermod { */ bool wakeDisplay() { if (!typeOK || !enabled) return false; + if (!initDone) return false; if (displayTurnedOff) { unsigned long now = millis(); while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing @@ -748,14 +937,17 @@ class FourLineDisplayUsermod : public Usermod { * Used in Rotary Encoder usermod. */ void overlay(const char* line1, long showHowLong, byte glyphType) { + if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it + if (!initDone) return; // WLEDMM bugfix unsigned long now = millis(); while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing + while ((reDrawing && overlayUntil == 0) && (millis()-now < 250)) delay(10); // wait if someone else is re-drawing drawing = true; // Turn the display back on if (!wakeDisplay()) clear(); // Print the overlay if (glyphType>0 && glyphType<255) { - if (lineHeight == 2) drawGlyph(5, 0, glyphType, u8x8_4LineDisplay_WLED_icons_6x6, true); // use 3x3 font with draw2x2Glyph() if flash runs short and comment out 6x6 font + if (lineHeight > 1) drawGlyph(5, 0, glyphType, u8x8_4LineDisplay_WLED_icons_6x6, true); // use 3x3 font with draw2x2Glyph() if flash runs short and comment out 6x6 font else drawGlyph(6, 0, glyphType, u8x8_4LineDisplay_WLED_icons_3x3, true); } if (line1) { @@ -772,13 +964,14 @@ class FourLineDisplayUsermod : public Usermod { * Clears the screen and prints. */ void overlayLogo(long showHowLong) { + if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it unsigned long now = millis(); while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing drawing = true; // Turn the display back on if (!wakeDisplay()) clear(); // Print the overlay - if (lineHeight == 2) { + if (lineHeight > 1) { //add a bit of randomness switch (millis()%3) { case 0: @@ -833,8 +1026,11 @@ class FourLineDisplayUsermod : public Usermod { * Used in Auto Save usermod */ void overlay(const char* line1, const char* line2, long showHowLong) { + if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it + if (!initDone) return; // WLEDMM bugfix unsigned long now = millis(); while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing + while ((reDrawing && overlayUntil == 0) && (millis()-now < 250)) delay(10); // wait if someone else is re-drawing drawing = true; // Turn the display back on if (!wakeDisplay()) clear(); @@ -854,6 +1050,8 @@ class FourLineDisplayUsermod : public Usermod { } void networkOverlay(const char* line1, long showHowLong) { + if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it + if (!initDone) return; // WLEDMM bugfix unsigned long now = millis(); while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing drawing = true; @@ -895,10 +1093,11 @@ class FourLineDisplayUsermod : public Usermod { /** * Enable sleep (turn the display off) or clock mode. */ - void sleepOrClock(bool enabled) { - if (enabled) { + void sleepOrClock(bool sleepEnable) { + if (sleepEnable) { displayTurnedOff = true; - if (clockMode && ntpEnabled) { + //setContrast(contrastFix? 2+ contrast/4 : 0); // un-comment to dim display in "clock mode" + if (clockMode) { // WLEDMM removed " && ntpEnabled" knownMinute = knownHour = 99; showTime(); } else @@ -906,6 +1105,7 @@ class FourLineDisplayUsermod : public Usermod { } else { displayTurnedOff = false; setPowerSave(0); + //setContrast(contrast); // un-comment to restore display brightness on wakeup } } @@ -991,7 +1191,7 @@ class FourLineDisplayUsermod : public Usermod { #endif #endif void onUpdateBegin(bool init) { - #ifdef ARDUINO_ARCH_ESP32 + #if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS) if (init && Display_Task) { vTaskSuspend(Display_Task); // update is about to begin, disable task to prevent crash } else { @@ -999,20 +1199,22 @@ class FourLineDisplayUsermod : public Usermod { if (Display_Task) vTaskResume(Display_Task); else - xTaskCreatePinnedToCore( + xTaskCreateUniversal( // this is guaranteed to work on any ESP32 (single or dual core) [](void * par) { // Function to implement the task // see https://www.freertos.org/vtaskdelayuntil.html - const TickType_t xFrequency = REFRESH_RATE_MS * portTICK_PERIOD_MS / 2; + const TickType_t xFrequency = REFRESH_RATE_MS * portTICK_PERIOD_MS / 2; TickType_t xLastWakeTime = xTaskGetTickCount(); for(;;) { delay(1); // DO NOT DELETE THIS LINE! It is needed to give the IDLE(0) task enough time and to keep the watchdog happy. // taskYIELD(), yield(), vTaskDelay() and esp_task_wdt_feed() didn't seem to work. - vTaskDelayUntil(&xLastWakeTime, xFrequency); // release CPU, by doing nothing for REFRESH_RATE_MS millis + xLastWakeTime = xTaskGetTickCount(); // workaround for vTaskDelayUntil bug: it does not always keep the last time so we refresh it explicitly FourLineDisplayUsermod::getInstance()->redraw(false); + vTaskDelayUntil(&xLastWakeTime, xFrequency); // release CPU, by doing nothing until next REFRESH_RATE_MS millis } }, "4LD", // Name of the task - 3072, // Stack size in words +// 3072, // Stack size in words + 4096, // bigger Stack size in words NULL, // Task input parameter 1, // Priority of the task (not idle) &Display_Task, // Task handle @@ -1061,30 +1263,47 @@ class FourLineDisplayUsermod : public Usermod { oappend(SET_F("addOption(dd,'SSD1305 128x64',5);")); oappend(SET_F("addOption(dd,'SSD1306 SPI',6);")); oappend(SET_F("addOption(dd,'SSD1306 SPI 128x64',7);")); - bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64); + oappend(SET_F("addOption(dd,'SSD1309 SPI 128x64',8);")); + oappend(SET_F("addOption(dd,'SSD1327 SPI 128x128',9);")); + bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64 || type > 7); // WLEDMM add defaults oappend(SET_F("addInfo('4LineDisplay:pin[]',0,'','I2C/SPI CLK');")); - oappend(SET_F("disableRO('4LineDisplay:pin[]',0);")); - #ifdef FLD_PIN_SCL - oappend(SET_F("xOption('4LineDisplay:pin[]',0,' ⎌',")); oappendi(FLD_PIN_SCL); oappend(");"); - #endif + oappend(SET_F("dRO('4LineDisplay:pin[]',0);")); // disable read only pins if (isSPI) { - oappend(SET_F("rOption('4LineDisplay:pin[]',0,'use global (")); oappendi(spi_sclk); oappend(")',-1);"); + oappend(SET_F("rOpt('4LineDisplay:pin[]',0,'use global (")); oappendi(spi_sclk); oappend(")',-1);"); + #ifdef FLD_PIN_CLOCKSPI + oappend(SET_F("xOpt('4LineDisplay:pin[]',0,' ⎌',")); oappendi(FLD_PIN_CLOCKSPI); oappend(");"); + #endif } else { - oappend(SET_F("rOption('4LineDisplay:pin[]',0,'use global (")); oappendi(i2c_scl); oappend(")',-1);"); + oappend(SET_F("rOpt('4LineDisplay:pin[]',0,'use global (")); oappendi(i2c_scl); oappend(")',-1);"); + #ifdef FLD_PIN_SCL + oappend(SET_F("xOpt('4LineDisplay:pin[]',0,' ⎌',")); oappendi(FLD_PIN_SCL); oappend(");"); + #endif } oappend(SET_F("addInfo('4LineDisplay:pin[]',1,'','I2C/SPI DTA');")); - #ifdef FLD_PIN_SDA - oappend(SET_F("xOption('4LineDisplay:pin[]',1,' ⎌',")); oappendi(FLD_PIN_SDA); oappend(");"); - #endif if (isSPI) { - oappend(SET_F("rOption('4LineDisplay:pin[]',1,'use global (")); oappendi(spi_mosi); oappend(")',-1);"); + oappend(SET_F("rOpt('4LineDisplay:pin[]',1,'use global (")); oappendi(spi_mosi); oappend(")',-1);"); + #ifdef FLD_PIN_MOSISPI + oappend(SET_F("xOpt('4LineDisplay:pin[]',1,' ⎌',")); oappendi(FLD_PIN_MOSISPI); oappend(");"); + #endif } else { - oappend(SET_F("rOption('4LineDisplay:pin[]',1,'use global (")); oappendi(i2c_sda); oappend(")',-1);"); + oappend(SET_F("rOpt('4LineDisplay:pin[]',1,'use global (")); oappendi(i2c_sda); oappend(")',-1);"); + #ifdef FLD_PIN_SDA + oappend(SET_F("xOpt('4LineDisplay:pin[]',1,' ⎌',")); oappendi(FLD_PIN_SDA); oappend(");"); + #endif } oappend(SET_F("addInfo('4LineDisplay:pin[]',2,'','SPI CS');")); + #ifdef FLD_PIN_CS + oappend(SET_F("xOpt('4LineDisplay:pin[]',2,' ⎌',")); oappendi(FLD_PIN_CS); oappend(");"); + #endif oappend(SET_F("addInfo('4LineDisplay:pin[]',3,'','SPI DC');")); + #ifdef FLD_PIN_DC + oappend(SET_F("xOpt('4LineDisplay:pin[]',3,' ⎌',")); oappendi(FLD_PIN_DC); oappend(");"); + #endif oappend(SET_F("addInfo('4LineDisplay:pin[]',4,'','SPI RST');")); + #ifdef FLD_PIN_RESET + oappend(SET_F("xOpt('4LineDisplay:pin[]',4,' ⎌',")); oappendi(FLD_PIN_RESET); oappend(");"); + #endif //WLEDMM add errorMessage to um settings if (strcmp(errorMessage, "") != 0) { @@ -1109,7 +1328,7 @@ class FourLineDisplayUsermod : public Usermod { void addToConfig(JsonObject& root) { // determine if we are using global HW pins (data & clock) int8_t hw_dta, hw_clk; - if ((type == SSD1306_SPI || type == SSD1306_SPI64)) { + if ((type == SSD1306_SPI || type == SSD1306_SPI64) || (type > 7)) { hw_clk = spi_sclk; hw_dta = spi_mosi; } else { @@ -1130,7 +1349,7 @@ class FourLineDisplayUsermod : public Usermod { top[FPSTR(_flip)] = (bool) flip; top[FPSTR(_contrast)] = contrast; top[FPSTR(_contrastFix)] = (bool) contrastFix; - #ifndef ARDUINO_ARCH_ESP32 + #if !defined(ARDUINO_ARCH_ESP32) || !defined(FLD_ESP32_USE_THREADS) top[FPSTR(_refreshRate)] = refreshRate; #endif top[FPSTR(_screenTimeOut)] = screenTimeout/1000; @@ -1166,7 +1385,7 @@ class FourLineDisplayUsermod : public Usermod { for (byte i=0; i<5; i++) ioPin[i] = top["pin"][i] | ioPin[i]; flip = top[FPSTR(_flip)] | flip; contrast = top[FPSTR(_contrast)] | contrast; - #ifndef ARDUINO_ARCH_ESP32 + #if !defined(ARDUINO_ARCH_ESP32) || !defined(FLD_ESP32_USE_THREADS) refreshRate = top[FPSTR(_refreshRate)] | refreshRate; refreshRate = min(5000, max(250, (int)refreshRate)); #endif @@ -1175,7 +1394,7 @@ class FourLineDisplayUsermod : public Usermod { clockMode = top[FPSTR(_clockMode)] | clockMode; showSeconds = top[FPSTR(_showSeconds)] | showSeconds; contrastFix = top[FPSTR(_contrastFix)] | contrastFix; - if (newType == SSD1306_SPI || newType == SSD1306_SPI64) + if (newType == SSD1306_SPI || newType == SSD1306_SPI64 || newType > 7) ioFrequency = min(20000, max(500, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency else ioFrequency = min(3400, max(100, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency @@ -1190,27 +1409,44 @@ class FourLineDisplayUsermod : public Usermod { // changing parameters from settings page bool pinsChanged = false; for (byte i=0; i<5; i++) if (ioPin[i] != oldPin[i]) { pinsChanged = true; break; } + #if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS) + unsigned long now = millis(); + while ((drawing || reDrawing) && millis()-now < 300) delay(10); // wait if someone else is drawing + #endif + drawing = false; + reDrawing = false; + if (pinsChanged || type!=newType) { - if (typeOK) delete u8x8; + if (typeOK) { + typeOK = false; + if (u8x8 != nullptr) delete u8x8; //WLEDMM warning: deleting object of polymorphic class type 'U8X8' which has non-virtual destructor might cause undefined behaviour [-Wdelete-non-virtual-dtor] + u8x8 = nullptr; + USER_PRINTLN(F("Display terminated.")); + } PinOwner po = PinOwner::UM_FourLineDisplay; - bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64); + bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64 || type > 7); if (isSPI) { pinManager.deallocateMultiplePins((const uint8_t *)(&oldPin[2]), 3, po); bool isHW = (oldPin[0]==spi_sclk && oldPin[1]==spi_mosi); + if (oldPin[0]==-1 && oldPin[1]==-1) isHW = true; // WLEDMM "use global" means hardware driver + if (spi_sclk==-1 && spi_mosi==-1) isHW = false; // WLEDMM global pins not set -> software driver if (isHW) po = PinOwner::HW_SPI; } else { - bool isHW = (oldPin[0]==i2c_scl && oldPin[1]==i2c_sda); - if (isHW) po = PinOwner::HW_I2C; + //bool isHW = (oldPin[0]==i2c_scl && oldPin[1]==i2c_sda); + //if ((ioPin[0] == -1) || (ioPin[1] == -1)) isHW = true; // WLEDMM "use global" = hardware + //if (isHW) po = PinOwner::HW_I2C; // WLEDMM don't try to de-alloc HW pins. } pinManager.deallocateMultiplePins((const uint8_t *)oldPin, 2, po); type = newType; setup(); needsRedraw |= true; } else { - u8x8->setBusClock(ioFrequency); // can be used for SPI too - setVcomh(contrastFix); - setContrast(contrast); - setFlipMode(flip); + if (enabled && typeOK && (nullptr != u8x8)) { // WLEDMM ensure we have a valid, active driver + u8x8->setBusClock(ioFrequency); // can be used for SPI too + setVcomh(contrastFix); + setContrast(contrast); + setFlipMode(flip); + } } knownHour = 99; if (needsRedraw && !wakeDisplay()) redraw(true); @@ -1245,3 +1481,8 @@ const char FourLineDisplayUsermod::_contrastFix[] PROGMEM = "contrastFix"; #ifdef ARDUINO_ARCH_ESP32 FourLineDisplayUsermod *FourLineDisplayUsermod::instance = nullptr; #endif + +// WLEDMM clean up some macros, so they don't cause problems in other usermods +#undef FLD_SemaphoreTake +#undef FLD_SemaphoreGive + diff --git a/usermods/usermod_v2_games/usermod_v2_games.h b/usermods/usermod_v2_games/usermod_v2_games.h index 0818ef28..1b1f9047 100644 --- a/usermods/usermod_v2_games/usermod_v2_games.h +++ b/usermods/usermod_v2_games/usermod_v2_games.h @@ -172,7 +172,7 @@ uint16_t mode_pongGame(void) { return FRAMETIME; } -static const char _data_FX_MODE_PONGGAME[] PROGMEM = "🎮 Pong@!;!;!;2"; +static const char _data_FX_MODE_PONGGAME[] PROGMEM = "🎮 Pong ☾@!;!;!;2"; //https://howtomechatronics.com/tutorials/arduino/arduino-and-mpu6050-accelerometer-and-gyroscope-tutorial/ @@ -183,7 +183,7 @@ uint16_t mode_IMUTest(void) { uint8_t y = 0; - if (IMU != nullptr) { + if ((IMU != nullptr) && (IMU->dmpReady)) { SEGMENT.setPixelColorXY(SEGMENT.virtualWidth() * (IMU->aa.x+INT16_MAX)/(2*INT16_MAX), y+=1, BLUE); SEGMENT.setPixelColorXY(SEGMENT.virtualWidth() * (IMU->aa.y+INT16_MAX)/(2*INT16_MAX), y+=1, BLUE); SEGMENT.setPixelColorXY(SEGMENT.virtualWidth() * (IMU->aa.z+INT16_MAX)/(2*INT16_MAX), y+=1, BLUE); @@ -207,7 +207,7 @@ uint16_t mode_IMUTest(void) { return FRAMETIME; } -static const char _data_FX_MODE_IMUTest[] PROGMEM = "🎮 IMUTest@;;;2d"; +static const char _data_FX_MODE_IMUTest[] PROGMEM = "🎮 IMUTest ☾@;;;2d"; #endif @@ -280,7 +280,7 @@ uint16_t mode_3DIMUCube(void) { float roll = 0; #ifdef USERMOD_MPU6050_IMU - if (IMU != nullptr) { + if ((IMU != nullptr) && (IMU->dmpReady)) { yaw = -IMU->ypr[0]; pitch = IMU->ypr[1]; roll = IMU->ypr[2]; @@ -319,7 +319,7 @@ uint16_t mode_3DIMUCube(void) { return FRAMETIME; } -static const char _data_FX_MODE_3DIMUCube[] PROGMEM = "🎮 3DIMUCube@,Perspective;!;!;2;pal=1"; //random cycle +static const char _data_FX_MODE_3DIMUCube[] PROGMEM = "🎮 3DIMUCube ☾@,Perspective;!;!;2;pal=1"; //WLEDMM random smooth class GamesUsermod : public Usermod { private: @@ -330,9 +330,9 @@ class GamesUsermod : public Usermod { strip.addEffect(255, &mode_pongGame, _data_FX_MODE_PONGGAME); #ifdef USERMOD_MPU6050_IMU IMU = (MPU6050Driver *)usermods.lookup(USERMOD_ID_IMU); - // #ifdef WLED_DEBUG + #ifdef WLED_DEBUG strip.addEffect(255, &mode_IMUTest, _data_FX_MODE_IMUTest); - // #endif + #endif #endif strip.addEffect(255, &mode_3DIMUCube, _data_FX_MODE_3DIMUCube); //works also without IMU } diff --git a/usermods/usermod_v2_klipper_percentage/readme.md b/usermods/usermod_v2_klipper_percentage/readme.md new file mode 100644 index 00000000..0619bf85 --- /dev/null +++ b/usermods/usermod_v2_klipper_percentage/readme.md @@ -0,0 +1,40 @@ +# Klipper Percentage Usermod +This usermod polls the Klipper API every 10s for the progressvalue. +The leds are then filled with a solid color according to that progress percentage. +the solid color is the secondary color of the segment. + +A corresponding curl command would be: +``` +curl --location --request GET 'http://[]/printer/objects/query?virtual_sdcard=progress' +``` +## Usage +Compile the source with the buildflag `-D USERMOD_KLIPPER_PERCENTAGE` added. + +You can also use the WLBD bot in the Discord by simply extending an exsisting build enviroment: +``` +[env:esp32klipper] +extends = env:esp32dev +build_flags = ${common.build_flags_esp32} -D USERMOD_KLIPPER_PERCENTAGE +``` + +## Settings + +### Enabled: +Checkbox to enable or disable the overlay + +### Klipper IP: +IP adress of your Klipper instance you want to poll. ESP has to be restarted after change + +### Direction : +0 = normal + +1 = reversed + +2 = center + +----- +Author: + +Sören Willrodt + +Discord: Sören#5281 \ No newline at end of file diff --git a/usermods/usermod_v2_klipper_percentage/usermod_v2_klipper_percentage.h b/usermods/usermod_v2_klipper_percentage/usermod_v2_klipper_percentage.h new file mode 100644 index 00000000..0e19cc80 --- /dev/null +++ b/usermods/usermod_v2_klipper_percentage/usermod_v2_klipper_percentage.h @@ -0,0 +1,222 @@ +#pragma once + +#include "wled.h" + +class klipper_percentage : public Usermod +{ +private: + unsigned long lastTime = 0; + String ip = "192.168.25.207"; + WiFiClient wifiClient; + char errorMessage[100] = ""; + int printPercent = 0; + int direction = 0; // 0 for along the strip, 1 for reversed direction + + static const char _name[]; + static const char _enabled[]; + bool enabled = false; + + void httpGet(WiFiClient &client, char *errorMessage) + { + // https://arduinojson.org/v6/example/http-client/ + // is this the most compact way to do http get and put it in arduinojson object??? + // would like async response ... ??? + client.setTimeout(10000); + if (!client.connect(ip.c_str(), 80)) + { + strcat(errorMessage, PSTR("Connection failed")); + } + else + { + // Send HTTP request + client.println(F("GET /printer/objects/query?virtual_sdcard=progress HTTP/1.0")); + client.println("Host: " + ip); + client.println(F("Connection: close")); + if (client.println() == 0) + { + strcat(errorMessage, PSTR("Failed to send request")); + } + else + { + // Check HTTP status + char status[32] = {0}; + client.readBytesUntil('\r', status, sizeof(status)); + if (strcmp(status, "HTTP/1.1 200 OK") != 0) + { + strcat(errorMessage, PSTR("Unexpected response: ")); + strcat(errorMessage, status); + } + else + { + // Skip HTTP headers + char endOfHeaders[] = "\r\n\r\n"; + if (!client.find(endOfHeaders)) + { + strcat(errorMessage, PSTR("Invalid response")); + } + } + } + } + } + +public: + void setup() + { + } + + void connected() + { + } + + void loop() + { + if (enabled) + { + if (WLED_CONNECTED) + { + if (millis() - lastTime > 10000) + { + httpGet(wifiClient, errorMessage); + if (strcmp(errorMessage, "") == 0) + { + PSRAMDynamicJsonDocument klipperDoc(4096); // in practive about 2673 + DeserializationError error = deserializeJson(klipperDoc, wifiClient); + if (error) + { + strcat(errorMessage, PSTR("deserializeJson() failed: ")); + strcat(errorMessage, error.c_str()); + } + printPercent = (int)(klipperDoc["result"]["status"]["virtual_sdcard"]["progress"].as() * 100); + + DEBUG_PRINT("Percent: "); + DEBUG_PRINTLN((int)(klipperDoc["result"]["status"]["virtual_sdcard"]["progress"].as() * 100)); + DEBUG_PRINT("LEDs: "); + DEBUG_PRINTLN(direction == 2 ? (strip.getLengthTotal() / 2) * printPercent / 100 : strip.getLengthTotal() * printPercent / 100); + } + else + { + DEBUG_PRINTLN(errorMessage); + DEBUG_PRINTLN(ip); + } + lastTime = millis(); + } + } + } + } + + void addToConfig(JsonObject &root) + { + JsonObject top = root.createNestedObject("Klipper Printing Percentage"); + top["Enabled"] = enabled; + top["Klipper IP"] = ip; + top["Direction"] = direction; + } + + bool readFromConfig(JsonObject &root) + { + // default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor + // setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed) + + JsonObject top = root["Klipper Printing Percentage"]; + + bool configComplete = !top.isNull(); + configComplete &= getJsonValue(top["Klipper IP"], ip); + configComplete &= getJsonValue(top["Enabled"], enabled); + configComplete &= getJsonValue(top["Direction"], direction); + return configComplete; + } + + /* + * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API. + * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI. + * Below it is shown how this could be used for e.g. a light sensor + */ + void addToJsonInfo(JsonObject &root) + { + JsonObject user = root["u"]; + if (user.isNull()) + user = root.createNestedObject("u"); + + JsonArray infoArr = user.createNestedArray(FPSTR(_name)); + String uiDomString = F(""); + infoArr.add(uiDomString); + } + + void addToJsonState(JsonObject &root) + { + JsonObject usermod = root[FPSTR(_name)]; + if (usermod.isNull()) + { + usermod = root.createNestedObject(FPSTR(_name)); + } + usermod["on"] = enabled; + } + void readFromJsonState(JsonObject &root) + { + JsonObject usermod = root[FPSTR(_name)]; + if (!usermod.isNull()) + { + if (usermod[FPSTR(_enabled)].is()) + { + enabled = usermod[FPSTR(_enabled)].as(); + } + } + } + + /* + * handleOverlayDraw() is called just before every show() (LED strip update frame) after effects have set the colors. + * Use this to blank out some LEDs or set them to a different color regardless of the set effect mode. + * Commonly used for custom clocks (Cronixie, 7 segment) + */ + void handleOverlayDraw() + { + if (enabled) + { + if (direction == 0) // normal + { + for (int i = 0; i < strip.getLengthTotal() * printPercent / 100; i++) + { + strip.setPixelColor(i, strip.getSegment(0).colors[1]); + } + } + else if (direction == 1) // reversed + { + for (int i = 0; i < strip.getLengthTotal() * printPercent / 100; i++) + { + strip.setPixelColor(strip.getLengthTotal() - i, strip.getSegment(0).colors[1]); + } + } + else if (direction == 2) // center + { + for (int i = 0; i < (strip.getLengthTotal() / 2) * printPercent / 100; i++) + { + strip.setPixelColor((strip.getLengthTotal() / 2) + i, strip.getSegment(0).colors[1]); + strip.setPixelColor((strip.getLengthTotal() / 2) - i, strip.getSegment(0).colors[1]); + } + } + else + { + direction = 0; + } + } + } + + /* + * getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!). + * This could be used in the future for the system to determine whether your usermod is installed. + */ + uint16_t getId() + { + return USERMOD_ID_KLIPPER; + } +}; +const char klipper_percentage::_name[] PROGMEM = "Klipper_Percentage"; +const char klipper_percentage::_enabled[] PROGMEM = "enabled"; \ No newline at end of file diff --git a/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h b/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h index 6db990c7..96539658 100644 --- a/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h +++ b/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h @@ -348,8 +348,10 @@ public: findCurrentEffectAndPalette(); } - if (modes_alpha_indexes[effectCurrentIndex] != effectCurrent || palettes_alpha_indexes[effectPaletteIndex] != effectPalette) { - currentEffectAndPaletteInitialized = false; + if (modes_alpha_indexes != nullptr) { // WLEDMM bugfix + if (modes_alpha_indexes[effectCurrentIndex] != effectCurrent || palettes_alpha_indexes[effectPaletteIndex] != effectPalette) { + currentEffectAndPaletteInitialized = false; + } } if (currentTime - loopTime >= 2) // 2ms since last check of encoder = 500Hz @@ -383,7 +385,7 @@ public: buttonWaitTime = 0; char newState = select_state + 1; bool changedState = false; - char lineBuffer[64]; + char lineBuffer[64] = { '\0' }; do { // finde new state switch (newState) { @@ -469,11 +471,13 @@ public: void displayNetworkInfo() { #ifdef USERMOD_FOUR_LINE_DISPLAY + if (display != nullptr) display->networkOverlay(PSTR("NETWORK INFO"), 10000); #endif } void findCurrentEffectAndPalette() { + if (modes_alpha_indexes == nullptr) return; // WLEDMM bugfix currentEffectAndPaletteInitialized = true; for (uint8_t i = 0; i < strip.getModeCount(); i++) { if (modes_alpha_indexes[i] == effectCurrent) { @@ -510,7 +514,8 @@ public: // 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa //setValuesFromFirstSelectedSeg(); //to make transition work on main segment (should no longer be required) stateUpdated(CALL_MODE_BUTTON); - updateInterfaces(CALL_MODE_BUTTON); + if ((millis() - lastInterfaceUpdate) > INTERFACE_UPDATE_COOLDOWN) // WLEDMM respect cooldown times, to avoid crash in AsyncWebSocketMessageBuffer + updateInterfaces(CALL_MODE_BUTTON); } void changeBrightness(bool increase) { @@ -522,10 +527,14 @@ public: } display->updateRedrawTime(); #endif - bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 0); + byte lastBri = bri; + if (bri < 40) bri = max(min((increase ? bri+fadeAmount/2 : bri-fadeAmount/2), 255), 0); // WLEDMM slower steps when brightness < 16% + else bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 0); + if (lastBri != bri) stateChanged = true; // WLEDMM bugfix lampUdated(); #ifdef USERMOD_FOUR_LINE_DISPLAY - display->updateBrightness(); + if (display->canDraw()) // only draw if nothing else is drawing + display->updateBrightness(); #endif } @@ -540,7 +549,7 @@ public: display->updateRedrawTime(); #endif effectCurrentIndex = max(min((increase ? effectCurrentIndex+1 : effectCurrentIndex-1), strip.getModeCount()-1), 0); - effectCurrent = modes_alpha_indexes[effectCurrentIndex]; + if (modes_alpha_indexes != nullptr) effectCurrent = modes_alpha_indexes[effectCurrentIndex]; stateChanged = true; if (applyToAll) { for (byte i=0; ishowCurrentEffectOrPalette(effectCurrent, JSON_mode_names, 3); + if (display->canDraw()) // only draw if nothing else is drawing + display->showCurrentEffectOrPalette(effectCurrent, JSON_mode_names, 3); #endif } @@ -582,7 +592,8 @@ public: } lampUdated(); #ifdef USERMOD_FOUR_LINE_DISPLAY - display->updateSpeed(); + if (display->canDraw()) // only draw if nothing else is drawing + display->updateSpeed(); #endif } @@ -610,7 +621,8 @@ public: } lampUdated(); #ifdef USERMOD_FOUR_LINE_DISPLAY - display->updateIntensity(); + if (display->canDraw()) // only draw if nothing else is drawing + display->updateIntensity(); #endif } @@ -688,7 +700,8 @@ public: } lampUdated(); #ifdef USERMOD_FOUR_LINE_DISPLAY - display->showCurrentEffectOrPalette(effectPalette, JSON_palette_names, 2); + if (display->canDraw()) // only draw if nothing else is drawing + display->showCurrentEffectOrPalette(effectPalette, JSON_palette_names, 2); #endif } @@ -764,7 +777,7 @@ public: if (presetHigh && presetLow && presetHigh > presetLow) { StaticJsonDocument<64> root; char str[64] = { '\0' }; - sprintf_P(str, PSTR("%d~%d~%s"), presetLow, presetHigh, increase?"":"-"); + snprintf_P(str, 64, PSTR("%d~%d~%s"), presetLow, presetHigh, increase?"":"-"); root["ps"] = str; deserializeState(root.as(), CALL_MODE_BUTTON_PRESET); /* @@ -875,13 +888,13 @@ public: oappend(SET_F("addHB('Rotary-Encoder');")); #ifdef ENCODER_DT_PIN - oappend(SET_F("xOption('Rotary-Encoder:DT-pin',1,' ⎌',")); oappendi(ENCODER_DT_PIN); oappend(");"); + oappend(SET_F("xOpt('Rotary-Encoder:DT-pin',1,' ⎌',")); oappendi(ENCODER_DT_PIN); oappend(");"); #endif #ifdef ENCODER_CLK_PIN - oappend(SET_F("xOption('Rotary-Encoder:CLK-pin',1,' ⎌',")); oappendi(ENCODER_CLK_PIN); oappend(");"); + oappend(SET_F("xOpt('Rotary-Encoder:CLK-pin',1,' ⎌',")); oappendi(ENCODER_CLK_PIN); oappend(");"); #endif #ifdef ENCODER_SW_PIN - oappend(SET_F("xOption('Rotary-Encoder:SW-pin',1,' ⎌',")); oappendi(ENCODER_SW_PIN); oappend(");"); + oappend(SET_F("xOpt('Rotary-Encoder:SW-pin',1,' ⎌',")); oappendi(ENCODER_SW_PIN); oappend(");"); #endif } @@ -931,7 +944,7 @@ public: enabled = false; return true; } - setup(); + if (enabled) setup(); // WLEDMM no pin stealing! } } // use "return !top["newestParameter"].isNull();" when updating Usermod with new features diff --git a/usermods/usermod_v2_weather/usermod_v2_weather.h b/usermods/usermod_v2_weather/usermod_v2_weather.h index af9998fa..2ed00424 100644 --- a/usermods/usermod_v2_weather/usermod_v2_weather.h +++ b/usermods/usermod_v2_weather/usermod_v2_weather.h @@ -94,7 +94,7 @@ uint16_t mode_2DWeather(void) { return FRAMETIME; } -static const char _data_FX_MODE_2DWEATHER[] PROGMEM = "Weather@;!;!;2;pal=54"; //temperature palette +static const char _data_FX_MODE_2DWEATHER[] PROGMEM = "🌦Weather ☾@;!;!;2;pal=54"; //temperature palette //utility function, move somewhere else??? void epochToString(time_t time, char *timeString) { @@ -142,14 +142,13 @@ void httpGet(WiFiClient &client, const char *url, char *errorMessage) { class WeatherUsermod : public Usermod { private: // strings to reduce flash memory usage (used more than twice) - static const char _name[]; //usermod name String apiKey = ""; //config var - unsigned long lastTime = 0; //will be used to download new forecast every hour char errorMessage[100] = ""; bool isConnected = false; //only call openweathermap if connected public: + WeatherUsermod(const char *name, bool enabled):Usermod(name, enabled) {} //WLEDMM: this shouldn't be necessary (passthrough of constructor), maybe because Usermod is an abstract class void setup() { strip.addEffect(255, &mode_2DWeather, _data_FX_MODE_2DWEATHER); @@ -256,7 +255,6 @@ class WeatherUsermod : public Usermod { /* * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API. * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI. - * Below it is shown how this could be used for e.g. a light sensor */ void addToJsonInfo(JsonObject& root) { @@ -292,7 +290,8 @@ class WeatherUsermod : public Usermod { void addToConfig(JsonObject& root) { - JsonObject top = root.createNestedObject(FPSTR(_name)); + Usermod::addToConfig(root); + JsonObject top = root[FPSTR(_name)]; top[F("apiKey")] = apiKey; top[F("units")] = weather_units; top[F("minTemp")] = weather_minTemp; @@ -302,10 +301,9 @@ class WeatherUsermod : public Usermod { bool readFromConfig(JsonObject& root) { + bool configComplete = Usermod::readFromConfig(root); JsonObject top = root[FPSTR(_name)]; - bool configComplete = !top.isNull(); - configComplete &= getJsonValue(top[F("apiKey")], apiKey); configComplete &= getJsonValue(top[F("units")], weather_units); configComplete &= getJsonValue(top[F("minTemp")], weather_minTemp); @@ -317,8 +315,8 @@ class WeatherUsermod : public Usermod { void appendConfigData() { - oappend(SET_F("addInfo('Weather:help',0,'');")); - + oappend(SET_F("addHB('Weather');")); // WLEDMM + oappend(SET_F("dd=addDropdown('Weather','units');")); oappend(SET_F("addOption(dd,'Kelvin',0);")); oappend(SET_F("addOption(dd,'Celcius',1);")); @@ -349,9 +347,6 @@ class WeatherUsermod : public Usermod { } }; -// strings to reduce flash memory usage (used more than twice) -const char WeatherUsermod::_name[] PROGMEM = "Weather"; - // example openweathermap data // {"cod":"200","message":0,"cnt":40,"list":[ // {"dt":1663945200,"main":{"temp":18.05,"feels_like":17.79,"temp_min":17.64,"temp_max":18.05,"pressure":1014,"sea_level":1014,"grnd_level":1013,"humidity":72,"temp_kf":0.41},"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"clouds":{"all":100},"wind":{"speed":4.32,"deg":238,"gust":5.6},"visibility":10000,"pop":0.48,"rain":{"3h":0.15},"sys":{"pod":"d"},"dt_txt":"2022-09-23 15:00:00"}, diff --git a/usermods/usermod_v2_word_clock/usermod_v2_word_clock.h b/usermods/usermod_v2_word_clock/usermod_v2_word_clock.h index 1068cd96..c825a7af 100644 --- a/usermods/usermod_v2_word_clock/usermod_v2_word_clock.h +++ b/usermods/usermod_v2_word_clock/usermod_v2_word_clock.h @@ -25,6 +25,7 @@ class WordClockUsermod : public Usermod bool displayItIs = false; int ledOffset = 100; bool meander = false; + bool nord = false; // defines for mask sizes #define maskSizeLeds 114 @@ -37,7 +38,7 @@ class WordClockUsermod : public Usermod // "minute" masks // Normal wiring - const int maskMinutes[12][maskSizeMinutes] = + const int maskMinutes[14][maskSizeMinutes] = { {107, 108, 109, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // :00 { 7, 8, 9, 10, 40, 41, 42, 43, -1, -1, -1, -1}, // :05 fünf nach @@ -50,11 +51,13 @@ class WordClockUsermod : public Usermod { 15, 16, 17, 18, 19, 20, 21, 33, 34, 35, -1, -1}, // :40 zwanzig vor { 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1}, // :45 dreiviertel { 11, 12, 13, 14, 33, 34, 35, -1, -1, -1, -1, -1}, // :50 zehn vor - { 7, 8, 9, 10, 33, 34, 35, -1, -1, -1, -1, -1} // :55 fünf vor + { 7, 8, 9, 10, 33, 34, 35, -1, -1, -1, -1, -1}, // :55 fünf vor + { 26, 27, 28, 29, 30, 31, 32, 40, 41, 42, 43, -1}, // :15 alternative viertel nach + { 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1} // :45 alternative viertel vor }; // Meander wiring - const int maskMinutesMea[12][maskSizeMinutesMea] = + const int maskMinutesMea[14][maskSizeMinutesMea] = { { 99, 100, 101, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // :00 { 7, 8, 9, 10, 33, 34, 35, 36, -1, -1, -1, -1}, // :05 fünf nach @@ -67,9 +70,12 @@ class WordClockUsermod : public Usermod { 11, 12, 13, 14, 15, 16, 17, 41, 42, 43, -1, -1}, // :40 zwanzig vor { 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1}, // :45 dreiviertel { 18, 19, 20, 21, 41, 42, 43, -1, -1, -1, -1, -1}, // :50 zehn vor - { 7, 8, 9, 10, 41, 42, 43, -1, -1, -1, -1, -1} // :55 fünf vor + { 7, 8, 9, 10, 41, 42, 43, -1, -1, -1, -1, -1}, // :55 fünf vor + { 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1}, // :15 alternative viertel nach + { 26, 27, 28, 29, 30, 31, 32, 41, 42, 43, -1, -1} // :45 alternative viertel vor }; + // hour masks // Normal wiring const int maskHours[13][maskSizeHours] = @@ -242,9 +248,15 @@ class WordClockUsermod : public Usermod setHours(hours, false); break; case 3: - // viertel - setMinutes(3); - setHours(hours + 1, false); + if (nord) { + // viertel nach + setMinutes(12); + setHours(hours, false); + } else { + // viertel + setMinutes(3); + setHours(hours + 1, false); + }; break; case 4: // 20 nach @@ -272,8 +284,13 @@ class WordClockUsermod : public Usermod setHours(hours + 1, false); break; case 9: - // viertel vor - setMinutes(9); + // viertel vor bzw dreiviertel + if (nord) { + setMinutes(9); + } + else { + setMinutes(12); + } setHours(hours + 1, false); break; case 10: @@ -410,6 +427,7 @@ class WordClockUsermod : public Usermod top["displayItIs"] = displayItIs; top["ledOffset"] = ledOffset; top["Meander wiring?"] = meander; + top["Norddeutsch"] = nord; } /* @@ -440,6 +458,7 @@ class WordClockUsermod : public Usermod configComplete &= getJsonValue(top["displayItIs"], displayItIs); configComplete &= getJsonValue(top["ledOffset"], ledOffset); configComplete &= getJsonValue(top["Meander wiring?"], meander); + configComplete &= getJsonValue(top["Norddeutsch"], nord); return configComplete; } diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 9906b3de..848f9d57 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -29,7 +29,11 @@ #include "fcn_declare.h" #define IBN 5100 -#define PALETTE_SOLID_WRAP (strip.paletteBlend == 1 || strip.paletteBlend == 3) + +// paletteBlend: 0 - wrap when moving, 1 - always wrap, 2 - never wrap, 3 - none (undefined) +#define PALETTE_SOLID_WRAP (strip.paletteBlend == 1 || strip.paletteBlend == 3) +#define PALETTE_MOVING_WRAP !(strip.paletteBlend == 2 || (strip.paletteBlend == 0 && SEGMENT.speed == 0)) + #define indexToVStrip(index, stripNr) ((index) | (int((stripNr)+1)<<16)) // effect utility functions @@ -120,7 +124,7 @@ uint16_t blink(uint32_t color1, uint32_t color2, bool strobe, bool do_palette) { uint16_t mode_blink(void) { return blink(SEGCOLOR(0), SEGCOLOR(1), false, true); } -static const char _data_FX_MODE_BLINK[] PROGMEM = "Blink@!,Duty cycle;!,!;!"; +static const char _data_FX_MODE_BLINK[] PROGMEM = "Blink@!,Duty cycle;!,!;!;01"; /* @@ -129,7 +133,7 @@ static const char _data_FX_MODE_BLINK[] PROGMEM = "Blink@!,Duty cycle;!,!;!"; uint16_t mode_blink_rainbow(void) { return blink(SEGMENT.color_wheel(SEGENV.call & 0xFF), SEGCOLOR(1), false, false); } -static const char _data_FX_MODE_BLINK_RAINBOW[] PROGMEM = "Blink Rainbow@Frequency,Blink duration;!,!;!"; +static const char _data_FX_MODE_BLINK_RAINBOW[] PROGMEM = "Blink Rainbow@Frequency,Blink duration;!,!;!;01"; /* @@ -138,7 +142,7 @@ static const char _data_FX_MODE_BLINK_RAINBOW[] PROGMEM = "Blink Rainbow@Frequen uint16_t mode_strobe(void) { return blink(SEGCOLOR(0), SEGCOLOR(1), true, true); } -static const char _data_FX_MODE_STROBE[] PROGMEM = "Strobe@!;!,!;!"; +static const char _data_FX_MODE_STROBE[] PROGMEM = "Strobe@!;!,!;!;01"; /* @@ -147,7 +151,7 @@ static const char _data_FX_MODE_STROBE[] PROGMEM = "Strobe@!;!,!;!"; uint16_t mode_strobe_rainbow(void) { return blink(SEGMENT.color_wheel(SEGENV.call & 0xFF), SEGCOLOR(1), true, false); } -static const char _data_FX_MODE_STROBE_RAINBOW[] PROGMEM = "Strobe Rainbow@!;,!;!"; +static const char _data_FX_MODE_STROBE_RAINBOW[] PROGMEM = "Strobe Rainbow@!;,!;!;01"; /* @@ -281,10 +285,12 @@ static const char _data_FX_MODE_RANDOM_COLOR[] PROGMEM = "Random Colors@!,Fade t * Lights every LED in a random color. Changes all LED at the same time * to new random colors. */ -uint16_t dynamic(boolean smooth=false) { +uint16_t mode_dynamic(void) { if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed if(SEGENV.call == 0) { + //SEGMENT.setUpLeds(); //lossless getPixelColor() + //SEGMENT.fill(BLACK); for (int i = 0; i < SEGLEN; i++) SEGENV.data[i] = random8(); } @@ -293,14 +299,14 @@ uint16_t dynamic(boolean smooth=false) { if (it != SEGENV.step && SEGMENT.speed != 0) //new color { for (int i = 0; i < SEGLEN; i++) { - if (random8() <= SEGMENT.intensity) SEGENV.data[i] = random8(); + if (random8() <= SEGMENT.intensity) SEGENV.data[i] = random8(); // random color index } SEGENV.step = it; } - if (smooth) { + if (SEGMENT.check1) { for (int i = 0; i < SEGLEN; i++) { - SEGMENT.blendPixelColor(i, SEGMENT.color_wheel(SEGENV.data[i]),16); // TODO + SEGMENT.blendPixelColor(i, SEGMENT.color_wheel(SEGENV.data[i]), 16); } } else { for (int i = 0; i < SEGLEN; i++) { @@ -309,22 +315,18 @@ uint16_t dynamic(boolean smooth=false) { } return FRAMETIME; } +static const char _data_FX_MODE_DYNAMIC[] PROGMEM = "Dynamic@!,!,,,,Smooth;;!"; /* - * Original effect "Dynamic" - */ -uint16_t mode_dynamic(void) { - return dynamic(false); -} -static const char _data_FX_MODE_DYNAMIC[] PROGMEM = "Dynamic@!,!;;!"; - - -/* - * effect "Dynamic" with smoth color-fading + * effect "Dynamic" with smooth color-fading */ uint16_t mode_dynamic_smooth(void) { - return dynamic(true); + bool old = SEGMENT.check1; + SEGMENT.check1 = true; + mode_dynamic(); + SEGMENT.check1 = old; + return FRAMETIME; } static const char _data_FX_MODE_DYNAMIC_SMOOTH[] PROGMEM = "Dynamic Smooth@!,!;;!"; @@ -348,7 +350,7 @@ uint16_t mode_breath(void) { return FRAMETIME; } -static const char _data_FX_MODE_BREATH[] PROGMEM = "Breathe@!;!,!;!"; +static const char _data_FX_MODE_BREATH[] PROGMEM = "Breathe@!;!,!;!;01"; /* @@ -364,7 +366,7 @@ uint16_t mode_fade(void) { return FRAMETIME; } -static const char _data_FX_MODE_FADE[] PROGMEM = "Fade@!;!,!;!"; +static const char _data_FX_MODE_FADE[] PROGMEM = "Fade@!;!,!;!;01"; /* @@ -603,33 +605,38 @@ static const char _data_FX_MODE_TWINKLE[] PROGMEM = "Twinkle@!,!;!,!;!;;m12=0"; * Dissolve function */ uint16_t dissolve(uint32_t color) { - bool wa = (SEGCOLOR(1) != 0 && strip.getBrightness() < 255); //workaround, can't compare getPixel to color if not full brightness + //bool wa = (SEGCOLOR(1) != 0 && strip.getBrightness() < 255); //workaround, can't compare getPixel to color if not full brightness + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); //lossless getPixelColor() + SEGMENT.fill(SEGCOLOR(1)); + } - for (int j = 0; j <= SEGLEN / 15; j++) - { + for (int j = 0; j <= SEGLEN / 15; j++) { if (random8() <= SEGMENT.intensity) { - for (size_t times = 0; times < 10; times++) //attempt to spawn a new pixel 5 times + for (size_t times = 0; times < 10; times++) //attempt to spawn a new pixel 10 times { uint16_t i = random16(SEGLEN); if (SEGENV.aux0) { //dissolve to primary/palette - if (SEGMENT.getPixelColor(i) == SEGCOLOR(1) || wa) { // TODO - if (color == SEGCOLOR(0)) - { + if (SEGMENT.getPixelColor(i) == SEGCOLOR(1) /*|| wa*/) { + if (color == SEGCOLOR(0)) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); - } else { SEGMENT.setPixelColor(i, color); } + } else { + SEGMENT.setPixelColor(i, color); + } break; //only spawn 1 new pixel per frame per 50 LEDs } } else { //dissolve to secondary - if (SEGMENT.getPixelColor(i) != SEGCOLOR(1)) { SEGMENT.setPixelColor(i, SEGCOLOR(1)); break; } // TODO + if (SEGMENT.getPixelColor(i) != SEGCOLOR(1)) { SEGMENT.setPixelColor(i, SEGCOLOR(1)); break; } } } } } - if (SEGENV.call > (255 - SEGMENT.speed) + 15U) - { + if (SEGENV.step > (255 - SEGMENT.speed) + 15U) { SEGENV.aux0 = !SEGENV.aux0; - SEGENV.call = 0; + SEGENV.step = 0; + } else { + SEGENV.step++; } return FRAMETIME; @@ -640,9 +647,9 @@ uint16_t dissolve(uint32_t color) { * Blink several LEDs on and then off */ uint16_t mode_dissolve(void) { - return dissolve(SEGCOLOR(0)); + return dissolve(SEGMENT.check1 ? SEGMENT.color_wheel(random8()) : SEGCOLOR(0)); } -static const char _data_FX_MODE_DISSOLVE[] PROGMEM = "Dissolve@Repeat speed,Dissolve speed;!,!;!"; +static const char _data_FX_MODE_DISSOLVE[] PROGMEM = "Dissolve@Repeat speed,Dissolve speed,,,,Random;!,!;!"; /* @@ -747,7 +754,7 @@ uint16_t mode_multi_strobe(void) { return FRAMETIME; } -static const char _data_FX_MODE_MULTI_STROBE[] PROGMEM = "Strobe Mega@!,!;!,!;!"; +static const char _data_FX_MODE_MULTI_STROBE[] PROGMEM = "Strobe Mega@!,!;!,!;!;01"; /* @@ -969,6 +976,7 @@ static const char _data_FX_MODE_COLORFUL[] PROGMEM = "Colorful@!,Saturation;1,2, * Emulates a traffic light. */ uint16_t mode_traffic_light(void) { + if (SEGLEN == 1) return mode_static(); for (int i=0; i < SEGLEN; i++) SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); uint32_t mdelay = 500; @@ -1001,6 +1009,7 @@ static const char _data_FX_MODE_TRAFFIC_LIGHT[] PROGMEM = "Traffic Light@!,US st */ #define FLASH_COUNT 4 uint16_t mode_chase_flash(void) { + if (SEGLEN == 1) return mode_static(); uint8_t flash_step = SEGENV.call % ((FLASH_COUNT * 2) + 1); for (int i = 0; i < SEGLEN; i++) { @@ -1030,6 +1039,7 @@ static const char _data_FX_MODE_CHASE_FLASH[] PROGMEM = "Chase Flash@!;Bg,Fx;!"; * Prim flashes running, followed by random color. */ uint16_t mode_chase_flash_random(void) { + if (SEGLEN == 1) return mode_static(); uint8_t flash_step = SEGENV.call % ((FLASH_COUNT * 2) + 1); for (int i = 0; i < SEGENV.aux1; i++) { @@ -1104,7 +1114,7 @@ uint16_t mode_running_random(void) { SEGENV.aux1 = it; return FRAMETIME; } -static const char _data_FX_MODE_RUNNING_RANDOM[] PROGMEM = "Stream@!,Zone size;;!"; +static const char _data_FX_MODE_RUNNING_RANDOM[] PROGMEM = "Stream ☾@!,Zone size;;!"; uint16_t larson_scanner(bool dual) { @@ -1163,6 +1173,7 @@ static const char _data_FX_MODE_DUAL_LARSON_SCANNER[] PROGMEM = "Scanner Dual@!, * Firing comets from one end. "Lighthouse" */ uint16_t mode_comet(void) { + if (SEGLEN == 1) return mode_static(); uint16_t counter = strip.now * ((SEGMENT.speed >>2) +1); uint16_t index = (counter * SEGLEN) >> 16; if (SEGENV.call == 0) SEGENV.aux0 = index; @@ -1190,30 +1201,33 @@ static const char _data_FX_MODE_COMET[] PROGMEM = "Lighthouse@!,Fade rate;!,!;!" * Fireworks function. */ uint16_t mode_fireworks() { - const uint16_t width = strip.isMatrix ? SEGMENT.virtualWidth() : SEGMENT.virtualLength(); + if (SEGLEN == 1) return mode_static(); + const uint16_t width = SEGMENT.is2D() ? SEGMENT.virtualWidth() : SEGMENT.virtualLength(); const uint16_t height = SEGMENT.virtualHeight(); - SEGMENT.fade_out(0); - if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); //lossless getPixelColor() + SEGMENT.fill(SEGCOLOR(1)); SEGENV.aux0 = UINT16_MAX; SEGENV.aux1 = UINT16_MAX; } + SEGMENT.fade_out(128); + bool valid1 = (SEGENV.aux0 < width*height); bool valid2 = (SEGENV.aux1 < width*height); uint32_t sv1 = 0, sv2 = 0; - if (valid1) sv1 = strip.isMatrix ? SEGMENT.getPixelColorXY(SEGENV.aux0%width, SEGENV.aux0/width) : SEGMENT.getPixelColor(SEGENV.aux0); // TODO get spark color - if (valid2) sv2 = strip.isMatrix ? SEGMENT.getPixelColorXY(SEGENV.aux1%width, SEGENV.aux1/width) : SEGMENT.getPixelColor(SEGENV.aux1); // TODO + if (valid1) sv1 = SEGMENT.is2D() ? SEGMENT.getPixelColorXY(SEGENV.aux0%width, SEGENV.aux0/width) : SEGMENT.getPixelColor(SEGENV.aux0); // get spark color + if (valid2) sv2 = SEGMENT.is2D() ? SEGMENT.getPixelColorXY(SEGENV.aux1%width, SEGENV.aux1/width) : SEGMENT.getPixelColor(SEGENV.aux1); if (!SEGENV.step) SEGMENT.blur(16); - if (valid1) { if (strip.isMatrix) SEGMENT.setPixelColorXY(SEGENV.aux0%width, SEGENV.aux0/width, sv1); else SEGMENT.setPixelColor(SEGENV.aux0, sv1); } // restore spark color after blur - if (valid2) { if (strip.isMatrix) SEGMENT.setPixelColorXY(SEGENV.aux1%width, SEGENV.aux1/width, sv2); else SEGMENT.setPixelColor(SEGENV.aux1, sv2); } // restore old spark color after blur + if (valid1) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(SEGENV.aux0%width, SEGENV.aux0/width, sv1); else SEGMENT.setPixelColor(SEGENV.aux0, sv1); } // restore spark color after blur + if (valid2) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(SEGENV.aux1%width, SEGENV.aux1/width, sv2); else SEGMENT.setPixelColor(SEGENV.aux1, sv2); } // restore old spark color after blur for (int i=0; i> 1)) == 0) { uint16_t index = random16(width*height); uint16_t j = index % width, k = index / width; uint32_t col = SEGMENT.color_from_palette(random8(), false, false, 0); - if (strip.isMatrix) SEGMENT.setPixelColorXY(j, k, col); + if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(j, k, col); else SEGMENT.setPixelColor(index, col); SEGENV.aux1 = SEGENV.aux0; // old spark SEGENV.aux0 = index; // remember where spark occured @@ -1225,12 +1239,12 @@ static const char _data_FX_MODE_FIREWORKS[] PROGMEM = "Fireworks@,Frequency;!,!; //Twinkling LEDs running. Inspired by https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Rain.h -uint16_t mode_rain() -{ +uint16_t mode_rain() { + if (SEGLEN == 1) return mode_static(); const uint16_t width = SEGMENT.virtualWidth(); const uint16_t height = SEGMENT.virtualHeight(); SEGENV.step += FRAMETIME; - if (SEGENV.step > SPEED_FORMULA_L) { + if (SEGENV.call && SEGENV.step > SPEED_FORMULA_L) { SEGENV.step = 1; if (strip.isMatrix) { uint32_t ctemp[width]; @@ -1241,9 +1255,9 @@ uint16_t mode_rain() SEGENV.aux1 = (SEGENV.aux1 % width) + (SEGENV.aux1 / width + 1) * width; } else { //shift all leds left - uint32_t ctemp = SEGMENT.getPixelColor(0); // TODO + uint32_t ctemp = SEGMENT.getPixelColor(0); for (int i = 0; i < SEGLEN - 1; i++) { - SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // TODO + SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); } SEGMENT.setPixelColor(SEGLEN -1, ctemp); // wrap around SEGENV.aux0++; // increase spark index @@ -1285,7 +1299,7 @@ uint16_t mode_fire_flicker(void) { SEGENV.step = it; return FRAMETIME; } -static const char _data_FX_MODE_FIRE_FLICKER[] PROGMEM = "Fire Flicker@!,!;!;!;;pal=0"; //WLEDMM pal=0 +static const char _data_FX_MODE_FIRE_FLICKER[] PROGMEM = "Fire Flicker@!,!;!;!;01;pal=0"; //WLEDMM pal=0 /* @@ -1336,8 +1350,8 @@ static const char _data_FX_MODE_LOADING[] PROGMEM = "Loading@!,Fade;!,!;!;;ix=16 //American Police Light with all LEDs Red and Blue -uint16_t police_base(uint32_t color1, uint32_t color2) -{ +uint16_t police_base(uint32_t color1, uint32_t color2) { + if (SEGLEN == 1) return mode_static(); uint16_t delay = 1 + (FRAMETIME<<3) / SEGLEN; // longer segments should change faster uint32_t it = strip.now / map(SEGMENT.speed, 0, 255, delay<<4, delay); uint16_t offset = it % SEGLEN; @@ -1585,8 +1599,7 @@ static const char _data_FX_MODE_ICU[] PROGMEM = "ICU@!,!,,,,,Overlay;!,!;!"; /* * Custom mode by Aircoookie. Color Wipe, but with 3 colors */ -uint16_t mode_tricolor_wipe(void) -{ +uint16_t mode_tricolor_wipe(void) { uint32_t cycleTime = 1000 + (255 - SEGMENT.speed)*200; uint32_t perc = strip.now % cycleTime; uint16_t prog = (perc * 65535) / cycleTime; @@ -1628,8 +1641,7 @@ static const char _data_FX_MODE_TRICOLOR_WIPE[] PROGMEM = "Tri Wipe@!;1,2,3;!"; * Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/TriFade.h * Modified by Aircoookie */ -uint16_t mode_tricolor_fade(void) -{ +uint16_t mode_tricolor_fade(void) { uint16_t counter = strip.now * ((SEGMENT.speed >> 3) +1); uint32_t prog = (counter * 768) >> 16; @@ -1672,8 +1684,7 @@ static const char _data_FX_MODE_TRICOLOR_FADE[] PROGMEM = "Tri Fade@!;1,2,3;!"; * Creates random comets * Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/MultiComet.h */ -uint16_t mode_multi_comet(void) -{ +uint16_t mode_multi_comet(void) { uint32_t cycleTime = 10 + (uint32_t)(255 - SEGMENT.speed); uint32_t it = strip.now / cycleTime; if (SEGENV.step == it) return FRAMETIME; @@ -1711,8 +1722,7 @@ static const char _data_FX_MODE_MULTI_COMET[] PROGMEM = "Multi Comet"; * Running random pixels ("Stream 2") * Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/RandomChase.h */ -uint16_t mode_random_chase(void) -{ +uint16_t mode_random_chase(void) { if (SEGENV.call == 0) { SEGENV.step = RGBW32(random8(), random8(), random8(), 0); SEGENV.aux0 = random16(); @@ -1740,7 +1750,7 @@ uint16_t mode_random_chase(void) random16_set_seed(prevSeed); // restore original seed so other effects can use "random" PRNG return FRAMETIME; } -static const char _data_FX_MODE_RANDOM_CHASE[] PROGMEM = "Stream 2@!;;"; +static const char _data_FX_MODE_RANDOM_CHASE[] PROGMEM = "Stream 2 ☾@!;;"; //7 bytes @@ -1754,8 +1764,7 @@ typedef struct Oscillator { /* / Oscillating bars of color, updated with standard framerate */ -uint16_t mode_oscillate(void) -{ +uint16_t mode_oscillate(void) { uint8_t numOscillators = 3; uint16_t dataSize = sizeof(oscillator) * numOscillators; @@ -1790,9 +1799,9 @@ uint16_t mode_oscillate(void) } } - for (int i=0; i < SEGLEN; i++) { + for (int i = 0; i < SEGLEN; i++) { uint32_t color = BLACK; - for (int j=0; j < numOscillators; j++) { + for (int j = 0; j < numOscillators; j++) { if(i >= oscillators[j].pos - oscillators[j].size && i <= oscillators[j].pos + oscillators[j].size) { color = (color == BLACK) ? SEGCOLOR(j) : color_blend(color, SEGCOLOR(j), 128); } @@ -1807,8 +1816,8 @@ static const char _data_FX_MODE_OSCILLATE[] PROGMEM = "Oscillate"; //TODO -uint16_t mode_lightning(void) -{ +uint16_t mode_lightning(void) { + if (SEGLEN == 1) return mode_static(); uint16_t ledstart = random16(SEGLEN); // Determine starting location of flash uint16_t ledlen = 1 + random16(SEGLEN -ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1) uint8_t bri = 255/random8(1, 3); @@ -1853,8 +1862,7 @@ static const char _data_FX_MODE_LIGHTNING[] PROGMEM = "Lightning@!,!,,,,,Overlay // Pride2015 // Animated, ever-changing rainbows. // by Mark Kriegsman: https://gist.github.com/kriegsman/964de772d64c502760e5 -uint16_t mode_pride_2015(void) -{ +uint16_t mode_pride_2015(void) { uint16_t duration = 10 + SEGMENT.speed; uint16_t sPseudotime = SEGENV.step; uint16_t sHue16 = SEGENV.aux0; @@ -1870,7 +1878,6 @@ uint16_t mode_pride_2015(void) sPseudotime += duration * msmultiplier; sHue16 += duration * beatsin88( 400, 5,9); uint16_t brightnesstheta16 = sPseudotime; - CRGB fastled_col; for (int i = 0 ; i < SEGLEN; i++) { hue16 += hueinc16; @@ -1883,11 +1890,8 @@ uint16_t mode_pride_2015(void) uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536; bri8 += (255 - brightdepth); - CRGB newcolor = CHSV( hue8, sat8, bri8); - fastled_col = CRGB(SEGMENT.getPixelColor(i)); // TODO - - nblend(fastled_col, newcolor, 64); - SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + CRGB newcolor = CHSV(hue8, sat8, bri8); + SEGMENT.blendPixelColor(i, newcolor, 64); } SEGENV.step = sPseudotime; SEGENV.aux0 = sHue16; @@ -1898,24 +1902,30 @@ static const char _data_FX_MODE_PRIDE_2015[] PROGMEM = "Pride 2015@!;;"; //eight colored dots, weaving in and out of sync with each other -uint16_t mode_juggle(void){ - SEGMENT.fade_out(SEGMENT.intensity); +uint16_t mode_juggle(void) { + if (SEGLEN == 1) return mode_static(); + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); //lossless getPixelColor() + SEGMENT.fill(BLACK); + } + + SEGMENT.fadeToBlackBy(192 - (3*SEGMENT.intensity/4)); + CRGB fastled_col; byte dothue = 0; for (int i = 0; i < 8; i++) { - uint16_t index = 0 + beatsin88((128 + SEGMENT.speed)*(i + 7), 0, SEGLEN -1); - fastled_col = CRGB(SEGMENT.getPixelColor(index)); // TODO + uint16_t index = 0 + beatsin88((16 + SEGMENT.speed)*(i + 7), 0, SEGLEN -1); + fastled_col = CRGB(SEGMENT.getPixelColor(index)); fastled_col |= (SEGMENT.palette==0)?CHSV(dothue, 220, 255):ColorFromPalette(SEGPALETTE, dothue, 255); - SEGMENT.setPixelColor(index, fastled_col.red, fastled_col.green, fastled_col.blue); + SEGMENT.setPixelColor(index, fastled_col); dothue += 32; } return FRAMETIME; } -static const char _data_FX_MODE_JUGGLE[] PROGMEM = "Juggle@!,Trail;;!;;sx=16,ix=240"; +static const char _data_FX_MODE_JUGGLE[] PROGMEM = "Juggle@!,Trail;;!;;sx=64,ix=128"; -uint16_t mode_palette() -{ +uint16_t mode_palette() { uint16_t counter = 0; if (SEGMENT.speed != 0) { @@ -1923,11 +1933,10 @@ uint16_t mode_palette() counter = counter >> 8; } - bool noWrap = (strip.paletteBlend == 2 || (strip.paletteBlend == 0 && SEGMENT.speed == 0)); for (int i = 0; i < SEGLEN; i++) { uint8_t colorIndex = (i * 255 / SEGLEN) - counter; - SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(colorIndex, false, noWrap, 255)); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(colorIndex, false, PALETTE_MOVING_WRAP, 255)); } return FRAMETIME; @@ -1963,43 +1972,43 @@ static const char _data_FX_MODE_PALETTE[] PROGMEM = "Palette@Cycle speed;;!;;c3= // There are two main parameters you can play with to control the look and // feel of your fire: COOLING (used in step 1 above) (Speed = COOLING), and SPARKING (used // in step 3 above) (Effect Intensity = Sparking). -uint16_t mode_fire_2012() -{ - uint16_t strips = SEGMENT.nrOfVStrips(); +uint16_t mode_fire_2012() { + if (SEGLEN == 1) return mode_static(); + const uint16_t strips = SEGMENT.nrOfVStrips(); if (!SEGENV.allocateData(strips * SEGLEN)) return mode_static(); //allocation failed byte* heat = SEGENV.data; - uint32_t it = strip.now >> 5; //div 32 + const uint32_t it = strip.now >> 6; //div 64 struct virtualStrip { static void runStrip(uint16_t stripNr, byte* heat, uint32_t it) { + const uint8_t ignition = max(3,SEGLEN/10); // ignition area: 10% of segment length or minimum 3 pixels + + // Step 1. Cool down every cell a little + for (int i = 0; i < SEGLEN; i++) { + uint8_t cool = (it != SEGENV.step) ? random8((((20 + SEGMENT.speed/3) * 16) / SEGLEN)+2) : random(8); + uint8_t minTemp = 0; + if (i 1; k--) { heat[k] = (heat[k - 1] + (heat[k - 2]<<1) ) / 3; // heat[k-2] multiplied by 2 } + } - // Step 3. Randomly ignite new 'sparks' of heat near the bottom - if (random8() <= SEGMENT.intensity) { - uint8_t y = random8(ignition); - heat[y] = qadd8(heat[y], random8(160,255)); - } + // Step 3. Randomly ignite new 'sparks' of heat near the bottom + if (random8() <= SEGMENT.intensity) { + uint8_t y = random8(ignition); + uint8_t boost = (32+SEGMENT.custom3*2) * (2*ignition-y) / (2*ignition); + heat[y] = qadd8(heat[y], random8(64+boost,128+boost)); } // Step 4. Map from heat cells to LED colors @@ -2012,19 +2021,20 @@ uint16_t mode_fire_2012() for (int stripNr=0; stripNr> 1)); @@ -2157,8 +2154,7 @@ uint16_t mode_noise16_2() static const char _data_FX_MODE_NOISE16_2[] PROGMEM = "Noise 2@!;!;!"; -uint16_t mode_noise16_3() -{ +uint16_t mode_noise16_3() { uint16_t scale = 800; // the "zoom factor" for the noise //CRGB fastled_col; SEGENV.step += (1 + SEGMENT.speed); @@ -2183,8 +2179,7 @@ static const char _data_FX_MODE_NOISE16_3[] PROGMEM = "Noise 3@!;!;!"; //https://github.com/aykevl/ledstrip-spark/blob/master/ledstrip.ino -uint16_t mode_noise16_4() -{ +uint16_t mode_noise16_4() { //CRGB fastled_col; uint32_t stp = (strip.now * SEGMENT.speed) >> 7; for (int i = 0; i < SEGLEN; i++) { @@ -2199,8 +2194,7 @@ static const char _data_FX_MODE_NOISE16_4[] PROGMEM = "Noise 4@!;!;!"; //based on https://gist.github.com/kriegsman/5408ecd397744ba0393e -uint16_t mode_colortwinkle() -{ +uint16_t mode_colortwinkle() { uint16_t dataSize = (SEGLEN+7) >> 3; //1 bit per LED if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed @@ -2280,6 +2274,7 @@ static const char _data_FX_MODE_LAKE[] PROGMEM = "Lake@!;Fx;!"; // send a meteor from begining to to the end of the strip with a trail that randomly decays. // adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain uint16_t mode_meteor() { + if (SEGLEN == 1) return mode_static(); if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed byte* trail = SEGENV.data; @@ -2317,6 +2312,7 @@ static const char _data_FX_MODE_METEOR[] PROGMEM = "Meteor@!,Trail length;!;!"; // send a meteor from begining to to the end of the strip with a trail that randomly decays. // adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain uint16_t mode_meteor_smooth() { + if (SEGLEN == 1) return mode_static(); if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed byte* trail = SEGENV.data; @@ -2353,8 +2349,8 @@ static const char _data_FX_MODE_METEOR_SMOOTH[] PROGMEM = "Meteor Smooth@!,Trail //Railway Crossing / Christmas Fairy lights -uint16_t mode_railway() -{ +uint16_t mode_railway() { + if (SEGLEN == 1) return mode_static(); uint16_t dur = (256 - SEGMENT.speed) * 40; uint16_t rampdur = (dur * SEGMENT.intensity) >> 8; if (SEGENV.step > dur) @@ -2455,6 +2451,7 @@ uint16_t ripple_base() uint16_t mode_ripple(void) { + if (SEGLEN == 1) return mode_static(); if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); return ripple_base(); } @@ -2462,6 +2459,7 @@ static const char _data_FX_MODE_RIPPLE[] PROGMEM = "Ripple@!,Wave #,,,,,Overlay; uint16_t mode_ripple_rainbow(void) { + if (SEGLEN == 1) return mode_static(); if (SEGENV.call ==0) { SEGENV.aux0 = random8(); SEGENV.aux1 = random8(); @@ -2626,6 +2624,7 @@ static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rat //inspired by https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectBlinkingHalloweenEyes uint16_t mode_halloween_eyes() { + if (SEGLEN == 1) return mode_static(); const uint16_t maxWidth = strip.isMatrix ? SEGMENT.virtualWidth() : SEGLEN; const uint16_t HALLOWEEN_EYE_SPACE = MAX(2, strip.isMatrix ? SEGMENT.virtualWidth()>>4: SEGLEN>>5); const uint16_t HALLOWEEN_EYE_WIDTH = HALLOWEEN_EYE_SPACE/2; @@ -2735,6 +2734,7 @@ static const char _data_FX_MODE_TRI_STATIC_PATTERN[] PROGMEM = "Solid Pattern Tr uint16_t spots_base(uint16_t threshold) { + if (SEGLEN == 1) return mode_static(); if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); uint16_t maxZones = SEGLEN >> 2; @@ -2790,6 +2790,7 @@ typedef struct Ball { * Bouncing Balls Effect */ uint16_t mode_bouncing_balls(void) { + if (SEGLEN == 1) return mode_static(); //allocate segment data const uint16_t strips = SEGMENT.nrOfVStrips(); // adapt for 2D const size_t maxNumBalls = 16; @@ -2829,7 +2830,7 @@ uint16_t mode_bouncing_balls(void) { balls[i].lastBounceTime = time; if (balls[i].impactVelocity < 0.015f) { - float impactVelocityStart = sqrtf(-2 * gravity) * random8(5,11)/10.0f; // randomize impact velocity + float impactVelocityStart = sqrtf(-2.0f * gravity) * random8(5,11)/10.0f; // randomize impact velocity balls[i].impactVelocity = impactVelocityStart; } } else if (balls[i].height > 1.0f) { @@ -2862,6 +2863,7 @@ static const char _data_FX_MODE_BOUNCINGBALLS[] PROGMEM = "Bouncing Balls@Gravit * Sinelon stolen from FASTLED examples */ uint16_t sinelon_base(bool dual, bool rainbow=false) { + if (SEGLEN == 1) return mode_static(); SEGMENT.fade_out(SEGMENT.intensity); uint16_t pos = beatsin16(SEGMENT.speed/10,0,SEGLEN-1); if (SEGENV.call == 0) SEGENV.aux0 = pos; @@ -2959,6 +2961,7 @@ typedef struct Spark { * modified from https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Popcorn.h */ uint16_t mode_popcorn(void) { + if (SEGLEN == 1) return mode_static(); //allocate segment data uint16_t strips = SEGMENT.nrOfVStrips(); uint16_t dataSize = sizeof(spark) * maxNumPopcorn; @@ -2987,7 +2990,7 @@ uint16_t mode_popcorn(void) { uint16_t peakHeight = 128 + random8(128); //0-255 peakHeight = (peakHeight * (SEGLEN -1)) >> 8; - popcorn[i].vel = sqrtf(-2.0 * gravity * peakHeight); + popcorn[i].vel = sqrtf(-2.0f * gravity * peakHeight); if (SEGMENT.palette) { @@ -3101,7 +3104,7 @@ uint16_t mode_candle() { return candle(false); } -static const char _data_FX_MODE_CANDLE[] PROGMEM = "Candle@!,!;!,!;!;1;sx=96,ix=224,pal=0"; +static const char _data_FX_MODE_CANDLE[] PROGMEM = "Candle@!,!;!,!;!;01;sx=96,ix=224,pal=0"; uint16_t mode_candle_multi() @@ -3132,6 +3135,7 @@ typedef struct particle { } star; uint16_t mode_starburst(void) { + if (SEGLEN == 1) return mode_static(); uint16_t maxData = FAIR_DATA_PER_SEG; //ESP8266: 256 ESP32: 640 uint8_t segs = strip.getActiveSegmentsNum(); if (segs <= (strip.getMaxSegments() /2)) maxData *= 2; //ESP8266: 512 if <= 8 segs ESP32: 1280 if <= 16 segs @@ -3251,6 +3255,7 @@ static const char _data_FX_MODE_STARBURST[] PROGMEM = "Fireworks Starburst@Chanc */ uint16_t mode_exploding_fireworks(void) { + if (SEGLEN == 1) return mode_static(); const uint16_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1; const uint16_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength(); @@ -3283,11 +3288,11 @@ uint16_t mode_exploding_fireworks(void) if (SEGENV.aux0 < 2) { //FLARE if (SEGENV.aux0 == 0) { //init flare flare->pos = 0; - flare->posX = strip.isMatrix ? random16(2,cols-1) : (SEGMENT.intensity > random8()); // will enable random firing side on 1D + flare->posX = strip.isMatrix ? random16(2,cols-3) : (SEGMENT.intensity > random8()); // will enable random firing side on 1D uint16_t peakHeight = 75 + random8(180); //0-255 peakHeight = (peakHeight * (rows -1)) >> 8; flare->vel = sqrtf(-2.0f * gravity * peakHeight); - flare->velX = strip.isMatrix ? (random8(8)-4)/32.f : 0; // no X velocity on 1D + flare->velX = strip.isMatrix ? (random8(9)-4)/32.f : 0; // no X velocity on 1D flare->col = 255; //brightness SEGENV.aux0 = 1; } @@ -3314,16 +3319,16 @@ uint16_t mode_exploding_fireworks(void) * Size is proportional to the height. */ int nSparks = flare->pos + random8(4); - nSparks = constrain(nSparks, 1, numSparks); + nSparks = constrain(nSparks, 4, numSparks); // initialize sparks if (SEGENV.aux0 == 2) { for (int i = 1; i < nSparks; i++) { sparks[i].pos = flare->pos; sparks[i].posX = flare->posX; - sparks[i].vel = (float(random16(0, 20000)) / 10000.0f) - 0.9f; // from -0.9 to 1.1 + sparks[i].vel = (float(random16(20001)) / 10000.0f) - 0.9f; // from -0.9 to 1.1 sparks[i].vel *= rows<32 ? 0.5f : 1; // reduce velocity for smaller strips - sparks[i].velX = strip.isMatrix ? (float(random16(0, 4000)) / 10000.0f) - 0.2f : 0; // from -0.2 to 0.2 + sparks[i].velX = strip.isMatrix ? (float(random16(10001)) / 10000.0f) - 0.5f : 0; // from -0.5 to 0.5 sparks[i].col = 345;//abs(sparks[i].vel * 750.0); // set colors before scaling velocity to keep them bright //sparks[i].col = constrain(sparks[i].col, 0, 345); sparks[i].colIndex = random8(); @@ -3385,6 +3390,7 @@ static const char _data_FX_MODE_EXPLODING_FIREWORKS[] PROGMEM = "Fireworks 1D@Gr */ uint16_t mode_drip(void) { + if (SEGLEN == 1) return mode_static(); //allocate segment data uint16_t strips = SEGMENT.nrOfVStrips(); const int maxNumDrops = 4; @@ -3482,6 +3488,7 @@ typedef struct Tetris { } tetris; uint16_t mode_tetrix(void) { + if (SEGLEN == 1) return mode_static(); uint16_t strips = SEGMENT.nrOfVStrips(); // allow running on virtual strips (columns in 2D segment) uint16_t dataSize = sizeof(tetris); if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed @@ -3524,7 +3531,7 @@ uint16_t mode_tetrix(void) { if (drop->pos > drop->stack) { // fall until top of stack drop->pos -= drop->speed; // may add gravity as: speed += gravity if (int(drop->pos) < int(drop->stack)) drop->pos = drop->stack; - for (int i=int(drop->pos); ipos); i < SEGLEN; i++) { uint32_t col = ipos)+drop->brick ? SEGMENT.color_from_palette(drop->col, false, false, 0) : SEGCOLOR(1); SEGMENT.setPixelColor(indexToVStrip(i, stripNr), col); } @@ -3539,7 +3546,7 @@ uint16_t mode_tetrix(void) { drop->brick = 0; // reset brick size (no more growing) if (drop->step > millis()) { // allow fading of virtual strip - for (int i=0; istack = 0; // reset brick stack size drop->step = 0; // proceed with next brick @@ -3666,7 +3673,7 @@ uint16_t mode_heartbeat(void) { return FRAMETIME; } -static const char _data_FX_MODE_HEARTBEAT[] PROGMEM = "Heartbeat@!,!;!,!;!;;m12=1"; //Bar +static const char _data_FX_MODE_HEARTBEAT[] PROGMEM = "Heartbeat@!,!;!,!;!;01;m12=1"; //Bar // "Pacifica" @@ -3790,6 +3797,7 @@ static const char _data_FX_MODE_PACIFICA[] PROGMEM = "Pacifica@!,Angle;;!;;pal=5 * Mode simulates a gradual sunrise */ uint16_t mode_sunrise() { + if (SEGLEN == 1) return mode_static(); //speed 0 - static sun //speed 1 - 60: sunrise time in minutes //speed 60 - 120 : sunset time in minutes - 60; @@ -3883,7 +3891,7 @@ static const char _data_FX_MODE_PHASEDNOISE[] PROGMEM = "Phased Noise@!,!;!,!;!" uint16_t mode_twinkleup(void) { // A very short twinkle routine with fade-in and dual controls. By Andrew Tuline. random16_set_seed(535); // The randomizer needs to be re-set each time through the loop in order for the same 'random' numbers to be the same each time through. - for (int i = 0; i SEGMENT.intensity) pixBri = 0; @@ -3945,7 +3953,7 @@ uint16_t mode_sinewave(void) { // Adjustable sinewave. By Andrew Tul SEGENV.step += SEGMENT.speed/16; // Speed of animation. uint16_t freq = SEGMENT.intensity/4;//SEGMENT.fft2/8; // Frequency of the signal. - for (int i=0; i> 4)); uint16_t numBirds = 2 + (SEGLEN >> 3); // 2 + 1/8 of a segment @@ -4051,6 +4060,7 @@ typedef struct Spotlight { */ uint16_t mode_dancing_shadows(void) { + if (SEGLEN == 1) return mode_static(); uint8_t numSpotlights = map(SEGMENT.intensity, 0, 255, 2, SPOT_MAX_COUNT); // 49 on 32 segment ESP32, 17 on 16 segment ESP8266 bool initialize = SEGENV.aux0 != numSpotlights; SEGENV.aux0 = numSpotlights; @@ -4177,7 +4187,7 @@ uint16_t mode_washing_machine(void) { SEGENV.step += (speed * 128.0f); - for (int i=0; i> 7)); SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(col, false, PALETTE_SOLID_WRAP, 3)); } @@ -4514,7 +4524,7 @@ static const char _data_FX_MODE_AURORA[] PROGMEM = "Aurora@!,!;1,2,3;!;;sx=24,pa // 16 bit perlinmove. Use Perlin Noise instead of sinewaves for movement. By Andrew Tuline. // Controls are speed, # of pixels, faderate. uint16_t mode_perlinmove(void) { - + if (SEGLEN == 1) return mode_static(); SEGMENT.fade_out(255-SEGMENT.custom1); for (int i = 0; i < SEGMENT.intensity/16 + 1; i++) { uint16_t locn = inoise16(millis()*128/(260-SEGMENT.speed)+i*15000, millis()*128/(260-SEGMENT.speed)); // Get a new pixel location from moving noise. @@ -4846,7 +4856,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t dataSize = sizeof(CRGB) * SEGMENT.length(); // using width*height prevents reallocation if mirroring is enabled - const uint16_t crcBufferLen = (SEGMENT.width() + SEGMENT.height())*2; // roughly sqrt(2)/2 for better repetition detection (Ewowi) + const uint16_t crcBufferLen = 2; //(SEGMENT.width() + SEGMENT.height())*71/100; // roughly sqrt(2)/2 for better repetition detection (Ewowi) if (!SEGENV.allocateData(dataSize + sizeof(uint16_t)*crcBufferLen)) return mode_static(); //allocation failed CRGB *prevLeds = reinterpret_cast(SEGENV.data); @@ -4854,7 +4864,9 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: CRGB backgroundColor = SEGCOLOR(1); - if (SEGENV.call == 0 || strip.now - SEGMENT.step > 5000) { + if (SEGENV.call == 0) SEGMENT.setUpLeds(); + + if (SEGENV.call == 0 || strip.now - SEGMENT.step > 3000) { SEGENV.step = strip.now; SEGENV.aux0 = 0; random16_set_seed(millis()>>2); //seed the random generator @@ -4870,7 +4882,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) prevLeds[XY(x,y)] = CRGB::Black; memset(crcBuffer, 0, sizeof(uint16_t)*crcBufferLen); - } else if (strip.now - SEGENV.step < FRAMETIME_FIXED * (uint32_t)map(SEGMENT.speed,0,255,64,4)) { //WLEDMM (uint32_t) surpresses warning + } else if (strip.now - SEGENV.step < FRAMETIME_FIXED * (uint32_t)map(SEGMENT.speed,0,255,64,4)) { // update only when appropriate time passes (in 42 FPS slots) return FRAMETIME; } @@ -4881,16 +4893,22 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: //calculate new leds for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { - colorCount colorsCount[9];//count the different colors in the 9*9 matrix - for (int i=0; i<9; i++) colorsCount[i] = {backgroundColor, 0}; //init colorsCount - //iterate through neighbors and count them and their different colors + colorCount colorsCount[9]; // count the different colors in the 3*3 matrix + for (int i=0; i<9; i++) colorsCount[i] = {backgroundColor, 0}; // init colorsCount + + // iterate through neighbors and count them and their different colors int neighbors = 0; - for (int i = -1; i <= 1; i++) for (int j = -1; j <= 1; j++) { //iterate through 9*9 matrix - uint16_t xy = XY((x+i+cols)%cols, (y+j+rows)%rows); //previous cell xy to check, wrap around segment (WLEDMM 0.13 style) + for (int i = -1; i <= 1; i++) for (int j = -1; j <= 1; j++) { // iterate through 3*3 matrix + if (i==0 && j==0) continue; // ignore itself + // wrap around segment + int16_t xx = x+i, yy = y+j; + if (x+i < 0) xx = cols-1; else if (x+i >= cols) xx = 0; + if (y+j < 0) yy = rows-1; else if (y+j >= rows) yy = 0; - // count different neighbours and colors, except the centre cell - if (xy != XY(x,y) && prevLeds[xy] != backgroundColor) { + uint16_t xy = XY(xx, yy); // previous cell xy to check + // count different neighbours and colors + if (prevLeds[xy] != backgroundColor) { neighbors++; bool colorFound = false; int k; @@ -4899,22 +4917,24 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: colorsCount[k].count++; colorFound = true; } - if (!colorFound) colorsCount[k] = {prevLeds[xy], 1}; //add new color found in the array } } // i,j // Rules of Life - uint32_t col = SEGMENT.getPixelColorXY(x,y); + uint32_t col = prevLeds[XY(x,y)]; uint32_t bgc = RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0); if ((col != bgc) && (neighbors < 2)) SEGMENT.setPixelColorXY(x,y, bgc); // Loneliness else if ((col != bgc) && (neighbors > 3)) SEGMENT.setPixelColorXY(x,y, bgc); // Overpopulation else if ((col == bgc) && (neighbors == 3)) { // Reproduction - //find dominant color and assign to cell + // find dominant color and assign it to a cell colorCount dominantColorCount = {backgroundColor, 0}; for (int i=0; i<9 && colorsCount[i].count != 0; i++) if (colorsCount[i].count > dominantColorCount.count) dominantColorCount = colorsCount[i]; - if (dominantColorCount.count > 0) SEGMENT.setPixelColorXY(x,y, dominantColorCount.color); //assign the dominant color + // assign the dominant color w/ a bit of randomness to avoid "gliders" + if (dominantColorCount.count > 0 && random8(128)) SEGMENT.setPixelColorXY(x,y, dominantColorCount.color); + } else if ((col == bgc) && (neighbors == 2) && !random8(128)) { // Mutation + SEGMENT.setPixelColorXY(x,y, SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 255)); } // else do nothing! } //x,y @@ -4932,7 +4952,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: return FRAMETIME; } // mode_2Dgameoflife() -static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!,,,,,All colors=0;!,!;!;2"; //WLEDMM support all colors +static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!,,,,,All colors ☾;!,!;!;2;c1=0"; //WLEDMM support all colors ///////////////////////// @@ -5099,7 +5119,7 @@ uint16_t mode_2DLissajous(void) { // By: Andrew Tuline return FRAMETIME; } // mode_2DLissajous() -static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous@X frequency,Fade rate;!;!;2"; +static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous ☾@X frequency,Fade rate;!;!;2"; /////////////////////// @@ -5310,7 +5330,7 @@ uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https SEGENV.step = 0; } - float adjustHeight = (float)map(rows, 8, 32, 28, 12); + float adjustHeight = (float)map(rows, 8, 32, 28, 12); // maybe use mapf() ??? uint16_t adjScale = map(cols, 8, 64, 310, 63); /* if (SEGENV.aux1 != SEGMENT.custom1/12) { // Hacky palette rotation. We need that black. @@ -5336,7 +5356,7 @@ uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https SEGMENT.setPixelColorXY(x, y, ColorFromPalette(auroraPalette, qsub8( inoise8((SEGENV.step%2) + x * _scale, y * 16 + SEGENV.step % 16, SEGENV.step / _speed), - fabsf((float)rows / 2 - (float)y) * adjustHeight))); + fabsf((float)rows / 2.0f - (float)y) * adjustHeight))); } } @@ -5351,7 +5371,7 @@ static const char _data_FX_MODE_2DPOLARLIGHTS[] PROGMEM = "Polar Lights@!,Scale; uint16_t mode_2DPulser(void) { // By: ldirko https://editor.soulmatelights.com/gallery/878-pulse-test , modifed by: Andrew Tuline if (!strip.isMatrix) return mode_static(); // not a 2D set-up - const uint16_t cols = SEGMENT.virtualWidth(); // WLEDMM bugfix + const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); if (SEGENV.call == 0) { @@ -5362,7 +5382,7 @@ uint16_t mode_2DPulser(void) { // By: ldirko https://edi SEGMENT.fadeToBlackBy(8 - (SEGMENT.intensity>>5)); uint32_t a = strip.now / (18 - SEGMENT.speed / 16); - uint16_t x = (a / 14) % cols; // WLEDMM bugfix + uint16_t x = (a / 14) % cols; uint16_t y = map((sin8(a * 5) + sin8(a * 4) + sin8(a * 2)), 0, 765, rows-1, 0); SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, map(y, 0, rows-1, 0, 255), 255, LINEARBLEND)); @@ -5800,8 +5820,8 @@ uint16_t mode_2Dfloatingblobs(void) { } } uint32_t c = SEGMENT.color_from_palette(blob->color[i], false, false, 0); - if (blob->r[i] > 1.f) SEGMENT.fill_circle(blob->y[i], blob->x[i], roundf(blob->r[i]), c); - else SEGMENT.setPixelColorXY(blob->y[i], blob->x[i], c); + if (blob->r[i] > 1.f) SEGMENT.fill_circle(blob->x[i], blob->y[i], roundf(blob->r[i]), c); + else SEGMENT.setPixelColorXY(blob->x[i], blob->y[i], c); // move x if (blob->x[i] + blob->r[i] >= cols - 1) blob->x[i] += (blob->sX[i] * ((cols - 1 - blob->x[i]) / blob->r[i] + 0.005f)); else if (blob->x[i] - blob->r[i] <= 0) blob->x[i] += (blob->sX[i] * (blob->x[i] / blob->r[i] + 0.005f)); @@ -5887,10 +5907,8 @@ uint16_t mode_2Dscrollingtext(void) { ++SEGENV.aux1 &= 0xFF; // color shift SEGENV.step = millis() + map(SEGMENT.speed, 0, 255, 10*FRAMETIME_FIXED, 2*FRAMETIME_FIXED); if (!SEGMENT.check2) { - // we need it 3 times - SEGMENT.fade_out(255 - (SEGMENT.custom1>>5)); // fade to background color - SEGMENT.fade_out(255 - (SEGMENT.custom1>>5)); // fade to background color - SEGMENT.fade_out(255 - (SEGMENT.custom1>>5)); // fade to background color + for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++ ) + SEGMENT.blendPixelColorXY(x, y, SEGCOLOR(1), 255 - (SEGMENT.custom1>>1)); } } for (int i = 0; i < numberOfLetters; i++) { @@ -5958,14 +5976,16 @@ static const char _data_FX_MODE_2DDRIFTROSE[] PROGMEM = "Drift Rose@Fade,Blur;;; um_data_t *um_data; if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { volumeSmth = *(float*) um_data->u_data[0]; - volumeRaw = *(float*) um_data->u_data[1]; + volumeRaw = *(int16_t*) um_data->u_data[1]; fftResult = (uint8_t*) um_data->u_data[2]; samplePeak = *(uint8_t*) um_data->u_data[3]; FFT_MajorPeak = *(float*) um_data->u_data[4]; my_magnitude = *(float*) um_data->u_data[5]; maxVol = (uint8_t*) um_data->u_data[6]; // requires UI element (SEGMENT.customX?), changes source element binNum = (uint8_t*) um_data->u_data[7]; // requires UI element (SEGMENT.customX?), changes source element - fftBin = (float*) um_data->u_data[8]; + FFT_MajPeakSmth= *(float*) um_data->u_data[8]; // FFT Majorpeak smoothed + soundPressure = *(float*) um_data->u_data[9]; // sound pressure ( = logarithmic scale microphone input). Range 0...255 + agcSensitivity = *(float*) um_data->u_data[10]; // current AGC gain, scaled to 0...255. use "255.0f - agcSensitivity" to get MIC input level } else { // add support for no audio data um_data = simulateSound(SEGMENT.soundSim); @@ -6019,6 +6039,8 @@ uint16_t mode_ripplepeak(void) { // * Ripple peak. By Andrew Tuli SEGMENT.custom1 = *binNum; SEGMENT.custom2 = *maxVol * 2; } + if (SEGMENT.custom1 < 2) SEGMENT.custom1 = 2; // WLEDMM prevent stupid settings + if (SEGMENT.custom2 < 48) SEGMENT.custom1 = 48; // WLEDMM prevent stupid settings *binNum = SEGMENT.custom1; // Select a bin. *maxVol = SEGMENT.custom2 / 2; // Our volume comparator. @@ -6136,8 +6158,13 @@ uint16_t mode_2DWaverly(void) { um_data = simulateSound(SEGMENT.soundSim); } float volumeSmth = *(float*) um_data->u_data[0]; + float soundPressure = *(float*) um_data->u_data[9]; + float agcSensitivity= *(float*) um_data->u_data[10]; SEGMENT.fadeToBlackBy(SEGMENT.speed); + if (SEGENV.check3 && SEGENV.check2) SEGENV.check2 = false; // only one of the two at any time + if ((SEGENV.check2) && (volumeSmth > 0.5f)) volumeSmth = soundPressure; // show sound pressure instead of volume + if (SEGENV.check3) volumeSmth = 255.0 - agcSensitivity; // show AGC level instead of volume long t = millis() / 2; for (int i = 0; i < cols; i++) { @@ -6145,7 +6172,8 @@ uint16_t mode_2DWaverly(void) { uint16_t thisMax = map(thisVal, 0, 512, 0, rows); for (int j = 0; j < thisMax; j++) { - SEGMENT.addPixelColorXY(i, j, ColorFromPalette(SEGPALETTE, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND)); + if (!SEGENV.check1) + SEGMENT.addPixelColorXY(i, j, ColorFromPalette(SEGPALETTE, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND)); SEGMENT.addPixelColorXY((cols - 1) - i, (rows - 1) - j, ColorFromPalette(SEGPALETTE, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND)); } } @@ -6153,7 +6181,7 @@ uint16_t mode_2DWaverly(void) { return FRAMETIME; } // mode_2DWaverly() -static const char _data_FX_MODE_2DWAVERLY[] PROGMEM = "Waverly@Amplification,Sensitivity;;!;2v;ix=64,si=0"; // Beatsin +static const char _data_FX_MODE_2DWAVERLY[] PROGMEM = "Waverly ☾@Amplification,Sensitivity,,,,No Clouds,Sound Pressure,AGC debug;;!;2v;ix=64,si=0"; // Beatsin #endif // WLED_DISABLE_2D @@ -6282,23 +6310,46 @@ uint16_t mode_gravimeter(void) { // Gravmeter. By Andrew Tuline. um_data = simulateSound(SEGMENT.soundSim); } float volumeSmth = *(float*) um_data->u_data[0]; + // int16_t volumeRaw = *(int16_t*)um_data->u_data[1]; //WLEDMM: this variable not used here + float soundPressure = *(float*) um_data->u_data[9]; + float agcSensitivity= *(float*) um_data->u_data[10]; #ifdef SR_DEBUG uint8_t samplePeak = *(uint8_t*)um_data->u_data[3]; #endif - //SEGMENT.fade_out(240); - SEGMENT.fade_out(249); // 25% + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + } - float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0; - segmentSampleAvg *= 0.25; // divide by 4, to compensate for later "sensitivty" upscaling + float realVolume = volumeSmth; + if (SEGENV.check3 && SEGENV.check2) SEGENV.check2 = false; // only one option + if (SEGENV.check2) volumeSmth = soundPressure; + if (SEGENV.check3) volumeSmth = 255.0 - agcSensitivity; - float mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 64, 0, (SEGLEN-1)); // map to pixels availeable in current segment + SEGMENT.fade_out(253); + float sensGain = (float)(SEGMENT.intensity+2) / 257.0f; // min gain = 1/128 + if (sensGain > 0.5f) sensGain = ((sensGain -0.5f) * 3.0f) +0.5f; // extend upper range to 3x + float sensOffset = (SEGMENT.check2 && SEGMENT.intensity > 128) ? (float(SEGMENT.intensity - 128)*0.42f) : 0.0f; // slightly raise lower limit, to show more details (sound pressure only) + + float segmentSampleAvg = (volumeSmth * sensGain) - sensOffset; + if (segmentSampleAvg < 0) segmentSampleAvg = 0; // could be <0 due to sensOffset + segmentSampleAvg *= 0.25f; // divide by 4, to compensate for later "sensitivty" upscaling + float mySampleAvg = mapf(segmentSampleAvg*2.0f, 0, 64, 0, (SEGLEN-1)); // map to pixels availeable in current segment int tempsamp = constrain(mySampleAvg,0,SEGLEN-1); // Keep the sample from overflowing. uint8_t gravity = 8 - SEGMENT.speed/32; + int blendVal; + if (SEGENV.check1) blendVal = 255 - roundf((segmentSampleAvg)*6.5f); // reverse, min 48 + else blendVal = roundf(segmentSampleAvg*8.0f); + + //if ((realVolume > 1) && ((blendVal < 1) || (blendVal > 254))) blendVal = millis() % 192; // provides flickering when overtuned + //else + blendVal = constrain(blendVal, 32, 255); // and saturation for all + if (realVolume > 0.85) // hide main "bar" in silence for (int i=0; i= gravcen->topLED) @@ -6306,8 +6357,11 @@ uint16_t mode_gravimeter(void) { // Gravmeter. By Andrew Tuline. else if (gravcen->gravityCounter % gravity == 0) gravcen->topLED--; - if (gravcen->topLED > 0) { - SEGMENT.setPixelColor(gravcen->topLED, SEGMENT.color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0)); + if ((gravcen->topLED > 0) && (SEGMENT.speed < 255)){ // hide top pixel if speed = 255 + if (SEGENV.check2 || SEGENV.check3) + SEGMENT.setPixelColor(gravcen->topLED, SEGMENT.color_from_palette(max(uint16_t(millis()/16),(uint16_t)2), false, PALETTE_SOLID_WRAP, 0)); // flicker a bit slower + else + SEGMENT.setPixelColor(gravcen->topLED, SEGMENT.color_from_palette(max(uint16_t(millis()/2),(uint16_t)2), false, PALETTE_SOLID_WRAP, 0)); // normal flickering } gravcen->gravityCounter = (gravcen->gravityCounter + 1) % gravity; @@ -6320,7 +6374,7 @@ uint16_t mode_gravimeter(void) { // Gravmeter. By Andrew Tuline. return FRAMETIME; } // mode_gravimeter() -static const char _data_FX_MODE_GRAVIMETER[] PROGMEM = "Gravimeter@Rate of fall,Sensitivity;!,!;!;1v;ix=128,m12=2,si=0"; // Arc, Beatsin +static const char _data_FX_MODE_GRAVIMETER[] PROGMEM = "Gravimeter ☾@Rate of fall,Sensitivity,,,,Invert Palette,Sound Pressure,AGC debug;!,!;!;1v;ix=128,m12=2,si=0"; // Arc, Beatsin ////////////////////// @@ -6369,7 +6423,7 @@ uint16_t mode_matripix(void) { // Matripix. By Andrew Tuline. SEGENV.aux0 = secondHand; int pixBri = volumeRaw * SEGMENT.intensity / 64; - for (int i=0; ithisphase += beatsin8(6,-4,4); // You can change direction and speed individually. plasmoip->thatphase += beatsin8(7,-4,4); // Two phase values to make a complex pattern. By Andrew Tuline. - for (int i=0; ithisphase) & 0xFF)/2; thisbright += cos8(((i*(97 +(5*SEGMENT.speed/32)))+plasmoip->thatphase) & 0xFF)/2; // Let's munge the brightness a bit and animate it all with the phases. @@ -6580,6 +6634,8 @@ uint16_t mode_puddlepeak(void) { // Puddlepeak. By Andrew Tuline. SEGMENT.custom1 = *binNum; SEGMENT.custom2 = *maxVol * 2; } + if (SEGMENT.custom1 < 2) SEGMENT.custom1 = 2; // WLEDMM prevent stupid settings + if (SEGMENT.custom2 < 48) SEGMENT.custom1 = 48; // WLEDMM prevent stupid settings *binNum = SEGMENT.custom1; // Select a bin. *maxVol = SEGMENT.custom2 / 2; // Our volume comparator. @@ -6712,6 +6768,7 @@ uint16_t mode_DJLight(void) { // Written by ??? Adapted by Wil um_data = simulateSound(SEGMENT.soundSim); } uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; + float volumeSmth = *(float*)um_data->u_data[0]; if (SEGENV.call == 0) { SEGMENT.setUpLeds(); @@ -6722,17 +6779,49 @@ uint16_t mode_DJLight(void) { // Written by ??? Adapted by Wil if (SEGENV.aux0 != secondHand) { // Triggered millis timing. SEGENV.aux0 = secondHand; - SEGMENT.setPixelColor(mid, CRGB(fftResult[15]/2, fftResult[5]/2, fftResult[0]/2)); // 16-> 15 as 16 is out of bounds - CRGB color = SEGMENT.getPixelColor(mid); - SEGMENT.setPixelColor(mid, color.fadeToBlackBy(map(fftResult[1*4], 0, 255, 255, 10))); // TODO - Update + CRGB color = CRGB(0,0,0); + // color = CRGB(fftResult[15]/2, fftResult[5]/2, fftResult[0]/2); // formula from 0.13.x (10Khz): R = 3880-5120, G=240-340, B=60-100 + if (!SEGENV.check1) { + color = CRGB(fftResult[12]/2, fftResult[3]/2, fftResult[1]/2); // formula for 0.14.x (22Khz): R = 3015-3704, G=216-301, B=86-129 + } else { + // candy factory: an attempt to get more colors + color = CRGB(fftResult[11]/2 + fftResult[12]/4 + fftResult[14]/4, // red : 2412-3704 + 4479-7106 + fftResult[4]/2 + fftResult[3]/4, // green: 216-430 + fftResult[0]/4 + fftResult[1]/4 + fftResult[2]/4); // blue: 46-216 + if ((color.getLuma() < 96) && (volumeSmth >= 1.5f)) { // enhance "almost dark" pixels with yellow, based on not-yet-used channels + unsigned yello_g = (fftResult[5] + fftResult[6] + fftResult[7]) / 3; + unsigned yello_r = (fftResult[7] + fftResult[8] + fftResult[9] + fftResult[10]) / 4; + color.green += (uint8_t) yello_g / 2; + color.red += (uint8_t) yello_r / 2; + } + } - for (int i = SEGLEN - 1; i > mid; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left + if (volumeSmth < 1.0f) color = CRGB(0,0,0); // silence = black + + // make colors less "pastel", by turning up color saturation in HSV space + if (color.getLuma() > 32) { // don't change "dark" pixels + CHSV hsvColor = rgb2hsv_approximate(color); + hsvColor.v = min(max(hsvColor.v, (uint8_t)48), (uint8_t)204); // 48 < brightness < 204 + if (SEGENV.check1) + hsvColor.s = max(hsvColor.s, (uint8_t)204); // candy factory mode: strongly turn up color saturation (> 192) + else + hsvColor.s = max(hsvColor.s, (uint8_t)108); // normal mode: turn up color saturation to avoid pastels + color = hsvColor; + } + //if (color.getLuma() > 12) color.maximizeBrightness(); // for testing + + //SEGMENT.setPixelColor(mid, color.fadeToBlackBy(map(fftResult[4], 0, 255, 255, 4))); // 0.13.x fade -> 180hz-260hz + uint8_t fadeVal = map(fftResult[3], 0, 255, 255, 4); // 0.14.x fade -> 216hz-301hz + if (SEGENV.check1) fadeVal = constrain(fadeVal, 0, 176); // "candy factory" mode - avoid complete fade-out + SEGMENT.setPixelColor(mid, color.fadeToBlackBy(fadeVal)); + + for (int i = SEGLEN - 1; i > mid; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); // move to the left for (int i = 0; i < mid; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right } return FRAMETIME; } // mode_DJLight() -static const char _data_FX_MODE_DJLIGHT[] PROGMEM = "DJ Light@Speed;;;1f;m12=2,si=0"; // Arc, Beatsin +static const char _data_FX_MODE_DJLIGHT[] PROGMEM = "DJ Light@Speed,,,,,Candy Factory;;;1f;m12=2,si=0"; // Arc, Beatsin //////////////////// @@ -6747,8 +6836,8 @@ uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN. // add support for no audio um_data = simulateSound(SEGMENT.soundSim); } - float FFT_MajorPeak = *(float*) um_data->u_data[4]; - float my_magnitude = *(float*) um_data->u_data[5] / 4.0f; + float FFT_MajorPeak = *(float*)um_data->u_data[4]; + float my_magnitude = *(float*)um_data->u_data[5] / 4.0f; if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception) if (SEGENV.call == 0) SEGMENT.fill(BLACK); @@ -6762,9 +6851,17 @@ uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN. uint16_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(MAX_FREQ_LOG10 - 1.78f); // Scale log10 of frequency values to the 255 colour index. if (FFT_MajorPeak < 61.0f) pixCol = 0; // handle underflow +#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) + uint16_t bright = (int) (sqrtf(my_magnitude)*16.0f); // WLEDMM sqrt scaling, to make peaks more prominent +#else uint16_t bright = (int)my_magnitude; +#endif SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), bright)); + if (SEGMENT.speed > 228) { // WLEDMM looks nice in 2D + SEGMENT.blur(5*(SEGMENT.speed - 224)); + SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), bright)); + } return FRAMETIME; } // mode_freqmap() @@ -6781,7 +6878,7 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch um_data = simulateSound(SEGMENT.soundSim); } float FFT_MajorPeak = *(float*)um_data->u_data[4]; - float volumeSmth = *(float*) um_data->u_data[0]; + float volumeSmth = *(float*)um_data->u_data[0]; if (SEGENV.call == 0) { SEGMENT.setUpLeds(); @@ -6818,7 +6915,7 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch // shift the pixels one pixel up SEGMENT.setPixelColor(0, color); - for (int i = SEGLEN - 1; i > 0; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left + for (int i = SEGLEN - 1; i > 0; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left } return FRAMETIME; @@ -6839,8 +6936,8 @@ uint16_t mode_freqpixels(void) { // Freqpixel. By Andrew Tuline. // add support for no audio um_data = simulateSound(SEGMENT.soundSim); } - float FFT_MajorPeak = *(float*) um_data->u_data[4]; - float my_magnitude = *(float*) um_data->u_data[5] / 16.0f; + float FFT_MajorPeak = *(float*)um_data->u_data[4]; + float my_magnitude = *(float*)um_data->u_data[5] / 16.0f; if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception) uint16_t fadeRate = 2*SEGMENT.speed - SEGMENT.speed*SEGMENT.speed/255; // Get to 255 as quick as you can. @@ -6882,8 +6979,8 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun // add support for no audio um_data = simulateSound(SEGMENT.soundSim); } - float FFT_MajorPeak = *(float*) um_data->u_data[4]; - float volumeSmth = *(float*) um_data->u_data[0]; + float FFT_MajorPeak = *(float*)um_data->u_data[4]; + float volumeSmth = *(float*)um_data->u_data[0]; if (SEGENV.call == 0) { SEGMENT.setUpLeds(); @@ -6944,16 +7041,16 @@ uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline. // add support for no audio um_data = simulateSound(SEGMENT.soundSim); } - float FFT_MajorPeak = *(float*) um_data->u_data[4]; - float volumeSmth = *(float*) um_data->u_data[0]; + float FFT_MajorPeak = *(float*)um_data->u_data[4]; + float volumeSmth = *(float*)um_data->u_data[0]; if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception) SEGMENT.fade_out(250); - float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0; + float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f; segmentSampleAvg *= 0.125; // divide by 8, to compensate for later "sensitivty" upscaling - float mySampleAvg = mapf(segmentSampleAvg*2.0, 0,32, 0, (float)SEGLEN/2.0); // map to pixels availeable in current segment + float mySampleAvg = mapf(segmentSampleAvg*2.0f, 0,32, 0, (float)SEGLEN/2.0); // map to pixels availeable in current segment int tempsamp = constrain(mySampleAvg,0,SEGLEN/2); // Keep the sample from overflowing. uint8_t gravity = 8 - SEGMENT.speed/32; @@ -7039,7 +7136,7 @@ uint16_t mode_rocktaves(void) { // Rocktaves. Same note from eac } frTemp -=132; // This should give us a base musical note of C3 - frTemp = fabsf(frTemp * 2.1); // Fudge factors to compress octave range starting at 0 and going to 255; + frTemp = fabsf(frTemp * 2.1f); // Fudge factors to compress octave range starting at 0 and going to 255; uint16_t i = map(beatsin8(8+octCount*4, 0, 255, 0, octCount*8), 0, 255, 0, SEGLEN-1); i = constrain(i, 0, SEGLEN-1); @@ -7078,6 +7175,9 @@ uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tulin SEGMENT.custom2 = *maxVol * 2; } + if (SEGMENT.custom1 < 2) SEGMENT.custom1 = 2; // WLEDMM prevent stupid settings + if (SEGMENT.custom2 < 48) SEGMENT.custom1 = 48; // WLEDMM prevent stupid settings + *binNum = SEGMENT.custom1; // Select a bin. *maxVol = SEGMENT.custom2 / 2; // Our volume comparator. @@ -7094,7 +7194,7 @@ uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tulin } else { SEGMENT.setPixelColor(SEGLEN-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(pixCol+SEGMENT.intensity, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude)); } - for (int i=0; i(SEGENV.data); //array of previous bar heights per frequency band @@ -7138,12 +7239,39 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma. int fadeoutDelay = (256 - SEGMENT.speed) / 64; if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fadeToBlackBy(SEGMENT.speed); + uint16_t lastBandHeight = 0; // WLEDMM: for smoothing out bars + + //WLEDMM: evenly ditribut bands + float bandwidth = (float)cols / NUM_BANDS; + float remaining = bandwidth; + uint8_t band = 0; for (int x=0; x < cols; x++) { - uint8_t band = map(x, 0, cols-1, 0, NUM_BANDS - 1); - if (NUM_BANDS < 16) band = map(band, 0, NUM_BANDS - 1, 0, 15); // always use full range. comment out this line to get the previous behaviour. - band = constrain(band, 0, 15); - uint16_t colorIndex = band * 17; - uint16_t barHeight = map(fftResult[band], 0, 255, 0, rows); // do not subtract -1 from rows here + //WLEDMM if not enough remaining + if (remaining < 1) {band++; remaining+= bandwidth;} //increase remaining but keep the current remaining + remaining--; //consume remaining + + // Serial.printf("x %d b %d n %d w %f %f\n", x, band, NUM_BANDS, bandwidth, remaining); + uint8_t frBand = ((NUM_BANDS < 16) && (NUM_BANDS > 1)) ? map(band, 0, NUM_BANDS - 1, 0, 15):band; // always use full range. comment out this line to get the previous behaviour. + // frBand = constrain(frBand, 0, 15); //WLEDMM can never be out of bounds (I think...) + uint16_t colorIndex = frBand * 17; //WLEDMM 0.255 + uint16_t bandHeight = fftResult[frBand]; // WLEDMM we use the original ffResult, to preserve accuracy + + // WLEDMM begin - smooth out bars + if ((x > 0) && (x < (cols-1)) && (SEGMENT.check2)) { + // get height of next (right side) bar + uint8_t nextband = (remaining < 1)? band +1: band; + nextband = constrain(nextband, 0, 15); // just to be sure + frBand = ((NUM_BANDS < 16) && (NUM_BANDS > 1)) ? map(nextband, 0, NUM_BANDS - 1, 0, 15):nextband; // always use full range. comment out this line to get the previous behaviour. + uint16_t nextBandHeight = fftResult[frBand]; + // smooth Band height + bandHeight = (7*bandHeight + 3*lastBandHeight + 3*nextBandHeight) / 12; // yeees, its 12 not 13 (10% amplification) + bandHeight = constrain(bandHeight, 0, 255); // remove potential over/underflows + colorIndex = map(x, 0, cols-1, 0, 255); //WLEDMM + } + lastBandHeight = bandHeight; // remember BandHeight (left side) for next iteration + uint16_t barHeight = map(bandHeight, 0, 255, 0, rows); // Now we map bandHeight to barHeight. do not subtract -1 from rows here + // WLEDMM end + if (barHeight > rows) barHeight = rows; // WLEDMM map() can "overshoot" due to rounding errors if (barHeight > previousBarHeight[x]) previousBarHeight[x] = barHeight; //drive the peak up @@ -7155,7 +7283,7 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma. ledColor = SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0); SEGMENT.setPixelColorXY(x, rows-1 - y, ledColor); } - if ((previousBarHeight[x] > 0) && (previousBarHeight[x] < rows)) // WLEDMM avoid "overshooting" into other segments + if ((SEGMENT.intensity < 255) && (previousBarHeight[x] > 0) && (previousBarHeight[x] < rows)) // WLEDMM avoid "overshooting" into other segments SEGMENT.setPixelColorXY(x, rows - previousBarHeight[x], (SEGCOLOR(2) != BLACK) ? SEGCOLOR(2) : ledColor); if (rippleTime && previousBarHeight[x]>0) previousBarHeight[x]--; //delay/ripple effect @@ -7163,13 +7291,13 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma. #ifdef SR_DEBUG // WLEDMM: abuse top left/right pixels for peak detection debugging - SEGMENT.setPixelColorXY(cols-1, rows-1, (samplePeak > 0) ? GREEN : BLACK); - if (samplePeak > 0) SEGMENT.setPixelColorXY(0, rows-1, GREEN); + SEGMENT.setPixelColorXY(cols-1, 0, (samplePeak > 0) ? GREEN : BLACK); + if (samplePeak > 0) SEGMENT.setPixelColorXY(0, 0, GREEN); // WLEDMM end #endif return FRAMETIME; } // mode_2DGEQ() -static const char _data_FX_MODE_2DGEQ[] PROGMEM = "GEQ@Fade speed,Ripple decay,# of bands,,,Color bars;!,,Peaks;!;2f;c1=255,c2=64,pal=11,si=0"; // Beatsin +static const char _data_FX_MODE_2DGEQ[] PROGMEM = "GEQ ☾@Fade speed,Ripple decay,# of bands,,,Color bars,Smooth bars ☾;!,,Peaks;!;2f;c1=255,c2=64,pal=11,si=0"; // Beatsin ///////////////////////// diff --git a/wled00/FX.h b/wled00/FX.h index 97019624..b7bd3e06 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -72,7 +72,11 @@ #ifndef MAX_NUM_SEGMENTS #define MAX_NUM_SEGMENTS 32 #endif - #define MAX_SEGMENT_DATA 32767 + #if defined(ARDUINO_ARCH_ESP32S2) + #define MAX_SEGMENT_DATA 24576 + #else + #define MAX_SEGMENT_DATA 32767 + #endif #endif /* How much data bytes each segment should max allocate to leave enough space for other segments, @@ -143,7 +147,7 @@ #define FX_MODE_SAW 16 #define FX_MODE_TWINKLE 17 #define FX_MODE_DISSOLVE 18 -#define FX_MODE_DISSOLVE_RANDOM 19 +#define FX_MODE_DISSOLVE_RANDOM 19 // candidate for removal (use Dissolve with with check 3) #define FX_MODE_SPARKLE 20 #define FX_MODE_FLASH_SPARKLE 21 #define FX_MODE_HYPER_SPARKLE 22 @@ -227,7 +231,7 @@ #define FX_MODE_HEARTBEAT 100 #define FX_MODE_PACIFICA 101 #define FX_MODE_CANDLE_MULTI 102 -#define FX_MODE_SOLID_GLITTER 103 +#define FX_MODE_SOLID_GLITTER 103 // candidate for removal (use glitter) #define FX_MODE_SUNRISE 104 #define FX_MODE_PHASED 105 #define FX_MODE_TWINKLEUP 106 @@ -241,7 +245,7 @@ // #define FX_MODE_CANDY_CANE 114 // removed in 0.14! #define FX_MODE_BLENDS 115 #define FX_MODE_TV_SIMULATOR 116 -#define FX_MODE_DYNAMIC_SMOOTH 117 +#define FX_MODE_DYNAMIC_SMOOTH 117 // candidate for removal (check3 in dynamic) // new 0.14 2D effects #define FX_MODE_2DSPACESHIPS 118 //gap fill @@ -312,7 +316,7 @@ #define FX_MODE_WAVESINS 184 #define FX_MODE_ROCKTAVES 185 #define FX_MODE_2DAKEMI 186 -#define FX_MODE_CUSTOMEFFECT 187 //WLEDMM Custom Effects +#define FX_MODE_ARTIFX 187 //WLEDMM ARTIFX #define MODE_COUNT 188 @@ -322,8 +326,8 @@ typedef enum mapping1D2D { M12_pArc = 2, M12_pCorner = 3, M12_jMap = 4, //WLEDMM jMap - M12_sCircle = 5, //WLEDMM jMap - M12_sBlock = 6 //WLEDMM jMap + M12_sCircle = 5, //WLEDMM Circle + M12_sBlock = 6 //WLEDMM Block } mapping1D2D_t; // segment, 72 bytes @@ -378,7 +382,7 @@ typedef struct Segment { CRGB* leds; // local leds[] array (may be a pointer to global) static CRGB *_globalLeds; // global leds[] array static uint16_t maxWidth, maxHeight; // these define matrix width & height (max. segment dimensions) - void *jMap; //WLEDMM jMap + void *jMap = nullptr; //WLEDMM jMap private: union { @@ -465,7 +469,7 @@ typedef struct Segment { _dataLen(0), _t(nullptr) { - refreshLightCapabilities(); + //refreshLightCapabilities(); } Segment(uint16_t sStartX, uint16_t sStopX, uint16_t sStartY, uint16_t sStopY) : Segment(sStartX, sStopX) { @@ -501,6 +505,9 @@ typedef struct Segment { inline bool isSelected(void) const { return selected; } inline bool isActive(void) const { return stop > start; } inline bool is2D(void) const { return (width()>1 && height()>1); } + inline bool hasRGB(void) const { return _isRGB; } + inline bool hasWhite(void) const { return _hasW; } + inline bool isCCT(void) const { return _isCCT; } inline uint16_t width(void) const { return stop - start; } // segment width in physical pixels (length if 1D) inline uint16_t height(void) const { return stopY - startY; } // segment height (if 2D) in physical pixels inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels @@ -609,6 +616,7 @@ typedef struct Segment { void blur2d(fract8 blur_amount) { blur(blur_amount); } void fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); } void nscale8(uint8_t scale); + bool jsonToPixels(char *name, uint8_t fileNr); #else uint16_t XY(uint16_t x, uint16_t y) { return x; } void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(x, c); } @@ -668,7 +676,6 @@ class WS2812FX { // 96 bytes isMatrix(false), #ifndef WLED_DISABLE_2D panels(1), - matrix{0,0,0,0}, #endif // semi-private (just obscured) used in effect functions through macros _currentPalette(CRGBPalette16(CRGB::Black)), @@ -721,7 +728,6 @@ class WS2812FX { // 96 bytes finalizeInit(), service(void), setMode(uint8_t segid, uint8_t m), - setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0), setColor(uint8_t slot, uint32_t c), setCCT(uint16_t k), setBrightness(uint8_t b, bool direct = false), @@ -731,15 +737,16 @@ class WS2812FX { // 96 bytes setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 1, uint8_t spacing = 0, uint16_t offset = UINT16_MAX, uint16_t startY=0, uint16_t stopY=1), setMainSegmentId(uint8_t n), restartRuntime(), - resetSegments(), + resetSegments(bool boundsOnly = false), //WLEDMM add boundsOnly makeAutoSegments(bool forceReset = false), fixInvalidSegments(), setPixelColor(int n, uint32_t c), show(void), setTargetFps(uint8_t fps), - deserializeMap(uint8_t n=0); + enumerateLedmaps(); //WLEDMM (from fcn_declare) - void fill(uint32_t c) { for (int i = 0; i < _length; i++) setPixelColor(i, c); } // fill whole strip with color (inline) + void setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setColor(slot, RGBW32(r,g,b,w)); } + void fill(uint32_t c) { for (int i = 0; i < getLengthTotal(); i++) setPixelColor(i, c); } // fill whole strip with color (inline) void addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name); // add effect to the list; defined in FX.cpp void setupEffectData(void); // add default effects to the list; defined in FX.cpp @@ -757,7 +764,8 @@ class WS2812FX { // 96 bytes hasCCTBus(void), // return true if the strip is being sent pixel updates isUpdating(void), - useLedsArray = true; //WLEDMM default true as recommended for overlapping segments + deserializeMap(uint8_t n=0), + useLedsArray = false; inline bool isServicing(void) { return _isServicing; } inline bool hasWhiteChannel(void) {return _hasWhiteChannel;} @@ -786,17 +794,17 @@ class WS2812FX { // 96 bytes ablMilliampsMax, currentMilliamps, getLengthPhysical(void), + getLengthTotal(void), // will include virtual/nonexistent pixels in matrix getFps(); inline uint16_t getFrameTime(void) { return _frametime; } inline uint16_t getMinShowDelay(void) { return MIN_SHOW_DELAY; } - inline uint16_t getLengthTotal(void) { return _length; } + inline uint16_t getLength(void) { return _length; } // 2D matrix may have less pixels than W*H inline uint16_t getTransition(void) { return _transitionDur; } uint32_t now, timebase, - currentColor(uint32_t colorNew, uint8_t tNr), getPixelColor(uint16_t); inline uint32_t getLastShow(void) { return _lastShow; } @@ -821,19 +829,28 @@ class WS2812FX { // 96 bytes #define WLED_MAX_PANELS 64 uint8_t panels, - panelsH, //WLEDMM quick fix - panelsV; //WLEDMM quick fix + panelsH, //WLEDMM needs to be stored as well + panelsV; //WLEDMM needs to be stored as well + //WLEDMM: keep storing basic 2d setup + bool + bOrA = false; //WLEDMM basic or advanced, default basic struct { bool bottomStart : 1; bool rightStart : 1; bool vertical : 1; bool serpentine : 1; } matrix; + struct { + bool bottomStart : 1; + bool rightStart : 1; + bool vertical : 1; + bool serpentine : 1; + } panelO; //panelOrientation typedef struct panel_t { - uint16_t xOffset; // x offset relative to the top left of matrix in LEDs - uint16_t yOffset; // y offset relative to the top left of matrix in LEDs + uint8_t xOffset; // x offset relative to the top left of matrix in LEDs. WLEDMM 8 bits/256 is enough + uint8_t yOffset; // y offset relative to the top left of matrix in LEDs. WLEDMM 8 bits/256 is enough uint8_t width; // width of the panel uint8_t height; // height of the panel union { @@ -845,12 +862,19 @@ class WS2812FX { // 96 bytes bool serpentine : 1; // is serpentine? }; }; + panel_t() + : xOffset(0) + , yOffset(0) + , width(8) + , height(8) + , options(0) + {} } Panel; std::vector panel; #endif void - setUpMatrix(bool reset = true), //WLEDMM: add reset option to switch on/off reset of customMappingTable + setUpMatrix(), setPixelColorXY(int x, int y, uint32_t c); // outsmart the compiler :) by correctly overloading diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 07116d0b..45eb6ac1 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -34,14 +34,12 @@ // note: matrix may be comprised of multiple panels each with different orientation // but ledmap takes care of that. ledmap is constructed upon initialization // so matrix should disable regular ledmap processing -void WS2812FX::setUpMatrix(bool reset) { +void WS2812FX::setUpMatrix() { #ifndef WLED_DISABLE_2D // erase old ledmap, just in case. - if (reset) { //WLEDMM: add reset option to switch on/off reset of customMappingTable - if (customMappingTable != nullptr) delete[] customMappingTable; - customMappingTable = nullptr; - customMappingSize = 0; - } + if (customMappingTable != nullptr) delete[] customMappingTable; + customMappingTable = nullptr; + customMappingSize = 0; // isMatrix is set in cfg.cpp or set.cpp if (isMatrix) { @@ -60,31 +58,59 @@ void WS2812FX::setUpMatrix(bool reset) { // safety check if (Segment::maxWidth * Segment::maxHeight > MAX_LEDS || Segment::maxWidth <= 1 || Segment::maxHeight <= 1) { - DEBUG_PRINTLN(F("2D Bounds error.")); + DEBUG_PRINTF("2D Bounds error. %d x %d\n", Segment::maxWidth, Segment::maxHeight); isMatrix = false; Segment::maxWidth = _length; Segment::maxHeight = 1; panels = 0; panel.clear(); // release memory allocated by panels + resetSegments(true); //WLEDMM bounds only return; } - if (reset) { //WLEDMM: add reset option to switch on/off reset of customMappingTable - customMappingTable = new uint16_t[Segment::maxWidth * Segment::maxHeight]; - //WLEDMM: init customMappingTable with a 1:1 mapping (for customMappingTable[customMappingTable[x]]) - for (uint16_t i=0; i(); + gapSize = map.size(); + if (!map.isNull() && gapSize >= customMappingSize) { // not an empty map + gapTable = new int8_t[gapSize]; + if (gapTable) for (size_t i = 0; i < gapSize; i++) { + gapTable[i] = constrain(map[i], -1, 1); + } + } + } + DEBUG_PRINTLN(F("Gaps loaded.")); + releaseJSONBufferLock(); + } uint16_t x, y, pix=0; //pixel for (size_t pan = 0; pan < panel.size(); pan++) { @@ -92,17 +118,22 @@ void WS2812FX::setUpMatrix(bool reset) { uint16_t h = p.vertical ? p.height : p.width; uint16_t v = p.vertical ? p.width : p.height; for (size_t j = 0; j < v; j++){ - for(size_t i = 0; i < h; i++, pix++) { + for(size_t i = 0; i < h; i++) { y = (p.vertical?p.rightStart:p.bottomStart) ? v-j-1 : j; x = (p.vertical?p.bottomStart:p.rightStart) ? h-i-1 : i; x = p.serpentine && j%2 ? h-x-1 : x; - customMappingTable[customMappingTable[(p.yOffset + (p.vertical?x:y)) * Segment::maxWidth + p.xOffset + (p.vertical?y:x)]] = pix; //WLEDMM: allow for 2 transitions if reset = false (ledmap and logical to physical) + size_t index = (p.yOffset + (p.vertical?x:y)) * Segment::maxWidth + p.xOffset + (p.vertical?y:x); + if (!gapTable || (gapTable && gapTable[index] > 0)) customMappingTable[index] = pix; // a useful pixel (otherwise -1 is retained) + if (!gapTable || (gapTable && gapTable[index] >= 0)) pix++; // not a missing pixel } } } + // delete gap array as we no longer need it + if (gapTable) delete[] gapTable; + #ifdef WLED_DEBUG - DEBUG_PRINT(F("Matrix ledmap:")); + DEBUG_PRINTF("Matrix ledmap: \n"); for (uint16_t i=0; i= customMappingSize) return; #else uint16_t index = x; - if (index >= _length) return; #endif if (index < customMappingSize) index = customMappingTable[index]; + if (index >= _length) return; busses.setPixelColor(index, col); } @@ -143,12 +173,11 @@ void IRAM_ATTR_YN WS2812FX::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) { #ifndef WLED_DISABLE_2D uint16_t index = (y * Segment::maxWidth + x); - if (index >= customMappingSize) return 0; // customMappingSize is always W * H of matrix in 2D setup #else uint16_t index = x; - if (index >= _length) return 0; #endif if (index < customMappingSize) index = customMappingTable[index]; + if (index >= _length) return 0; return busses.getPixelColor(index); } @@ -523,6 +552,42 @@ void Segment::drawArc(uint16_t x0, uint16_t y0, uint16_t radius, uint32_t color, } } +bool Segment::jsonToPixels(char * name, uint8_t fileNr) { + char fileName[32]; + //WLEDMM: als support segment name ledmaps + bool isFile = false;; + // strcpy_P(fileName, PSTR("/mario")); + sprintf(fileName, "/%s%d.json", name, fileNr); //WLEDMM: trick to not include 0 in ledmap.json + // strcat(fileName, ".json"); + isFile = WLED_FS.exists(fileName); + + if (!isFile) { + return false; + } + + if (!requestJSONBufferLock(23)) return false; + + if (!readObjectFromFile(fileName, nullptr, &doc)) { + releaseJSONBufferLock(); + return false; //if file does not exist just exit + } + + JsonArray map = doc[F("seg")][F("i")]; + + if (!map.isNull() && map.size()) { // not an empty map + + for (uint16_t i=0; i= 0 || x0 < cols) && ((bits>>(j+(8-w))) & 0x01)) { // bit set & drawing on-screen - addPixelColorXY(x0, y0, col); + setPixelColorXY(x0, y0, col); } } } diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index c3e6ce8c..c086809d 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -81,7 +81,7 @@ uint16_t Segment::maxHeight = 1; // copy constructor Segment::Segment(const Segment &orig) { //DEBUG_PRINTLN(F("-- Copy segment constructor --")); - memcpy(this, &orig, sizeof(Segment)); + memcpy((void*)this, (void*)&orig, sizeof(Segment)); name = nullptr; data = nullptr; _dataLen = 0; @@ -97,7 +97,7 @@ Segment::Segment(const Segment &orig) { // move constructor Segment::Segment(Segment &&orig) noexcept { //DEBUG_PRINTLN(F("-- Move segment constructor --")); - memcpy(this, &orig, sizeof(Segment)); + memcpy((void*)this, (void*)&orig, sizeof(Segment)); orig.name = nullptr; orig.data = nullptr; orig._dataLen = 0; @@ -116,7 +116,7 @@ Segment& Segment::operator= (const Segment &orig) { if (leds && !Segment::_globalLeds) free(leds); deallocateData(); // copy source - memcpy(this, &orig, sizeof(Segment)); + memcpy((void*)this, (void*)&orig, sizeof(Segment)); // erase pointers to allocated data name = nullptr; data = nullptr; @@ -141,7 +141,7 @@ Segment& Segment::operator= (Segment &&orig) noexcept { deallocateData(); // free old runtime data if (_t) delete _t; if (leds && !Segment::_globalLeds) free(leds); - memcpy(this, &orig, sizeof(Segment)); + memcpy((void*)this, (void*)&orig, sizeof(Segment)); orig.name = nullptr; orig.data = nullptr; orig._dataLen = 0; @@ -188,7 +188,8 @@ void Segment::deallocateData() { void Segment::resetIfRequired() { if (reset) { if (leds && !Segment::_globalLeds) { free(leds); leds = nullptr; } - //if (_t) { delete _t; _t = nullptr; transitional = false; } + if (transitional && _t) { transitional = false; delete _t; _t = nullptr; } + deallocateData(); next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0; reset = false; // setOption(SEG_OPTION_RESET, false); } @@ -230,14 +231,34 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) { case FX_MODE_NOISE16_4 : pal = 26; break; // landscape 33 case FX_MODE_GLITTER : pal = 11; break; // rainbow colors case FX_MODE_SUNRISE : pal = 35; break; // heat palette - case FX_MODE_FLOW : pal = 6; break; // party + case FX_MODE_RAILWAY : pal = 3; break; // prim + sec } switch (pal) { case 0: //default palette. Exceptions for specific effects above targetPalette = PartyColors_p; break; case 1: {//periodically replace palette with a random one. Transition palette change in 500ms uint32_t timeSinceLastChange = millis() - _lastPaletteChange; - if (timeSinceLastChange > 5000 /*+ ((uint32_t)(255-intensity))*100*/) { + if (timeSinceLastChange > randomPaletteChangeTime * 1000U) { + prevRandomPalette = randomPalette; + randomPalette = CRGBPalette16( + CHSV(random8(), random8(160, 255), random8(128, 255)), + CHSV(random8(), random8(160, 255), random8(128, 255)), + CHSV(random8(), random8(160, 255), random8(128, 255)), + CHSV(random8(), random8(160, 255), random8(128, 255))); + _lastPaletteChange = millis(); + timeSinceLastChange = 0; + } + + //WLEDMM: smooth transitions of palettes instead of every 5 sec with short transition + for (int i=0; i< 16; i++) { + targetPalette[i].r = prevRandomPalette[i].r*(5000-timeSinceLastChange)/5000 + randomPalette[i].r*timeSinceLastChange/5000; + targetPalette[i].g = prevRandomPalette[i].g*(5000-timeSinceLastChange)/5000 + randomPalette[i].g*timeSinceLastChange/5000; + targetPalette[i].b = prevRandomPalette[i].b*(5000-timeSinceLastChange)/5000 + randomPalette[i].b*timeSinceLastChange/5000; + } + break;} + case 74: {//periodically replace palette with a random one. Transition palette change in 500ms + uint32_t timeSinceLastChange = millis() - _lastPaletteChange; + if (timeSinceLastChange > randomPaletteChangeTime * 1000U) { prevRandomPalette = randomPalette; randomPalette = CRGBPalette16( CHSV(random8(), random8(160, 255), random8(128, 255)), @@ -401,8 +422,8 @@ void Segment::set(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t o markForReset(); return; } - if (i1 < Segment::maxWidth) start = i1; // Segment::maxWidth equals strip.getLengthTotal() for 1D - stop = i2 > Segment::maxWidth ? Segment::maxWidth : MAX(1,i2); + if (i1 < Segment::maxWidth || (i1 >= Segment::maxWidth*Segment::maxHeight && i1 < strip.getLengthTotal())) start = i1; // Segment::maxWidth equals strip.getLengthTotal() for 1D + stop = i2 > Segment::maxWidth*Segment::maxHeight ? MIN(i2,strip.getLengthTotal()) : (i2 > Segment::maxWidth ? Segment::maxWidth : MAX(1,i2)); startY = 0; stopY = 1; #ifndef WLED_DISABLE_2D @@ -423,6 +444,10 @@ void Segment::set(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t o bool Segment::setColor(uint8_t slot, uint32_t c) { //returns true if changed if (slot >= NUM_COLORS || c == colors[slot]) return false; + if (!_isRGB && !_hasW) { + if (slot == 0 && c == BLACK) return false; // on/off segment cannot have primary color black + if (slot == 1 && c != BLACK) return false; // on/off segment cannot have secondary color non black + } if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change colors[slot] = c; stateChanged = true; // send UDP/WS broadcast @@ -556,12 +581,12 @@ class JMapC { char previousSegmentName[50] = ""; ~JMapC() { - Serial.println("~JMapC"); + USER_PRINTLN("~JMapC"); deletejVectorMap(); } void deletejVectorMap() { if (jVectorMap.size() > 0) { - Serial.println("delete jVectorMap"); + USER_PRINTLN("delete jVectorMap"); for (size_t i=0; i>16; // hack to allow running on virtual strips (2D segment columns/rows) +#endif i &= 0xFFFF; if (i >= virtualLength() || i<0) return; // if pixel would fall out of segment just exit @@ -847,12 +874,14 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATT } return; } else if (Segment::maxHeight!=1 && (width()==1 || height()==1)) { - // we have a vertical or horizontal 1D segment (WARNING: virtual...() may be transposed) - int x = 0, y = 0; - if (virtualHeight()>1) y = i; - if (virtualWidth() >1) x = i; - setPixelColorXY(x, y, col); - return; + if (start < Segment::maxWidth*Segment::maxHeight) { + // we have a vertical or horizontal 1D segment (WARNING: virtual...() may be transposed) + int x = 0, y = 0; + if (virtualHeight()>1) y = i; + if (virtualWidth() >1) x = i; + setPixelColorXY(x, y, col); + return; + } } #endif @@ -931,7 +960,9 @@ void Segment::setPixelColor(float i, uint32_t col, bool aa) uint32_t Segment::getPixelColor(int i) { +#ifndef WLED_DISABLE_2D int vStrip = i>>16; +#endif i &= 0xFFFF; #ifndef WLED_DISABLE_2D @@ -1018,29 +1049,46 @@ uint8_t Segment::differs(Segment& b) const { } void Segment::refreshLightCapabilities() { - uint8_t capabilities = 0x01; + uint8_t capabilities = 0; + uint16_t segStartIdx = 0xFFFFU; + uint16_t segStopIdx = 0; + + if (start < Segment::maxWidth * Segment::maxHeight) { + // we are withing 2D matrix (includes 1D segments) + for (int y = startY; y < stopY; y++) for (int x = start; x < stop; x++) { + uint16_t index = x + Segment::maxWidth * y; + if (index < strip.customMappingSize) index = strip.customMappingTable[index]; // convert logical address to physical + if (index < 0xFFFFU) { + if (segStartIdx > index) segStartIdx = index; + if (segStopIdx < index) segStopIdx = index; + } + if (segStartIdx == segStopIdx) segStopIdx++; // we only have 1 pixel segment + } + } else { + // we are on the strip located after the matrix + segStartIdx = start; + segStopIdx = stop; + } for (uint8_t b = 0; b < busses.getNumBusses(); b++) { Bus *bus = busses.getBus(b); if (bus == nullptr || bus->getLength()==0) break; if (!bus->isOk()) continue; - if (bus->getStart() >= stop) continue; - if (bus->getStart() + bus->getLength() <= start) continue; + if (bus->getStart() >= segStopIdx) continue; + if (bus->getStart() + bus->getLength() <= segStartIdx) continue; - uint8_t type = bus->getType(); - if (type == TYPE_ONOFF || type == TYPE_ANALOG_1CH || (!cctFromRgb && type == TYPE_ANALOG_2CH)) capabilities &= 0xFE; // does not support RGB - if (bus->isRgbw()) capabilities |= 0x02; // segment supports white channel - if (!cctFromRgb) { - switch (type) { - case TYPE_ANALOG_5CH: - case TYPE_ANALOG_2CH: - capabilities |= 0x04; //segment supports white CCT - } + //uint8_t type = bus->getType(); + if (bus->hasRGB() || (cctFromRgb && bus->hasCCT())) capabilities |= SEG_CAPABILITY_RGB; + if (!cctFromRgb && bus->hasCCT()) capabilities |= SEG_CAPABILITY_CCT; + if (correctWB && (bus->hasRGB() || bus->hasCCT())) capabilities |= SEG_CAPABILITY_CCT; //white balance correction (CCT slider) + if (bus->hasWhite()) { + uint8_t aWM = Bus::getGlobalAWMode() == AW_GLOBAL_DISABLED ? bus->getAutoWhiteMode() : Bus::getGlobalAWMode(); + bool whiteSlider = (aWM == RGBW_MODE_DUAL || aWM == RGBW_MODE_MANUAL_ONLY); // white slider allowed + // if auto white calculation from RGB is active (Accurate/Brighter), force RGB controls even if there are no RGB busses + if (!whiteSlider) capabilities |= SEG_CAPABILITY_RGB; + // if auto white calculation from RGB is disabled/optional (None/Dual), allow white channel adjustments + if ( whiteSlider) capabilities |= SEG_CAPABILITY_W; } - if (correctWB && !(type == TYPE_ANALOG_1CH || type == TYPE_ONOFF)) capabilities |= 0x04; //white balance correction (uses CCT slider) - uint8_t aWM = Bus::getAutoWhiteMode()<255 ? Bus::getAutoWhiteMode() : bus->getAWMode(); - bool whiteSlider = (aWM == RGBW_MODE_DUAL || aWM == RGBW_MODE_MANUAL_ONLY); // white slider allowed - if (bus->isRgbw() && (whiteSlider || !(capabilities & 0x01))) capabilities |= 0x08; // allow white channel adjustments (AWM allows or is not RGB) } _capabilities = capabilities; } @@ -1164,7 +1212,7 @@ void Segment::blur(uint8_t blur_amount) * The colours are a transition r -> g -> b -> back to r * Inspired by the Adafruit examples. */ -uint32_t Segment::color_wheel(uint8_t pos) { // TODO +uint32_t Segment::color_wheel(uint8_t pos) { if (palette) return color_from_palette(pos, false, true, 0); pos = 255 - pos; if(pos < 85) { @@ -1268,6 +1316,65 @@ uint8_t * Segment::getAudioPalette(int pal) { // WS2812FX class implementation /////////////////////////////////////////////////////////////////////////////// +//WLEDMM from util.cpp +// enumerate all ledmapX.json files on FS and extract ledmap names if existing +void WS2812FX::enumerateLedmaps() { + ledMaps = 1; + for (int i=1; i<10; i++) { + char fileName[33]; + snprintf_P(fileName, sizeof(fileName), PSTR("/ledmap%d.json"), i); + bool isFile = WLED_FS.exists(fileName); + + #ifndef ESP8266 + if (ledmapNames[i-1]) { //clear old name + delete[] ledmapNames[i-1]; + ledmapNames[i-1] = nullptr; + } + #endif + + if (isFile) { + ledMaps |= 1 << i; + + #ifndef ESP8266 + if (requestJSONBufferLock(21)) { + if (readObjectFromFile(fileName, nullptr, &doc)) { + size_t len = 0; + if (!doc["n"].isNull()) { + // name field exists + const char *name = doc["n"].as(); + if (name != nullptr) len = strlen(name); + if (len > 0 && len < 33) { + ledmapNames[i-1] = new char[len+1]; + if (ledmapNames[i-1]) strlcpy(ledmapNames[i-1], name, 33); + } + } + if (!ledmapNames[i-1]) { + char tmp[33]; + snprintf_P(tmp, 32, PSTR("ledmap%d.json"), i); + len = strlen(tmp); + ledmapNames[i-1] = new char[len+1]; + if (ledmapNames[i-1]) strlcpy(ledmapNames[i-1], tmp, 33); + } + } + releaseJSONBufferLock(); + } + #endif + } + } + //WLEDMM add segment names to be used as ledmap names + uint8_t segment_index = 0; + for (segment &seg : _segments) { + if (seg.name != nullptr && strcmp(seg.name, "") != 0) { + char fileName[33]; + snprintf_P(fileName, sizeof(fileName), PSTR("/lm%s.json"), seg.name); + bool isFile = WLED_FS.exists(fileName); + if (isFile) ledMaps |= 1 << (10+segment_index); + } + segment_index++; + } +} + + //do not call this method from system context (network callback) void WS2812FX::finalizeInit(void) { @@ -1308,7 +1415,7 @@ void WS2812FX::finalizeInit(void) if (bus == nullptr) continue; if (bus->getStart() + bus->getLength() > MAX_LEDS) break; //RGBW mode is enabled if at least one of the strips is RGBW - _hasWhiteChannel |= bus->isRgbw(); + _hasWhiteChannel |= bus->hasWhite(); //refresh is required to remain off if at least one of the strips requires the refresh. _isOffRefreshRequired |= bus->isOffRefreshRequired(); uint16_t busEnd = bus->getStart() + bus->getLength(); @@ -1322,10 +1429,11 @@ void WS2812FX::finalizeInit(void) #endif } - if (!isMatrix) { // if 2D then max values defined in setUpMatrix() using panel set-up + if (isMatrix) setUpMatrix(); + else { Segment::maxWidth = _length; Segment::maxHeight = 1; - } + } //initialize leds array. TBD: realloc if nr of leds change if (Segment::_globalLeds) { @@ -1334,17 +1442,20 @@ void WS2812FX::finalizeInit(void) Segment::_globalLeds = nullptr; } if (useLedsArray) { + size_t arrSize = sizeof(CRGB) * getLengthTotal(); #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM) if (psramFound()) - Segment::_globalLeds = (CRGB*) ps_malloc(sizeof(CRGB) * _length); + Segment::_globalLeds = (CRGB*) ps_malloc(arrSize); else #endif - Segment::_globalLeds = (CRGB*) malloc(sizeof(CRGB) * _length); - memset(Segment::_globalLeds, 0, sizeof(CRGB) * _length); + Segment::_globalLeds = (CRGB*) malloc(arrSize); + memset(Segment::_globalLeds, 0, arrSize); } //segments are created in makeAutoSegments(); + DEBUG_PRINTLN(F("Loading custom palettes")); loadCustomPalettes(); // (re)load all custom palettes + DEBUG_PRINTLN(F("Loading custom ledmaps")); deserializeMap(); // (re)load default ledmap } @@ -1405,15 +1516,15 @@ void WS2812FX::service() { void IRAM_ATTR WS2812FX::setPixelColor(int i, uint32_t col) { - if (i >= _length) return; if (i < customMappingSize) i = customMappingTable[i]; + if (i >= _length) return; busses.setPixelColor(i, col); } uint32_t WS2812FX::getPixelColor(uint16_t i) { - if (i >= _length) return 0; if (i < customMappingSize) i = customMappingTable[i]; + if (i >= _length) return 0; return busses.getPixelColor(i); } @@ -1475,7 +1586,7 @@ void WS2812FX::estimateCurrentAndLimitBri() { } } - if (bus->isRgbw()) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less + if (bus->hasWhite()) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less busPowerSum *= 3; busPowerSum = busPowerSum >> 2; //same as /= 4 } @@ -1626,6 +1737,12 @@ uint8_t WS2812FX::getActiveSegmentsNum(void) { return c; } +uint16_t WS2812FX::getLengthTotal(void) { + uint16_t len = Segment::maxWidth * Segment::maxHeight; // will be _length for 1D (see finalizeInit()) but should cover whole matrix for 2D + if (isMatrix && _length > len) len = _length; // for 2D with trailing strip + return len; +} + uint16_t WS2812FX::getLengthPhysical(void) { uint16_t len = 0; for (size_t b = 0; b < busses.getNumBusses(); b++) { @@ -1643,12 +1760,7 @@ bool WS2812FX::hasRGBWBus(void) { for (size_t b = 0; b < busses.getNumBusses(); b++) { Bus *bus = busses.getBus(b); if (bus == nullptr || bus->getLength()==0) break; - switch (bus->getType()) { - case TYPE_SK6812_RGBW: - case TYPE_TM1814: - case TYPE_ANALOG_4CH: - return true; - } + if (bus->hasRGB() && bus->hasWhite()) return true; } return false; } @@ -1673,7 +1785,6 @@ void WS2812FX::purgeSegments(bool force) { if (_segments.size() <= 1) return; for (size_t i = _segments.size()-1; i > 0; i--) if (_segments[i].stop == 0 || force) { - DEBUG_PRINT(F("Purging segment segment: ")); DEBUG_PRINTLN(i); deleted++; _segments.erase(_segments.begin() + i); } @@ -1696,43 +1807,58 @@ void WS2812FX::restartRuntime() { for (segment &seg : _segments) seg.markForReset(); } -void WS2812FX::resetSegments() { - _segments.clear(); // destructs all Segment as part of clearing - #ifndef WLED_DISABLE_2D - segment seg = isMatrix ? Segment(0, Segment::maxWidth, 0, Segment::maxHeight) : Segment(0, _length); - #else - segment seg = Segment(0, _length); - #endif - _segments.push_back(seg); - _mainSegment = 0; +void WS2812FX::resetSegments(bool boundsOnly) { //WLEDMM add boundsonly + if (!boundsOnly) { + _segments.clear(); // destructs all Segment as part of clearing + #ifndef WLED_DISABLE_2D + segment seg = isMatrix ? Segment(0, Segment::maxWidth, 0, Segment::maxHeight) : Segment(0, _length); + #else + segment seg = Segment(0, _length); + #endif + _segments.push_back(seg); + _mainSegment = 0; + } else { //WLEDMM boundsonly + for (segment &seg : _segments) { + #ifndef WLED_DISABLE_2D + seg.start = 0; + seg.stop = Segment::maxWidth; + seg.startY = 0; + seg.stopY = Segment::maxHeight; + #else + seg.start = 0; + seg.stop = _length; + #endif + } + } + } void WS2812FX::makeAutoSegments(bool forceReset) { - if (isMatrix) { - #ifndef WLED_DISABLE_2D - // only create 1 2D segment - if (forceReset || getSegmentsNum() == 0) resetSegments(); // initialises 1 segment - else if (getActiveSegmentsNum() == 1) { - size_t i = getLastActiveSegmentId(); - _segments[i].start = 0; - _segments[i].stop = Segment::maxWidth; - _segments[i].startY = 0; - _segments[i].stopY = Segment::maxHeight; - _segments[i].grouping = 1; - _segments[i].spacing = 0; - _mainSegment = i; - } - #endif - } else if (autoSegments) { //make one segment per bus + if (autoSegments) { //make one segment per bus uint16_t segStarts[MAX_NUM_SEGMENTS] = {0}; uint16_t segStops [MAX_NUM_SEGMENTS] = {0}; - uint8_t s = 0; - for (uint8_t i = 0; i < busses.getNumBusses(); i++) { + size_t s = 0; + + #ifndef WLED_DISABLE_2D + // 2D segment is the 1st one using entire matrix + if (isMatrix) { + segStarts[0] = 0; + segStops[0] = Segment::maxWidth*Segment::maxHeight; + s++; + } + #endif + + for (size_t i = s; i < busses.getNumBusses(); i++) { Bus* b = busses.getBus(i); segStarts[s] = b->getStart(); segStops[s] = segStarts[s] + b->getLength(); + #ifndef WLED_DISABLE_2D + if (isMatrix && segStops[s] < Segment::maxWidth*Segment::maxHeight) continue; // ignore buses comprising matrix + if (isMatrix && segStarts[s] < Segment::maxWidth*Segment::maxHeight) segStarts[s] = Segment::maxWidth*Segment::maxHeight; + #endif + //check for overlap with previous segments for (size_t j = 0; j < s; j++) { if (segStops[j] > segStarts[s] && segStarts[j] < segStops[s]) { @@ -1744,23 +1870,40 @@ void WS2812FX::makeAutoSegments(bool forceReset) { } s++; } + _segments.clear(); - for (size_t i = 0; i < s; i++) { - Segment seg = Segment(segStarts[i], segStops[i]); - seg.selected = true; - _segments.push_back(seg); + _segments.reserve(s); // prevent reallocations + // there is always at least one segment (but we need to differentiate between 1D and 2D) + #ifndef WLED_DISABLE_2D + if (isMatrix) + _segments.push_back(Segment(0, Segment::maxWidth, 0, Segment::maxHeight)); + else + #endif + _segments.push_back(Segment(segStarts[0], segStops[0])); + for (size_t i = 1; i < s; i++) { + _segments.push_back(Segment(segStarts[i], segStops[i])); } - _mainSegment = 0; + } else { + if (forceReset || getSegmentsNum() == 0) resetSegments(); //expand the main seg to the entire length, but only if there are no other segments, or reset is forced else if (getActiveSegmentsNum() == 1) { size_t i = getLastActiveSegmentId(); + #ifndef WLED_DISABLE_2D + _segments[i].start = 0; + _segments[i].stop = Segment::maxWidth; + _segments[i].startY = 0; + _segments[i].stopY = Segment::maxHeight; + _segments[i].grouping = 1; + _segments[i].spacing = 0; + #else _segments[i].start = 0; _segments[i].stop = _length; - _mainSegment = 0; + #endif } } + _mainSegment = 0; fixInvalidSegments(); } @@ -1768,14 +1911,30 @@ void WS2812FX::makeAutoSegments(bool forceReset) { void WS2812FX::fixInvalidSegments() { //make sure no segment is longer than total (sanity check) for (size_t i = getSegmentsNum()-1; i > 0; i--) { - if (_segments[i].start >= _length) { _segments.erase(_segments.begin()+i); continue; } - if (_segments[i].stop > _length) _segments[i].stop = _length; - // this is always called as the last step after finalizeInit(), update covered bus types - _segments[i].refreshLightCapabilities(); + if (isMatrix) { + #ifndef WLED_DISABLE_2D + if (_segments[i].start >= Segment::maxWidth * Segment::maxHeight) { + // 1D segment at the end of matrix + if (_segments[i].start >= _length || _segments[i].startY > 0 || _segments[i].stopY > 1) { _segments.erase(_segments.begin()+i); continue; } + if (_segments[i].stop > _length) _segments[i].stop = _length; + continue; + } + if (_segments[i].start >= Segment::maxWidth || _segments[i].startY >= Segment::maxHeight) { _segments.erase(_segments.begin()+i); continue; } + if (_segments[i].stop > Segment::maxWidth) _segments[i].stop = Segment::maxWidth; + if (_segments[i].stopY > Segment::maxHeight) _segments[i].stopY = Segment::maxHeight; + #endif + } else { + if (_segments[i].start >= _length) { _segments.erase(_segments.begin()+i); continue; } + if (_segments[i].stop > _length) _segments[i].stop = _length; + } } + // this is always called as the last step after finalizeInit(), update covered bus types + for (segment &seg : _segments) + seg.refreshLightCapabilities(); } //true if all segments align with a bus, or if a segment covers the total length +//irrelevant in 2D set-up bool WS2812FX::checkSegmentAlignment() { bool aligned = false; for (segment &seg : _segments) { @@ -1792,8 +1951,7 @@ bool WS2812FX::checkSegmentAlignment() { //After this function is called, setPixelColor() will use that segment (offsets, grouping, ... will apply) //Note: If called in an interrupt (e.g. JSON API), original segment must be restored, //otherwise it can lead to a crash on ESP32 because _segment_index is modified while in use by the main thread -uint8_t WS2812FX::setPixelSegment(uint8_t n) -{ +uint8_t WS2812FX::setPixelSegment(uint8_t n) { uint8_t prevSegId = _segment_index; if (n < _segments.size()) { _segment_index = n; @@ -1802,8 +1960,7 @@ uint8_t WS2812FX::setPixelSegment(uint8_t n) return prevSegId; } -void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) -{ +void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) { if (i2 >= i) { for (uint16_t x = i; x <= i2; x++) setPixelColor(x, col); @@ -1813,26 +1970,24 @@ void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) } } -void WS2812FX::setTransitionMode(bool t) -{ +void WS2812FX::setTransitionMode(bool t) { for (segment &seg : _segments) if (!seg.transitional) seg.startTransition(t ? _transitionDur : 0); } #ifdef WLED_DEBUG -void WS2812FX::printSize() -{ +void WS2812FX::printSize() { size_t size = 0; for (const Segment &seg : _segments) size += seg.getSize(); DEBUG_PRINTF("Segments: %d -> %uB\n", _segments.size(), size); DEBUG_PRINTF("Modes: %d*%d=%uB\n", sizeof(mode_ptr), _mode.size(), (_mode.capacity()*sizeof(mode_ptr))); DEBUG_PRINTF("Data: %d*%d=%uB\n", sizeof(const char *), _modeData.size(), (_modeData.capacity()*sizeof(const char *))); DEBUG_PRINTF("Map: %d*%d=%uB\n", sizeof(uint16_t), (int)customMappingSize, customMappingSize*sizeof(uint16_t)); - if (useLedsArray) DEBUG_PRINTF("Buffer: %d*%d=%uB\n", sizeof(CRGB), (int)_length, _length*sizeof(CRGB)); + size = getLengthTotal(); + if (useLedsArray) DEBUG_PRINTF("Buffer: %d*%u=%uB\n", sizeof(CRGB), size, size*sizeof(CRGB)); } #endif -void WS2812FX::loadCustomPalettes() -{ +void WS2812FX::loadCustomPalettes() { byte tcp[72]; //support gradient palettes with up to 18 entries CRGBPalette16 targetPalette; customPalettes.clear(); // start fresh @@ -1880,85 +2035,103 @@ void WS2812FX::loadCustomPalettes() } //load custom mapping table from JSON file (called from finalizeInit() or deserializeState()) -void WS2812FX::deserializeMap(uint8_t n) { - // WLEDMM: also supports isMatrix +bool WS2812FX::deserializeMap(uint8_t n) { + // 2D support creates its own ledmap (on the fly) if a ledmap.json exists it will overwrite built one. char fileName[32]; - strcpy_P(fileName, PSTR("/ledmap")); - if (n) sprintf(fileName +7, "%d", n); - strcat(fileName, ".json"); - bool isFile = WLED_FS.exists(fileName); + //WLEDMM: als support segment name ledmaps + bool isFile = false;; + if (n<10) { + strcpy_P(fileName, PSTR("/ledmap")); + if (n) sprintf(fileName +7, "%d", n); //WLEDMM: trick to not include 0 in ledmap.json + strcat(fileName, ".json"); + isFile = WLED_FS.exists(fileName); + } else { //WLEDM add segment name as ledmap.name + uint8_t segment_index = 0; + for (segment &seg : _segments) { + if (n == 10 + segment_index && !isFile && seg.name != nullptr) { + sprintf_P(fileName, PSTR("/%s.json"), seg.name); + isFile = WLED_FS.exists(fileName); + } + if (isFile) break; + segment_index++; + } + } if (!isFile) { // erase custom mapping if selecting nonexistent ledmap.json (n==0) - if (!n && customMappingTable != nullptr) { - //WLEDMM: if isMatrix then not erase but back to matrix default - if (isMatrix) - setUpMatrix(true); - else { - customMappingSize = 0; - delete[] customMappingTable; - customMappingTable = nullptr; - } + //WLEDM: doubt this is necessary as return false causes setupMatrix to deal with this + if (!isMatrix && !n && customMappingTable != nullptr) { + customMappingSize = 0; + delete[] customMappingTable; + customMappingTable = nullptr; + loadedLedmap = 0; //WLEDMM } - return; + return false; } - if (!requestJSONBufferLock(7)) return; - - DEBUG_PRINT(F("Reading LED map from ")); - DEBUG_PRINTLN(fileName); + if (!requestJSONBufferLock(7)) return false; if (!readObjectFromFile(fileName, nullptr, &doc)) { releaseJSONBufferLock(); - return; //if file does not exist just exit + return false; //if file does not exist just exit } + USER_PRINT(F("Reading LED map from ")); //WLEDMM use USER_PRINT + USER_PRINTLN(fileName); + // erase old custom ledmap if (customMappingTable != nullptr) { customMappingSize = 0; delete[] customMappingTable; customMappingTable = nullptr; + loadedLedmap = 0; } JsonArray map = doc[F("map")]; if (!map.isNull() && map.size()) { // not an empty map - //WLEDMM: if isMatrix then customMap size is whole matrix -#ifndef WLED_DISABLE_2D - if (isMatrix) - customMappingSize = Segment::maxWidth * Segment::maxHeight; - else -#endif - customMappingSize = map.size(); - customMappingTable = new uint16_t[customMappingSize]; - for (uint16_t i=0; i0 && doc[F("height")]>0) { + Segment::maxWidth = doc[F("width")];; + Segment::maxHeight = doc[F("height")];; + resetSegments(true); //WLEDMM not makeAutoSegments() as we only want to change bounds } - setUpMatrix(false); //WLEDMM: apply logical to physical mapping after the ledmap + + customMappingSize = map.size(); + customMappingTable = new uint16_t[customMappingSize]; + + for (uint16_t i=0; i HSB convert here - Blynk.virtualWrite(V3, bri? 1:0); - Blynk.virtualWrite(V4, effectCurrent); - Blynk.virtualWrite(V5, effectSpeed); - Blynk.virtualWrite(V6, effectIntensity); - Blynk.virtualWrite(V7, nightlightActive); - Blynk.virtualWrite(V8, notifyDirect); - #endif -} - -#ifndef WLED_DISABLE_BLYNK -BLYNK_WRITE(V0) -{ - bri = param.asInt();//bri - stateUpdated(CALL_MODE_BLYNK); -} - -BLYNK_WRITE(V1) -{ - blHue = param.asInt();//hue - colorHStoRGB(blHue*10,blSat,col); - colorUpdated(CALL_MODE_BLYNK); -} - -BLYNK_WRITE(V2) -{ - blSat = param.asInt();//sat - colorHStoRGB(blHue*10,blSat,col); - colorUpdated(CALL_MODE_BLYNK); -} - -BLYNK_WRITE(V3) -{ - bool on = (param.asInt()>0); - if (!on != !bri) {toggleOnOff(); stateUpdated(CALL_MODE_BLYNK);} -} - -BLYNK_WRITE(V4) -{ - effectCurrent = param.asInt()-1;//fx - colorUpdated(CALL_MODE_BLYNK); -} - -BLYNK_WRITE(V5) -{ - effectSpeed = param.asInt();//sx - colorUpdated(CALL_MODE_BLYNK); -} - -BLYNK_WRITE(V6) -{ - effectIntensity = param.asInt();//ix - colorUpdated(CALL_MODE_BLYNK); -} - -BLYNK_WRITE(V7) -{ - nightlightActive = (param.asInt()>0); -} - -BLYNK_WRITE(V8) -{ - notifyDirect = (param.asInt()>0); //send notifications -} -#endif diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp new file mode 100644 index 00000000..08bb5128 --- /dev/null +++ b/wled00/bus_manager.cpp @@ -0,0 +1,563 @@ +/* + * Class implementation for addressing various light types + */ + +#include +#include +#include "const.h" +#include "pin_manager.h" +#include "bus_wrapper.h" +#include "bus_manager.h" + +//colors.cpp +uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb); +uint16_t approximateKelvinFromRGB(uint32_t rgb); +void colorRGBtoRGBW(byte* rgb); + +//udp.cpp +uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte *buffer, uint8_t bri=255, bool isRGBW=false); + +// enable additional debug output +//WLEDMM: #define DEBUGOUT(x) netDebugEnabled?NetDebug.print(x):Serial.print(x) not supported in this file as netDebugEnabled not in scope +#if defined(WLED_DEBUG_HOST) + #include "net_debug.h" + #define DEBUGOUT NetDebug +#else + #define DEBUGOUT Serial +#endif + +#ifdef WLED_DEBUG + #ifndef ESP8266 + #include + #endif + #define DEBUG_PRINT(x) DEBUGOUT.print(x) + #define DEBUG_PRINTLN(x) DEBUGOUT.println(x) + #define DEBUG_PRINTF(x...) DEBUGOUT.printf(x) +#else + #define DEBUG_PRINT(x) + #define DEBUG_PRINTLN(x) + #define DEBUG_PRINTF(x...) +#endif + +//color mangling macros +#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b)))) +#define R(c) (byte((c) >> 16)) +#define G(c) (byte((c) >> 8)) +#define B(c) (byte(c)) +#define W(c) (byte((c) >> 24)) + + +void ColorOrderMap::add(uint16_t start, uint16_t len, uint8_t colorOrder) { + if (_count >= WLED_MAX_COLOR_ORDER_MAPPINGS) { + return; + } + if (len == 0) { + return; + } + if (colorOrder > COL_ORDER_MAX) { + return; + } + _mappings[_count].start = start; + _mappings[_count].len = len; + _mappings[_count].colorOrder = colorOrder; + _count++; +} + +uint8_t IRAM_ATTR ColorOrderMap::getPixelColorOrder(uint16_t pix, uint8_t defaultColorOrder) const { + if (_count == 0) return defaultColorOrder; + // upper nibble containd W swap information + uint8_t swapW = defaultColorOrder >> 4; + for (uint8_t i = 0; i < _count; i++) { + if (pix >= _mappings[i].start && pix < (_mappings[i].start + _mappings[i].len)) { + return _mappings[i].colorOrder | (swapW << 4); + } + } + return defaultColorOrder; +} + + +uint32_t Bus::autoWhiteCalc(uint32_t c) { + uint8_t aWM = _autoWhiteMode; + if (_gAWM < 255) aWM = _gAWM; + if (aWM == RGBW_MODE_MANUAL_ONLY) return c; + uint8_t w = W(c); + //ignore auto-white calculation if w>0 and mode DUAL (DUAL behaves as BRIGHTER if w==0) + if (w > 0 && aWM == RGBW_MODE_DUAL) return c; + uint8_t r = R(c); + uint8_t g = G(c); + uint8_t b = B(c); + if (aWM == RGBW_MODE_MAX) return RGBW32(r, g, b, r > g ? (r > b ? r : b) : (g > b ? g : b)); // brightest RGB channel + w = r < g ? (r < b ? r : b) : (g < b ? g : b); + if (aWM == RGBW_MODE_AUTO_ACCURATE) { r -= w; g -= w; b -= w; } //subtract w in ACCURATE mode + return RGBW32(r, g, b, w); +} + + +BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) : Bus(bc.type, bc.start, bc.autoWhite), _colorOrderMap(com) { + if (!IS_DIGITAL(bc.type) || !bc.count) return; + if (!pinManager.allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return; + _pins[0] = bc.pins[0]; + if (IS_2PIN(bc.type)) { + if (!pinManager.allocatePin(bc.pins[1], true, PinOwner::BusDigital)) { + cleanup(); return; + } + _pins[1] = bc.pins[1]; + } + reversed = bc.reversed; + _needsRefresh = bc.refreshReq || bc.type == TYPE_TM1814; + _skip = bc.skipAmount; //sacrificial pixels + _len = bc.count + _skip; + _iType = PolyBus::getI(bc.type, _pins, nr); + if (_iType == I_NONE) return; + uint16_t lenToCreate = _len; + if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(_len); // only needs a third of "RGB" LEDs for NeoPixelBus + _busPtr = PolyBus::create(_iType, _pins, lenToCreate, nr); + _valid = (_busPtr != nullptr); + _colorOrder = bc.colorOrder; + DEBUG_PRINTF("%successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u)\n", _valid?"S":"Uns", nr, _len, bc.type, _pins[0],_pins[1],_iType); +} + +void BusDigital::show() { + PolyBus::show(_busPtr, _iType); +} + +bool BusDigital::canShow() { + return PolyBus::canShow(_busPtr, _iType); +} + +void BusDigital::setBrightness(uint8_t b) { + //Fix for turning off onboard LED breaking bus + #ifdef LED_BUILTIN + if (_bri == 0 && b > 0) { + if (_pins[0] == LED_BUILTIN || _pins[1] == LED_BUILTIN) PolyBus::begin(_busPtr, _iType, _pins); + } + #endif + Bus::setBrightness(b); + PolyBus::setBrightness(_busPtr, _iType, b); +} + +//If LEDs are skipped, it is possible to use the first as a status LED. +//TODO only show if no new show due in the next 50ms +void BusDigital::setStatusPixel(uint32_t c) { + if (_skip && canShow()) { + PolyBus::setPixelColor(_busPtr, _iType, 0, c, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); + PolyBus::show(_busPtr, _iType); + } +} + +void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) { + if (_type == TYPE_SK6812_RGBW || _type == TYPE_TM1814 || _type == TYPE_WS2812_1CH_X3) c = autoWhiteCalc(c); + if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT + if (reversed) pix = _len - pix -1; + else pix += _skip; + uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder); + if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs + uint16_t pOld = pix; + pix = IC_INDEX_WS2812_1CH_3X(pix); + uint32_t cOld = PolyBus::getPixelColor(_busPtr, _iType, pix, co); + switch (pOld % 3) { // change only the single channel (TODO: this can cause loss because of get/set) + case 0: c = RGBW32(R(cOld), W(c) , B(cOld), 0); break; + case 1: c = RGBW32(W(c) , G(cOld), B(cOld), 0); break; + case 2: c = RGBW32(R(cOld), G(cOld), W(c) , 0); break; + } + } + PolyBus::setPixelColor(_busPtr, _iType, pix, c, co); +} + +uint32_t BusDigital::getPixelColor(uint16_t pix) { + if (reversed) pix = _len - pix -1; + else pix += _skip; + uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder); + if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs + uint16_t pOld = pix; + pix = IC_INDEX_WS2812_1CH_3X(pix); + uint32_t c = PolyBus::getPixelColor(_busPtr, _iType, pix, co); + switch (pOld % 3) { // get only the single channel + case 0: c = RGBW32(G(c), G(c), G(c), G(c)); break; + case 1: c = RGBW32(R(c), R(c), R(c), R(c)); break; + case 2: c = RGBW32(B(c), B(c), B(c), B(c)); break; + } + return c; + } + return PolyBus::getPixelColor(_busPtr, _iType, pix, co); +} + +uint8_t BusDigital::getPins(uint8_t* pinArray) { + uint8_t numPins = IS_2PIN(_type) ? 2 : 1; + for (uint8_t i = 0; i < numPins; i++) pinArray[i] = _pins[i]; + return numPins; +} + +void BusDigital::setColorOrder(uint8_t colorOrder) { + // upper nibble contains W swap information + if ((colorOrder & 0x0F) > 5) return; + _colorOrder = colorOrder; +} + +void BusDigital::reinit() { + PolyBus::begin(_busPtr, _iType, _pins); +} + +void BusDigital::cleanup() { + DEBUG_PRINTLN(F("Digital Cleanup.")); + PolyBus::cleanup(_busPtr, _iType); + _iType = I_NONE; + _valid = false; + _busPtr = nullptr; + pinManager.deallocatePin(_pins[1], PinOwner::BusDigital); + pinManager.deallocatePin(_pins[0], PinOwner::BusDigital); +} + + +BusPwm::BusPwm(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { + _valid = false; + if (!IS_PWM(bc.type)) return; + uint8_t numPins = NUM_PWM_PINS(bc.type); + + #ifdef ESP8266 + analogWriteRange(255); //same range as one RGB channel + analogWriteFreq(WLED_PWM_FREQ); + #else + _ledcStart = pinManager.allocateLedc(numPins); + if (_ledcStart == 255) { //no more free LEDC channels + deallocatePins(); return; + } + #endif + + for (uint8_t i = 0; i < numPins; i++) { + uint8_t currentPin = bc.pins[i]; + if (!pinManager.allocatePin(currentPin, true, PinOwner::BusPwm)) { + deallocatePins(); return; + } + _pins[i] = currentPin; //store only after allocatePin() succeeds + #ifdef ESP8266 + pinMode(_pins[i], OUTPUT); + #else + ledcSetup(_ledcStart + i, WLED_PWM_FREQ, 8); + ledcAttachPin(_pins[i], _ledcStart + i); + #endif + } + reversed = bc.reversed; + _valid = true; +} + +void BusPwm::setPixelColor(uint16_t pix, uint32_t c) { + if (pix != 0 || !_valid) return; //only react to first pixel + if (_type != TYPE_ANALOG_3CH) c = autoWhiteCalc(c); + if (_cct >= 1900 && (_type == TYPE_ANALOG_3CH || _type == TYPE_ANALOG_4CH)) { + c = colorBalanceFromKelvin(_cct, c); //color correction from CCT + } + uint8_t r = R(c); + uint8_t g = G(c); + uint8_t b = B(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) { + case TYPE_ANALOG_1CH: //one channel (white), relies on auto white calculation + _data[0] = w; + break; + case TYPE_ANALOG_2CH: //warm white + cold white + _data[1] = cw; + _data[0] = ww; + break; + case TYPE_ANALOG_5CH: //RGB + warm white + cold white + _data[4] = cw; + w = ww; + case TYPE_ANALOG_4CH: //RGBW + _data[3] = w; + case TYPE_ANALOG_3CH: //standard dumb RGB + _data[0] = r; _data[1] = g; _data[2] = b; + break; + } +} + +//does no index check +uint32_t BusPwm::getPixelColor(uint16_t pix) { + if (!_valid) return 0; + return RGBW32(_data[0], _data[1], _data[2], _data[3]); +} + +void BusPwm::show() { + if (!_valid) return; + uint8_t numPins = NUM_PWM_PINS(_type); + for (uint8_t i = 0; i < numPins; i++) { + uint8_t scaled = (_data[i] * _bri) / 255; + if (reversed) scaled = 255 - scaled; + #ifdef ESP8266 + analogWrite(_pins[i], scaled); + #else + ledcWrite(_ledcStart + i, scaled); + #endif + } +} + +uint8_t BusPwm::getPins(uint8_t* pinArray) { + if (!_valid) return 0; + uint8_t numPins = NUM_PWM_PINS(_type); + for (uint8_t i = 0; i < numPins; i++) { + pinArray[i] = _pins[i]; + } + return numPins; +} + +void BusPwm::deallocatePins() { + uint8_t numPins = NUM_PWM_PINS(_type); + for (uint8_t i = 0; i < numPins; i++) { + pinManager.deallocatePin(_pins[i], PinOwner::BusPwm); + if (!pinManager.isPinOk(_pins[i])) continue; + #ifdef ESP8266 + digitalWrite(_pins[i], LOW); //turn off PWM interrupt + #else + if (_ledcStart < 16) ledcDetachPin(_pins[i]); + #endif + } + #ifdef ARDUINO_ARCH_ESP32 + pinManager.deallocateLedc(_ledcStart, numPins); + #endif +} + + +BusOnOff::BusOnOff(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { + _valid = false; + if (bc.type != TYPE_ONOFF) return; + + uint8_t currentPin = bc.pins[0]; + if (!pinManager.allocatePin(currentPin, true, PinOwner::BusOnOff)) { + return; + } + _pin = currentPin; //store only after allocatePin() succeeds + pinMode(_pin, OUTPUT); + reversed = bc.reversed; + _valid = true; +} + +void BusOnOff::setPixelColor(uint16_t pix, uint32_t c) { + if (pix != 0 || !_valid) return; //only react to first pixel + c = autoWhiteCalc(c); + uint8_t r = R(c); + uint8_t g = G(c); + uint8_t b = B(c); + uint8_t w = W(c); + + _data = bool(r|g|b|w) && bool(_bri) ? 0xFF : 0; +} + +uint32_t BusOnOff::getPixelColor(uint16_t pix) { + if (!_valid) return 0; + return RGBW32(_data, _data, _data, _data); +} + +void BusOnOff::show() { + if (!_valid) return; + digitalWrite(_pin, reversed ? !(bool)_data : (bool)_data); +} + +uint8_t BusOnOff::getPins(uint8_t* pinArray) { + if (!_valid) return 0; + pinArray[0] = _pin; + return 1; +} + + +BusNetwork::BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { + _valid = false; + switch (bc.type) { + case TYPE_NET_ARTNET_RGB: + _rgbw = false; + _UDPtype = 2; + break; + case TYPE_NET_E131_RGB: + _rgbw = false; + _UDPtype = 1; + break; + default: // TYPE_NET_DDP_RGB / TYPE_NET_DDP_RGBW + _rgbw = bc.type == TYPE_NET_DDP_RGBW; + _UDPtype = 0; + break; + } + _UDPchannels = _rgbw ? 4 : 3; + _data = (byte *)malloc(bc.count * _UDPchannels); + if (_data == nullptr) return; + memset(_data, 0, bc.count * _UDPchannels); + _len = bc.count; + _client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]); + _broadcastLock = false; + _valid = true; +} + +void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) { + if (!_valid || pix >= _len) return; + if (hasWhite()) c = autoWhiteCalc(c); + if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT + uint16_t offset = pix * _UDPchannels; + _data[offset] = R(c); + _data[offset+1] = G(c); + _data[offset+2] = B(c); + if (_rgbw) _data[offset+3] = W(c); +} + +uint32_t BusNetwork::getPixelColor(uint16_t pix) { + if (!_valid || pix >= _len) return 0; + uint16_t offset = pix * _UDPchannels; + return RGBW32(_data[offset], _data[offset+1], _data[offset+2], _rgbw ? (_data[offset+3] << 24) : 0); +} + +void BusNetwork::show() { + if (!_valid || !canShow()) return; + _broadcastLock = true; + realtimeBroadcast(_UDPtype, _client, _len, _data, _bri, _rgbw); + _broadcastLock = false; +} + +uint8_t BusNetwork::getPins(uint8_t* pinArray) { + for (uint8_t i = 0; i < 4; i++) { + pinArray[i] = _client[i]; + } + return 4; +} + +void BusNetwork::cleanup() { + _type = I_NONE; + _valid = false; + if (_data != nullptr) free(_data); + _data = nullptr; +} + + +//utility to get the approx. memory usage of a given BusConfig +uint32_t BusManager::memUsage(BusConfig &bc) { + uint8_t type = bc.type; + uint16_t len = bc.count + bc.skipAmount; + if (type > 15 && type < 32) { + #ifdef ESP8266 + if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem + if (type > 29) return len*20; //RGBW + return len*15; + } + if (type > 29) return len*4; //RGBW + return len*3; + #else //ESP32 RMT uses double buffer? + if (type > 29) return len*8; //RGBW + return len*6; + #endif + } + if (type > 31 && type < 48) return 5; + if (type == 44 || type == 45) return len*4; //RGBW + return len*3; //RGB +} + +int BusManager::add(BusConfig &bc) { + if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1; + if (bc.type >= TYPE_NET_DDP_RGB && bc.type < 96) { + busses[numBusses] = new BusNetwork(bc); + } else if (IS_DIGITAL(bc.type)) { + busses[numBusses] = new BusDigital(bc, numBusses, colorOrderMap); + } else if (bc.type == TYPE_ONOFF) { + busses[numBusses] = new BusOnOff(bc); + } else { + busses[numBusses] = new BusPwm(bc); + } + return numBusses++; +} + +//do not call this method from system context (network callback) +void BusManager::removeAll() { + DEBUG_PRINTLN(F("Removing all.")); + //prevents crashes due to deleting busses while in use. + while (!canAllShow()) yield(); + for (uint8_t i = 0; i < numBusses; i++) delete busses[i]; + numBusses = 0; +} + +void BusManager::show() { + for (uint8_t i = 0; i < numBusses; i++) { + busses[i]->show(); + } +} + +void BusManager::setStatusPixel(uint32_t c) { + for (uint8_t i = 0; i < numBusses; i++) { + busses[i]->setStatusPixel(c); + } +} + +void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c, int16_t cct) { + for (uint8_t i = 0; i < numBusses; i++) { + Bus* b = busses[i]; + uint16_t bstart = b->getStart(); + if (pix < bstart || pix >= bstart + b->getLength()) continue; + busses[i]->setPixelColor(pix - bstart, c); + } +} + +void BusManager::setBrightness(uint8_t b) { + for (uint8_t i = 0; i < numBusses; i++) { + busses[i]->setBrightness(b); + } +} + +void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) { + if (cct > 255) cct = 255; + if (cct >= 0) { + //if white balance correction allowed, save as kelvin value instead of 0-255 + if (allowWBCorrection) cct = 1900 + (cct << 5); + } else cct = -1; + Bus::setCCT(cct); +} + +uint32_t BusManager::getPixelColor(uint16_t pix) { + for (uint8_t i = 0; i < numBusses; i++) { + Bus* b = busses[i]; + uint16_t bstart = b->getStart(); + if (pix < bstart || pix >= bstart + b->getLength()) continue; + return b->getPixelColor(pix - bstart); + } + return 0; +} + +bool BusManager::canAllShow() { + for (uint8_t i = 0; i < numBusses; i++) { + if (!busses[i]->canShow()) return false; + } + return true; +} + +Bus* BusManager::getBus(uint8_t busNr) { + if (busNr >= numBusses) return nullptr; + return busses[busNr]; +} + +//semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit()) +uint16_t BusManager::getTotalLength() { + uint16_t len = 0; + for (uint8_t i=0; igetLength(); + return len; +} + +// Bus static member definition +int16_t Bus::_cct = -1; +uint8_t Bus::_cctBlend = 0; +uint8_t Bus::_gAWM = 255; diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 8250695f..ffb3bd14 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -6,44 +6,17 @@ */ #include "const.h" -#include "pin_manager.h" -#include "bus_wrapper.h" -#include - -//colors.cpp -uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb); -void colorRGBtoRGBW(byte* rgb); - -// enable additional debug output -#if defined(WLED_DEBUG_HOST) - #define DEBUGOUT NetDebug -#else - #define DEBUGOUT Serial -#endif - -#ifdef WLED_DEBUG - #ifndef ESP8266 - #include - #endif - #define DEBUG_PRINT(x) DEBUGOUT.print(x) - #define DEBUG_PRINTLN(x) DEBUGOUT.println(x) - #define DEBUG_PRINTF(x...) DEBUGOUT.printf(x) -#else - #define DEBUG_PRINT(x) - #define DEBUG_PRINTLN(x) - #define DEBUG_PRINTF(x...) -#endif #define GET_BIT(var,bit) (((var)>>(bit))&0x01) #define SET_BIT(var,bit) ((var)|=(uint16_t)(0x0001<<(bit))) #define UNSET_BIT(var,bit) ((var)&=(~(uint16_t)(0x0001<<(bit)))) -//color mangling macros -#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b)))) -#define R(c) (byte((c) >> 16)) -#define G(c) (byte((c) >> 8)) -#define B(c) (byte(c)) -#define W(c) (byte((c) >> 24)) +#define NUM_ICS_WS2812_1CH_3X(len) (((len)+2)/3) // 1 WS2811 IC controls 3 zones (each zone has 1 LED, W) +#define IC_INDEX_WS2812_1CH_3X(i) ((i)/3) + +#define NUM_ICS_WS2812_2CH_3X(len) (((len)+1)*2/3) // 2 WS2811 ICs control 3 zones (each zone has 2 LEDs, CW and WW) +#define IC_INDEX_WS2812_2CH_3X(i) ((i)*2/3) +#define WS2812_2CH_3X_SPANS_2_ICS(i) ((i)&0x01) // every other LED zone is on two different ICs //temporary struct for passing bus configuration to bus struct BusConfig { @@ -88,53 +61,29 @@ struct ColorOrderMapEntry { }; struct ColorOrderMap { - void add(uint16_t start, uint16_t len, uint8_t colorOrder) { - if (_count >= WLED_MAX_COLOR_ORDER_MAPPINGS) { - return; - } - if (len == 0) { - return; - } - if (colorOrder > COL_ORDER_MAX) { - return; - } - _mappings[_count].start = start; - _mappings[_count].len = len; - _mappings[_count].colorOrder = colorOrder; - _count++; - } + void add(uint16_t start, uint16_t len, uint8_t colorOrder); - uint8_t count() const { - return _count; - } - - void reset() { - _count = 0; - memset(_mappings, 0, sizeof(_mappings)); - } - - const ColorOrderMapEntry* get(uint8_t n) const { - if (n > _count) { - return nullptr; + uint8_t count() const { + return _count; } - return &(_mappings[n]); - } - inline uint8_t IRAM_ATTR getPixelColorOrder(uint16_t pix, uint8_t defaultColorOrder) const { - if (_count == 0) return defaultColorOrder; - // upper nibble containd W swap information - uint8_t swapW = defaultColorOrder >> 4; - for (uint8_t i = 0; i < _count; i++) { - if (pix >= _mappings[i].start && pix < (_mappings[i].start + _mappings[i].len)) { - return _mappings[i].colorOrder | (swapW << 4); + void reset() { + _count = 0; + memset(_mappings, 0, sizeof(_mappings)); + } + + const ColorOrderMapEntry* get(uint8_t n) const { + if (n > _count) { + return nullptr; } + return &(_mappings[n]); } - return defaultColorOrder; - } + + uint8_t getPixelColorOrder(uint16_t pix, uint8_t defaultColorOrder) const; private: - uint8_t _count; - ColorOrderMapEntry _mappings[WLED_MAX_COLOR_ORDER_MAPPINGS]; + uint8_t _count; + ColorOrderMapEntry _mappings[WLED_MAX_COLOR_ORDER_MAPPINGS]; }; //parent class of BusDigital, BusPwm, and BusNetwork @@ -148,7 +97,7 @@ class Bus { { _type = type; _start = start; - _autoWhiteMode = Bus::isRgbw(_type) ? aw : RGBW_MODE_MANUAL_ONLY; + _autoWhiteMode = Bus::hasWhite(_type) ? aw : RGBW_MODE_MANUAL_ONLY; }; virtual ~Bus() {} //throw the bus under the bus @@ -172,37 +121,37 @@ class Bus { inline bool isOffRefreshRequired() { return _needsRefresh; } bool containsPixel(uint16_t pix) { return pix >= _start && pix < _start+_len; } - virtual bool isRgbw() { return Bus::isRgbw(_type); } - static bool isRgbw(uint8_t type) { - if (type == TYPE_SK6812_RGBW || type == TYPE_TM1814) return true; - if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true; - if (type == TYPE_NET_DDP_RGBW) return true; - return false; - } virtual bool hasRGB() { - if (_type == TYPE_WS2812_1CH || _type == TYPE_WS2812_WWA || _type == TYPE_ANALOG_1CH || _type == TYPE_ANALOG_2CH || _type == TYPE_ONOFF) return false; + if ((_type >= TYPE_WS2812_1CH && _type <= TYPE_WS2812_WWA) || _type == TYPE_ANALOG_1CH || _type == TYPE_ANALOG_2CH || _type == TYPE_ONOFF) return false; return true; } - virtual bool hasWhite() { - if (_type == TYPE_SK6812_RGBW || _type == TYPE_TM1814 || _type == TYPE_WS2812_1CH || _type == TYPE_WS2812_WWA || - _type == TYPE_ANALOG_1CH || _type == TYPE_ANALOG_2CH || _type == TYPE_ANALOG_4CH || _type == TYPE_ANALOG_5CH || _type == TYPE_NET_DDP_RGBW) return true; + virtual bool hasWhite() { return Bus::hasWhite(_type); } + static bool hasWhite(uint8_t type) { + if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_SK6812_RGBW || type == TYPE_TM1814) 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_NET_DDP_RGBW) return true; // network types with white channel + return false; + } + virtual bool hasCCT() { + if (_type == TYPE_WS2812_2CH_X3 || _type == TYPE_WS2812_WWA || + _type == TYPE_ANALOG_2CH || _type == TYPE_ANALOG_5CH) return true; return false; } static void setCCT(uint16_t cct) { _cct = cct; } - static void setCCTBlend(uint8_t b) { - if (b > 100) b = 100; - _cctBlend = (b * 127) / 100; - //compile-time limiter for hardware that can't power both white channels at max - #ifdef WLED_MAX_CCT_BLEND - if (_cctBlend > WLED_MAX_CCT_BLEND) _cctBlend = WLED_MAX_CCT_BLEND; - #endif - } - inline void setAWMode(uint8_t m) { if (m < 4) _autoWhiteMode = m; } - inline uint8_t getAWMode() { return _autoWhiteMode; } - inline static void setAutoWhiteMode(uint8_t m) { if (m < 4) _gAWM = m; else _gAWM = 255; } - inline static uint8_t getAutoWhiteMode() { return _gAWM; } + static void setCCTBlend(uint8_t b) { + if (b > 100) b = 100; + _cctBlend = (b * 127) / 100; + //compile-time limiter for hardware that can't power both white channels at max + #ifdef WLED_MAX_CCT_BLEND + if (_cctBlend > WLED_MAX_CCT_BLEND) _cctBlend = WLED_MAX_CCT_BLEND; + #endif + } + inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; } + 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 uint8_t getGlobalAWMode() { return _gAWM; } bool reversed = false; @@ -214,440 +163,151 @@ class Bus { bool _valid; bool _needsRefresh; uint8_t _autoWhiteMode; - static uint8_t _gAWM; // definition in FX_fcn.cpp - static int16_t _cct; // definition in FX_fcn.cpp - static uint8_t _cctBlend; // definition in FX_fcn.cpp + static uint8_t _gAWM; + static int16_t _cct; + static uint8_t _cctBlend; - uint32_t autoWhiteCalc(uint32_t c) { - uint8_t aWM = _autoWhiteMode; - if (_gAWM < 255) aWM = _gAWM; - if (aWM == RGBW_MODE_MANUAL_ONLY) return c; - uint8_t w = W(c); - //ignore auto-white calculation if w>0 and mode DUAL (DUAL behaves as BRIGHTER if w==0) - if (w > 0 && aWM == RGBW_MODE_DUAL) return c; - uint8_t r = R(c); - uint8_t g = G(c); - uint8_t b = B(c); - w = r < g ? (r < b ? r : b) : (g < b ? g : b); - if (aWM == RGBW_MODE_AUTO_ACCURATE) { r -= w; g -= w; b -= w; } //subtract w in ACCURATE mode - return RGBW32(r, g, b, w); - } + uint32_t autoWhiteCalc(uint32_t c); }; class BusDigital : public Bus { public: - BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) : Bus(bc.type, bc.start, bc.autoWhite), _colorOrderMap(com) { - if (!IS_DIGITAL(bc.type) || !bc.count) return; - if (!pinManager.allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return; - _pins[0] = bc.pins[0]; - if (IS_2PIN(bc.type)) { - if (!pinManager.allocatePin(bc.pins[1], true, PinOwner::BusDigital)) { - cleanup(); return; - } - _pins[1] = bc.pins[1]; + BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com); + + inline void show(); + + bool canShow(); + + void setBrightness(uint8_t b); + + void setStatusPixel(uint32_t c); + + void setPixelColor(uint16_t pix, uint32_t c); + + uint32_t getPixelColor(uint16_t pix); + + uint8_t getColorOrder() { + return _colorOrder; } - reversed = bc.reversed; - _needsRefresh = bc.refreshReq || bc.type == TYPE_TM1814; - _skip = bc.skipAmount; //sacrificial pixels - _len = bc.count + _skip; - _iType = PolyBus::getI(bc.type, _pins, nr); - if (_iType == I_NONE) return; - _busPtr = PolyBus::create(_iType, _pins, _len, nr); - _valid = (_busPtr != nullptr); - _colorOrder = bc.colorOrder; - DEBUG_PRINTF("%successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u)\n", _valid?"S":"Uns", nr, _len, bc.type, _pins[0],_pins[1],_iType); - }; - inline void show() { - PolyBus::show(_busPtr, _iType); - } - - inline bool canShow() { - return PolyBus::canShow(_busPtr, _iType); - } - - void setBrightness(uint8_t b) { - //Fix for turning off onboard LED breaking bus - #ifdef LED_BUILTIN - if (_bri == 0 && b > 0) { - if (_pins[0] == LED_BUILTIN || _pins[1] == LED_BUILTIN) PolyBus::begin(_busPtr, _iType, _pins); + uint16_t getLength() { + return _len - _skip; } - #endif - Bus::setBrightness(b); - PolyBus::setBrightness(_busPtr, _iType, b); - } - //If LEDs are skipped, it is possible to use the first as a status LED. - //TODO only show if no new show due in the next 50ms - void setStatusPixel(uint32_t c) { - if (_skip && canShow()) { - PolyBus::setPixelColor(_busPtr, _iType, 0, c, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); - PolyBus::show(_busPtr, _iType); + uint8_t getPins(uint8_t* pinArray); + + void setColorOrder(uint8_t colorOrder); + + uint8_t skippedLeds() { + return _skip; } - } - void setPixelColor(uint16_t pix, uint32_t c) { - if (_type == TYPE_SK6812_RGBW || _type == TYPE_TM1814) c = autoWhiteCalc(c); - if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT - if (reversed) pix = _len - pix -1; - else pix += _skip; - PolyBus::setPixelColor(_busPtr, _iType, pix, c, _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder)); - } + void reinit(); - uint32_t getPixelColor(uint16_t pix) { - if (reversed) pix = _len - pix -1; - else pix += _skip; - return PolyBus::getPixelColor(_busPtr, _iType, pix, _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder)); - } + void cleanup(); - inline uint8_t getColorOrder() { - return _colorOrder; - } - - uint16_t getLength() { - return _len - _skip; - } - - uint8_t getPins(uint8_t* pinArray) { - uint8_t numPins = IS_2PIN(_type) ? 2 : 1; - for (uint8_t i = 0; i < numPins; i++) pinArray[i] = _pins[i]; - return numPins; - } - - void setColorOrder(uint8_t colorOrder) { - // upper nibble contains W swap information - if ((colorOrder & 0x0F) > 5) return; - _colorOrder = colorOrder; - } - - inline uint8_t skippedLeds() { - return _skip; - } - - inline void reinit() { - PolyBus::begin(_busPtr, _iType, _pins); - } - - void cleanup() { - DEBUG_PRINTLN(F("Digital Cleanup.")); - PolyBus::cleanup(_busPtr, _iType); - _iType = I_NONE; - _valid = false; - _busPtr = nullptr; - pinManager.deallocatePin(_pins[1], PinOwner::BusDigital); - pinManager.deallocatePin(_pins[0], PinOwner::BusDigital); - } - - ~BusDigital() { - cleanup(); - } + ~BusDigital() { + cleanup(); + } private: - uint8_t _colorOrder = COL_ORDER_GRB; - uint8_t _pins[2] = {255, 255}; - uint8_t _iType = I_NONE; - uint8_t _skip = 0; - void * _busPtr = nullptr; - const ColorOrderMap &_colorOrderMap; + uint8_t _colorOrder = COL_ORDER_GRB; + uint8_t _pins[2] = {255, 255}; + uint8_t _iType = 0; //I_NONE; + uint8_t _skip = 0; + void * _busPtr = nullptr; + const ColorOrderMap &_colorOrderMap; }; class BusPwm : public Bus { public: - BusPwm(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { - _valid = false; - if (!IS_PWM(bc.type)) return; - uint8_t numPins = NUM_PWM_PINS(bc.type); + BusPwm(BusConfig &bc); - #ifdef ESP8266 - analogWriteRange(255); //same range as one RGB channel - analogWriteFreq(WLED_PWM_FREQ); - #else - _ledcStart = pinManager.allocateLedc(numPins); - if (_ledcStart == 255) { //no more free LEDC channels - deallocatePins(); return; - } - #endif + void setPixelColor(uint16_t pix, uint32_t c); - for (uint8_t i = 0; i < numPins; i++) { - uint8_t currentPin = bc.pins[i]; - if (!pinManager.allocatePin(currentPin, true, PinOwner::BusPwm)) { - deallocatePins(); return; - } - _pins[i] = currentPin; //store only after allocatePin() succeeds - #ifdef ESP8266 - pinMode(_pins[i], OUTPUT); - #else - ledcSetup(_ledcStart + i, WLED_PWM_FREQ, 8); - ledcAttachPin(_pins[i], _ledcStart + i); - #endif - } - reversed = bc.reversed; - _valid = true; - }; + //does no index check + uint32_t getPixelColor(uint16_t pix); - void setPixelColor(uint16_t pix, uint32_t c) { - if (pix != 0 || !_valid) return; //only react to first pixel - if (_type != TYPE_ANALOG_3CH) c = autoWhiteCalc(c); - if (_cct >= 1900 && (_type == TYPE_ANALOG_3CH || _type == TYPE_ANALOG_4CH)) { - c = colorBalanceFromKelvin(_cct, c); //color correction from CCT - } - uint8_t r = R(c); - uint8_t g = G(c); - uint8_t b = B(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; + void show(); + + uint8_t getPins(uint8_t* pinArray); + + void cleanup() { + deallocatePins(); } - 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) { - case TYPE_ANALOG_1CH: //one channel (white), relies on auto white calculation - _data[0] = w; - break; - case TYPE_ANALOG_2CH: //warm white + cold white - _data[1] = cw; - _data[0] = ww; - break; - case TYPE_ANALOG_5CH: //RGB + warm white + cold white - _data[4] = cw; - w = ww; - case TYPE_ANALOG_4CH: //RGBW - _data[3] = w; - case TYPE_ANALOG_3CH: //standard dumb RGB - _data[0] = r; _data[1] = g; _data[2] = b; - break; + ~BusPwm() { + cleanup(); } - } - - //does no index check - uint32_t getPixelColor(uint16_t pix) { - if (!_valid) return 0; - return RGBW32(_data[0], _data[1], _data[2], _data[3]); - } - - void show() { - if (!_valid) return; - uint8_t numPins = NUM_PWM_PINS(_type); - for (uint8_t i = 0; i < numPins; i++) { - uint8_t scaled = (_data[i] * _bri) / 255; - if (reversed) scaled = 255 - scaled; - #ifdef ESP8266 - analogWrite(_pins[i], scaled); - #else - ledcWrite(_ledcStart + i, scaled); - #endif - } - } - - uint8_t getPins(uint8_t* pinArray) { - if (!_valid) return 0; - uint8_t numPins = NUM_PWM_PINS(_type); - for (uint8_t i = 0; i < numPins; i++) { - pinArray[i] = _pins[i]; - } - return numPins; - } - - void cleanup() { - deallocatePins(); - } - - ~BusPwm() { - cleanup(); - } private: - uint8_t _pins[5] = {255, 255, 255, 255, 255}; - uint8_t _data[5] = {0}; - #ifdef ARDUINO_ARCH_ESP32 - uint8_t _ledcStart = 255; - #endif - - void deallocatePins() { - uint8_t numPins = NUM_PWM_PINS(_type); - for (uint8_t i = 0; i < numPins; i++) { - pinManager.deallocatePin(_pins[i], PinOwner::BusPwm); - if (!pinManager.isPinOk(_pins[i])) continue; - #ifdef ESP8266 - digitalWrite(_pins[i], LOW); //turn off PWM interrupt - #else - if (_ledcStart < 16) ledcDetachPin(_pins[i]); - #endif - } + uint8_t _pins[5] = {255, 255, 255, 255, 255}; + uint8_t _data[5] = {0}; #ifdef ARDUINO_ARCH_ESP32 - pinManager.deallocateLedc(_ledcStart, numPins); + uint8_t _ledcStart = 255; #endif - } + + void deallocatePins(); }; class BusOnOff : public Bus { public: - BusOnOff(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { - _valid = false; - if (bc.type != TYPE_ONOFF) return; + BusOnOff(BusConfig &bc); - uint8_t currentPin = bc.pins[0]; - if (!pinManager.allocatePin(currentPin, true, PinOwner::BusOnOff)) { - return; + void setPixelColor(uint16_t pix, uint32_t c); + + uint32_t getPixelColor(uint16_t pix); + + void show(); + + uint8_t getPins(uint8_t* pinArray); + + void cleanup() { + pinManager.deallocatePin(_pin, PinOwner::BusOnOff); } - _pin = currentPin; //store only after allocatePin() succeeds - pinMode(_pin, OUTPUT); - reversed = bc.reversed; - _valid = true; - }; - void setPixelColor(uint16_t pix, uint32_t c) { - if (pix != 0 || !_valid) return; //only react to first pixel - c = autoWhiteCalc(c); - uint8_t r = R(c); - uint8_t g = G(c); - uint8_t b = B(c); - uint8_t w = W(c); - - _data = bool((r+g+b+w) && _bri) ? 0xFF : 0; - } - - uint32_t getPixelColor(uint16_t pix) { - if (!_valid) return 0; - return RGBW32(_data, _data, _data, _data); - } - - void show() { - if (!_valid) return; - digitalWrite(_pin, reversed ? !(bool)_data : (bool)_data); - } - - uint8_t getPins(uint8_t* pinArray) { - if (!_valid) return 0; - pinArray[0] = _pin; - return 1; - } - - void cleanup() { - pinManager.deallocatePin(_pin, PinOwner::BusOnOff); - } - - ~BusOnOff() { - cleanup(); - } + ~BusOnOff() { + cleanup(); + } private: - uint8_t _pin = 255; - uint8_t _data = 0; + uint8_t _pin = 255; + uint8_t _data = 0; }; class BusNetwork : public Bus { public: - BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { - _valid = false; -// switch (bc.type) { -// case TYPE_NET_ARTNET_RGB: -// _rgbw = false; -// _UDPtype = 2; -// break; -// case TYPE_NET_E131_RGB: -// _rgbw = false; -// _UDPtype = 1; -// break; -// case TYPE_NET_DDP_RGB: -// _rgbw = false; -// _UDPtype = 0; -// break; -// default: // TYPE_NET_DDP_RGB / TYPE_NET_DDP_RGBW - _rgbw = bc.type == TYPE_NET_DDP_RGBW; - _UDPtype = 0; -// break; -// } - _UDPchannels = _rgbw ? 4 : 3; - _data = (byte *)malloc(bc.count * _UDPchannels); - if (_data == nullptr) return; - memset(_data, 0, bc.count * _UDPchannels); - _len = bc.count; - _client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]); - _broadcastLock = false; - _valid = true; - }; + BusNetwork(BusConfig &bc); - bool hasRGB() { return true; } - bool hasWhite() { return _rgbw; } + bool hasRGB() { return true; } + bool hasWhite() { return _rgbw; } - void setPixelColor(uint16_t pix, uint32_t c) { - if (!_valid || pix >= _len) return; - if (isRgbw()) c = autoWhiteCalc(c); - if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT - uint16_t offset = pix * _UDPchannels; - _data[offset] = R(c); - _data[offset+1] = G(c); - _data[offset+2] = B(c); - if (_rgbw) _data[offset+3] = W(c); - } + void setPixelColor(uint16_t pix, uint32_t c); - uint32_t getPixelColor(uint16_t pix) { - if (!_valid || pix >= _len) return 0; - uint16_t offset = pix * _UDPchannels; - return RGBW32(_data[offset], _data[offset+1], _data[offset+2], _rgbw ? (_data[offset+3] << 24) : 0); - } + uint32_t getPixelColor(uint16_t pix); - void show() { - if (!_valid || !canShow()) return; - _broadcastLock = true; - realtimeBroadcast(_UDPtype, _client, _len, _data, _bri, _rgbw); - _broadcastLock = false; - } + void show(); - inline bool canShow() { - // this should be a return value from UDP routine if it is still sending data out - return !_broadcastLock; - } - - uint8_t getPins(uint8_t* pinArray) { - for (uint8_t i = 0; i < 4; i++) { - pinArray[i] = _client[i]; + bool canShow() { + // this should be a return value from UDP routine if it is still sending data out + return !_broadcastLock; } - return 4; - } - inline bool isRgbw() { - return _rgbw; - } + uint8_t getPins(uint8_t* pinArray); - inline uint16_t getLength() { - return _len; - } + uint16_t getLength() { + return _len; + } - void cleanup() { - _type = I_NONE; - _valid = false; - if (_data != nullptr) free(_data); - _data = nullptr; - } + void cleanup(); - ~BusNetwork() { - cleanup(); - } + ~BusNetwork() { + cleanup(); + } private: IPAddress _client; @@ -661,154 +321,56 @@ class BusNetwork : public Bus { class BusManager { public: - BusManager() {}; + BusManager() {}; - //utility to get the approx. memory usage of a given BusConfig - static uint32_t memUsage(BusConfig &bc) { - uint8_t type = bc.type; - uint16_t len = bc.count + bc.skipAmount; - if (type > 15 && type < 32) { - #ifdef ESP8266 - if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem - if (type > 29) return len*20; //RGBW - return len*15; - } - if (type > 29) return len*4; //RGBW - return len*3; - #else //ESP32 RMT uses double buffer? - if (type > 29) return len*8; //RGBW - return len*6; - #endif + //utility to get the approx. memory usage of a given BusConfig + static uint32_t memUsage(BusConfig &bc); + + int add(BusConfig &bc); + + //do not call this method from system context (network callback) + void removeAll(); + + void show(); + + void setStatusPixel(uint32_t c); + + void IRAM_ATTR setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1); + + void setBrightness(uint8_t b); + + void setSegmentCCT(int16_t cct, bool allowWBCorrection = false); + + uint32_t getPixelColor(uint16_t pix); + + bool canAllShow(); + + Bus* getBus(uint8_t busNr); + + //semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit()) + uint16_t getTotalLength(); + + inline void updateColorOrderMap(const ColorOrderMap &com) { + memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap)); } - if (type > 31 && type < 48) return 5; - if (type == 44 || type == 45) return len*4; //RGBW - return len*3; //RGB - } -/* - int add(BusConfig &bc); - void removeAll(); //do not call this method from system context (network callback) - void setStatusPixel(uint32_t c); - void setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1); - void setBrightness(uint8_t b); - void setSegmentCCT(int16_t cct, bool allowWBCorrection = false); - uint32_t getPixelColor(uint16_t pix); - bool canAllShow(); - Bus* getBus(uint8_t busNr); - void show(); - uint16_t getTotalLength(); //semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit()) -*/ - // the following functions are inlined by compiler since they are defined within class definition - // they should be placed in cpp file instead - int add(BusConfig &bc) { - if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1; - if (bc.type >= TYPE_NET_DDP_RGB && bc.type < 96) { - busses[numBusses] = new BusNetwork(bc); - } else if (IS_DIGITAL(bc.type)) { - busses[numBusses] = new BusDigital(bc, numBusses, colorOrderMap); - } else if (bc.type == TYPE_ONOFF) { - busses[numBusses] = new BusOnOff(bc); - } else { - busses[numBusses] = new BusPwm(bc); + inline const ColorOrderMap& getColorOrderMap() const { + return colorOrderMap; } - return numBusses++; - } - //do not call this method from system context (network callback) - void removeAll() { - DEBUG_PRINTLN(F("Removing all.")); - //prevents crashes due to deleting busses while in use. - while (!canAllShow()) yield(); - for (uint8_t i = 0; i < numBusses; i++) delete busses[i]; - numBusses = 0; - } - - void show() { - for (uint8_t i = 0; i < numBusses; i++) { - busses[i]->show(); + inline uint8_t getNumBusses() { + return numBusses; } - } - - void setStatusPixel(uint32_t c) { - for (uint8_t i = 0; i < numBusses; i++) { - busses[i]->setStatusPixel(c); - } - } - - void IRAM_ATTR setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1) { - for (uint8_t i = 0; i < numBusses; i++) { - Bus* b = busses[i]; - uint16_t bstart = b->getStart(); - if (pix < bstart || pix >= bstart + b->getLength()) continue; - busses[i]->setPixelColor(pix - bstart, c); - } - } - - void setBrightness(uint8_t b) { - for (uint8_t i = 0; i < numBusses; i++) { - busses[i]->setBrightness(b); - } - } - - void setSegmentCCT(int16_t cct, bool allowWBCorrection = false) { - if (cct > 255) cct = 255; - if (cct >= 0) { - //if white balance correction allowed, save as kelvin value instead of 0-255 - if (allowWBCorrection) cct = 1900 + (cct << 5); - } else cct = -1; - Bus::setCCT(cct); - } - - uint32_t getPixelColor(uint16_t pix) { - for (uint8_t i = 0; i < numBusses; i++) { - Bus* b = busses[i]; - uint16_t bstart = b->getStart(); - if (pix < bstart || pix >= bstart + b->getLength()) continue; - return b->getPixelColor(pix - bstart); - } - return 0; - } - - bool canAllShow() { - for (uint8_t i = 0; i < numBusses; i++) { - if (!busses[i]->canShow()) return false; - } - return true; - } - - Bus* getBus(uint8_t busNr) { - if (busNr >= numBusses) return nullptr; - return busses[busNr]; - } - - //semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit()) - uint16_t getTotalLength() { - uint16_t len = 0; - for (uint8_t i=0; igetLength(); - return len; - } - - inline void updateColorOrderMap(const ColorOrderMap &com) { - memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap)); - } - - inline const ColorOrderMap& getColorOrderMap() const { - return colorOrderMap; - } - - inline uint8_t getNumBusses() { - return numBusses; - } private: - uint8_t numBusses = 0; - Bus* busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES]; - ColorOrderMap colorOrderMap; + uint8_t numBusses = 0; + Bus* busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES]; + ColorOrderMap colorOrderMap; - inline uint8_t getNumVirtualBusses() { - int j = 0; - for (int i=0; igetType() >= TYPE_NET_DDP_RGB && busses[i]->getType() < 96) j++; - return j; - } + inline uint8_t getNumVirtualBusses() { + int j = 0; + for (int i=0; igetType() >= TYPE_NET_DDP_RGB && busses[i]->getType() < 96) j++; + return j; + } }; -#endif +#endif \ No newline at end of file diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h index 323f75af..bbf38d2e 100644 --- a/wled00/bus_wrapper.h +++ b/wled00/bus_wrapper.h @@ -182,7 +182,12 @@ #endif //APA102 -#define B_HS_DOT_3 NeoPixelBrightnessBus //hardware SPI +#ifdef WLED_USE_ETHERNET +// fix for #2542 (by @BlackBird77) +#define B_HS_DOT_3 NeoPixelBrightnessBus //hardware HSPI with DMA (ESP32 only) +#else +#define B_HS_DOT_3 NeoPixelBrightnessBus //hardware HSPI +#endif #define B_SS_DOT_3 NeoPixelBrightnessBus //soft SPI //LPD8806 @@ -894,6 +899,8 @@ class PolyBus { uint8_t offset = pins[0] -1; //for driver: 0 = uart0, 1 = uart1, 2 = dma, 3 = bitbang if (offset > 3) offset = 3; switch (busType) { + case TYPE_WS2812_1CH_X3: + case TYPE_WS2812_2CH_X3: case TYPE_WS2812_RGB: case TYPE_WS2812_WWA: return I_8266_U0_NEO_3 + offset; @@ -926,6 +933,8 @@ class PolyBus { if (num > 7) offset = num -7; #endif switch (busType) { + case TYPE_WS2812_1CH_X3: + case TYPE_WS2812_2CH_X3: case TYPE_WS2812_RGB: case TYPE_WS2812_WWA: return I_32_RN_NEO_3 + offset; diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 2aa73652..d0a731b0 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -17,6 +17,10 @@ void getStringFromJson(char* dest, const char* src, size_t len) { } bool deserializeConfig(JsonObject doc, bool fromFS) { + + //WLEDMM add USER_PRINT + USER_PRINTF("deserializeConfig\n"); + bool needsSave = false; //int rev_major = doc["rev"][0]; // 1 //int rev_minor = doc["rev"][1]; // 0 @@ -87,7 +91,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { uint8_t autoWhiteMode = RGBW_MODE_MANUAL_ONLY; CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]); CJSON(strip.milliampsPerLed, hw_led[F("ledma")]); - Bus::setAutoWhiteMode(hw_led[F("rgbwm")] | 255); + Bus::setGlobalAWMode(hw_led[F("rgbwm")] | 255); CJSON(correctWB, hw_led["cct"]); CJSON(cctFromRgb, hw_led[F("cr")]); CJSON(strip.cctBlending, hw_led[F("cb")]); @@ -100,13 +104,20 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { JsonObject matrix = hw_led[F("matrix")]; if (!matrix.isNull()) { strip.isMatrix = true; + + //WLEDMM: keep storing basic 2d setup CJSON(strip.panels, matrix[F("mpc")]); - CJSON(strip.panelsV, matrix[F("mpv")]); //WLEDMM quick fix - CJSON(strip.panelsH, matrix[F("mph")]); //WLEDMM quick fix + CJSON(strip.bOrA, matrix["ba"]); //WLEDMM basic or advanced + CJSON(strip.panelsV, matrix[F("mpv")]); + CJSON(strip.panelsH, matrix[F("mph")]); CJSON(strip.matrix.bottomStart, matrix[F("pb")]); CJSON(strip.matrix.rightStart, matrix[F("pr")]); CJSON(strip.matrix.vertical, matrix[F("pv")]); CJSON(strip.matrix.serpentine, matrix["ps"]); + CJSON(strip.panelO.bottomStart, matrix[F("pbl")]); + CJSON(strip.panelO.rightStart, matrix[F("prl")]); + CJSON(strip.panelO.vertical, matrix[F("pvl")]); + CJSON(strip.panelO.serpentine, matrix["psl"]); strip.panel.clear(); JsonArray panels = matrix[F("panels")]; @@ -135,8 +146,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { p.options = 0; strip.panel.push_back(p); } - - strip.setUpMatrix(); + // cannot call strip.setUpMatrix() here due to already locked JSON buffer } #endif @@ -310,9 +320,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { Wire.setPins(i2c_sda, i2c_scl); // this will fail if Wire is initilised (Wire.begin() called prior) #endif // Wire.begin(); // WLEDMM moved into pinManager - Serial.printf("pinmgr success for global i2c %d %d\n", i2c_sda, i2c_scl); + DEBUG_PRINTF("pinmgr success for global i2c %d %d\n", i2c_sda, i2c_scl); } else { - Serial.printf("pinmgr not success for global i2c %d %d\n", i2c_sda, i2c_scl); + DEBUG_PRINTF("pinmgr not success for global i2c %d %d\n", i2c_sda, i2c_scl); } JsonArray hw_if_spi = hw[F("if")][F("spi-pin")]; CJSON(spi_mosi, hw_if_spi[0]); @@ -325,9 +335,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { #else SPI.begin(); #endif - Serial.printf("pinmgr success for global spi %d %d %d\n", spi_mosi, spi_miso, spi_sclk); + DEBUG_PRINTF("pinmgr success for global spi %d %d %d\n", spi_mosi, spi_miso, spi_sclk); } else { - Serial.printf("pinmgr not success for global spi %d %d %d\n", spi_mosi, spi_miso, spi_sclk); + DEBUG_PRINTF("pinmgr not success for global spi %d %d %d\n", spi_mosi, spi_miso, spi_sclk); } //int hw_status_pin = hw[F("status")]["pin"]; // -1 @@ -337,18 +347,27 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { CJSON(strip.paletteBlend, light[F("pal-mode")]); CJSON(autoSegments, light[F("aseg")]); + CJSON(gammaCorrectVal, light["gc"]["val"]); // default 2.8 float light_gc_bri = light["gc"]["bri"]; - float light_gc_col = light["gc"]["col"]; // 2.8 - if (light_gc_bri > 1.5) gammaCorrectBri = true; - else if (light_gc_bri > 0.5) gammaCorrectBri = false; - if (light_gc_col > 1.5) gammaCorrectCol = true; - else if (light_gc_col > 0.5) gammaCorrectCol = false; + float light_gc_col = light["gc"]["col"]; + if (light_gc_bri > 1.0f) gammaCorrectBri = true; + else gammaCorrectBri = false; + if (light_gc_col > 1.0f) gammaCorrectCol = true; + else gammaCorrectCol = false; + if (gammaCorrectVal > 1.0f && gammaCorrectVal <= 3) { + if (gammaCorrectVal != 2.8f) calcGammaTable(gammaCorrectVal); + } else { + gammaCorrectVal = 1.0f; // no gamma correction + gammaCorrectBri = false; + gammaCorrectCol = false; + } JsonObject light_tr = light["tr"]; CJSON(fadeTransition, light_tr["mode"]); int tdd = light_tr["dur"] | -1; if (tdd >= 0) transitionDelay = transitionDelayDefault = tdd * 100; CJSON(strip.paletteFade, light_tr["pal"]); + CJSON(randomPaletteChangeTime, light_tr[F("rpc")]); JsonObject light_nl = light["nl"]; CJSON(nightlightMode, light_nl["mode"]); @@ -410,10 +429,19 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { if (!DMXAddress || DMXAddress > 510) DMXAddress = 1; CJSON(DMXSegmentSpacing, if_live_dmx[F("dss")]); if (DMXSegmentSpacing > 150) DMXSegmentSpacing = 0; + CJSON(e131Priority, if_live_dmx[F("e131prio")]); + if (e131Priority > 200) e131Priority = 200; CJSON(DMXMode, if_live_dmx["mode"]); tdd = if_live[F("timeout")] | -1; if (tdd >= 0) realtimeTimeoutMs = tdd * 100; + + #ifdef WLED_ENABLE_DMX_INPUT + CJSON(dmxTransmitPin, if_live_dmx[F("rxPin")]); + CJSON(dmxReceivePin, if_live_dmx[F("txPin")]); + CJSON(dmxEnablePin, if_live_dmx[F("enablePin")]); + #endif + CJSON(arlsForceMaxBri, if_live[F("maxbri")]); CJSON(arlsDisableGammaCorrection, if_live[F("no-gc")]); // false CJSON(arlsOffset, if_live[F("offset")]); // 0 @@ -425,17 +453,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { CJSON(alexaNumPresets, interfaces["va"]["p"]); -#ifndef WLED_DISABLE_BLYNK - const char* apikey = interfaces["blynk"][F("token")] | "Hidden"; - tdd = strnlen(apikey, 36); - if (tdd > 20 || tdd == 0) - getStringFromJson(blynkApiKey, apikey, 36); //normally not present due to security - - JsonObject if_blynk = interfaces["blynk"]; - getStringFromJson(blynkHost, if_blynk[F("host")], 33); - CJSON(blynkPort, if_blynk["port"]); -#endif - #ifdef WLED_ENABLE_MQTT JsonObject if_mqtt = interfaces["mqtt"]; CJSON(mqttEnabled, if_mqtt["en"]); @@ -467,6 +484,18 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { CJSON(hueIP[i], if_hue_ip[i]); #endif +//WLEDMM: add netdebug variables +#ifdef WLED_DEBUG_HOST + JsonObject if_ndb = interfaces["ndb"]; + JsonArray if_ndb_ip = if_ndb["ip"]; + for (byte i = 0; i < 4; i++) + CJSON(netDebugPrintIP[i], if_ndb_ip[i]); + CJSON(netDebugPrintPort, if_ndb["port"]); + CJSON(netDebugEnabled, if_ndb["enabled"]); + // USER_PRINTF("deserializeConfig %d\n", netDebugEnabled); + pinManager.manageDebugTXPin(); +#endif + JsonObject if_ntp = interfaces[F("ntp")]; CJSON(ntpEnabled, if_ntp["en"]); getStringFromJson(ntpServerName, if_ntp[F("host")], 33); // "1.wled.pool.ntp.org" @@ -704,7 +733,7 @@ void serializeConfig() { hw_led[F("cr")] = cctFromRgb; hw_led[F("cb")] = strip.cctBlending; hw_led["fps"] = strip.getTargetFps(); - hw_led[F("rgbwm")] = Bus::getAutoWhiteMode(); // global override + hw_led[F("rgbwm")] = Bus::getGlobalAWMode(); // global auto white mode override hw_led[F("ld")] = strip.useLedsArray; #ifndef WLED_DISABLE_2D @@ -712,8 +741,11 @@ void serializeConfig() { if (strip.isMatrix) { JsonObject matrix = hw_led.createNestedObject(F("matrix")); matrix[F("mpc")] = strip.panels; - matrix[F("mph")] = strip.panelsH; //WLEDMM quick fix - matrix[F("mpv")] = strip.panelsV; //WLEDMM quick fix + + //WLEDMM: keep storing basic 2d setup + matrix[F("ba")] = strip.bOrA; //WLEDMM basic or advanced + matrix[F("mph")] = strip.panelsH; + matrix[F("mpv")] = strip.panelsV; matrix[F("pb")] = strip.matrix.bottomStart; matrix[F("pr")] = strip.matrix.rightStart; matrix[F("pv")] = strip.matrix.vertical; @@ -751,7 +783,7 @@ void serializeConfig() { ins[F("skip")] = bus->skippedLeds(); ins["type"] = bus->getType() & 0x7F; ins["ref"] = bus->isOffRefreshRequired(); - ins[F("rgbwm")] = bus->getAWMode(); + ins[F("rgbwm")] = bus->getAutoWhiteMode(); } JsonArray hw_com = hw.createNestedArray(F("com")); @@ -816,13 +848,15 @@ void serializeConfig() { light[F("aseg")] = autoSegments; JsonObject light_gc = light.createNestedObject("gc"); - light_gc["bri"] = (gammaCorrectBri) ? 2.8 : 1.0; - light_gc["col"] = (gammaCorrectCol) ? 2.8 : 1.0; + light_gc["bri"] = (gammaCorrectBri) ? gammaCorrectVal : 1.0f; // keep compatibility + light_gc["col"] = (gammaCorrectCol) ? gammaCorrectVal : 1.0f; // keep compatibility + light_gc["val"] = gammaCorrectVal; JsonObject light_tr = light.createNestedObject("tr"); light_tr["mode"] = fadeTransition; light_tr["dur"] = transitionDelayDefault / 100; light_tr["pal"] = strip.paletteFade; + light_tr[F("rpc")] = randomPaletteChangeTime; JsonObject light_nl = light.createNestedObject("nl"); light_nl["mode"] = nightlightMode; @@ -871,9 +905,15 @@ void serializeConfig() { JsonObject if_live_dmx = if_live.createNestedObject("dmx"); if_live_dmx[F("uni")] = e131Universe; if_live_dmx[F("seqskip")] = e131SkipOutOfSequence; + if_live_dmx[F("e131prio")] = e131Priority; if_live_dmx[F("addr")] = DMXAddress; if_live_dmx[F("dss")] = DMXSegmentSpacing; if_live_dmx["mode"] = DMXMode; + #ifdef WLED_ENABLE_DMX_INPUT + if_live_dmx[F("rxPin")] = dmxTransmitPin; + if_live_dmx[F("txPin")] = dmxReceivePin; + if_live_dmx[F("enablePin")] = dmxEnablePin; + #endif if_live[F("timeout")] = realtimeTimeoutMs / 100; if_live[F("maxbri")] = arlsForceMaxBri; @@ -889,13 +929,6 @@ void serializeConfig() { if_va["p"] = alexaNumPresets; -#ifndef WLED_DISABLE_BLYNK - JsonObject if_blynk = interfaces.createNestedObject("blynk"); - if_blynk[F("token")] = strlen(blynkApiKey) ? "Hidden":""; - if_blynk[F("host")] = blynkHost; - if_blynk["port"] = blynkPort; -#endif - #ifdef WLED_ENABLE_MQTT JsonObject if_mqtt = interfaces.createNestedObject("mqtt"); if_mqtt["en"] = mqttEnabled; @@ -927,6 +960,18 @@ void serializeConfig() { } #endif +//WLEDMM: add netdebug variables +#ifdef WLED_DEBUG_HOST + JsonObject if_ndb = interfaces.createNestedObject("ndb"); + JsonArray if_ndb_ip = if_ndb.createNestedArray("ip"); + for (byte i = 0; i < 4; i++) { + if_ndb_ip.add(netDebugPrintIP[i]); + } + if_ndb["port"] = netDebugPrintPort; + if_ndb["enabled"] = netDebugEnabled; + // USER_PRINTF("serializeConfig %d\n", netDebugEnabled); +#endif + JsonObject if_ntp = interfaces.createNestedObject("ntp"); if_ntp["en"] = ntpEnabled; if_ntp[F("host")] = ntpServerName; @@ -998,6 +1043,9 @@ void serializeConfig() { JsonObject usermods_settings = doc.createNestedObject("um"); usermods.addToConfig(usermods_settings); + //WLEDMM add USER_PRINT + USER_PRINTF("serializeConfig\n"); + File f = WLED_FS.open("/cfg.json", "w"); if (f) serializeJson(doc, f); f.close(); @@ -1024,13 +1072,8 @@ bool deserializeConfigSec() { JsonObject ap = doc["ap"]; getStringFromJson(apPass, ap["psk"] , 65); +#if defined(WLED_ENABLE_MQTT) || !defined(WLED_DISABLE_HUESYNC) JsonObject interfaces = doc["if"]; - -#ifndef WLED_DISABLE_BLYNK - const char* apikey = interfaces["blynk"][F("token")] | "Hidden"; - int tdd = strnlen(apikey, 36); - if (tdd > 20 || tdd == 0) - getStringFromJson(blynkApiKey, apikey, 36); #endif #ifdef WLED_ENABLE_MQTT @@ -1070,11 +1113,10 @@ void serializeConfigSec() { JsonObject ap = doc.createNestedObject("ap"); ap["psk"] = apPass; +#if defined(WLED_ENABLE_MQTT) || !defined(WLED_DISABLE_HUESYNC) JsonObject interfaces = doc.createNestedObject("if"); -#ifndef WLED_DISABLE_BLYNK - JsonObject if_blynk = interfaces.createNestedObject("blynk"); - if_blynk[F("token")] = blynkApiKey; #endif + #ifdef WLED_ENABLE_MQTT JsonObject if_mqtt = interfaces.createNestedObject("mqtt"); if_mqtt["psk"] = mqttPass; diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 3cba87d2..61457eba 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -57,39 +57,42 @@ void setRandomColor(byte* rgb) void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) //hue, sat to rgb { - float h = ((float)hue)/65535.0; - float s = ((float)sat)/255.0; - byte i = floor(h*6); - float f = h * 6-i; - float p = 255 * (1-s); - float q = 255 * (1-f*s); - float t = 255 * (1-(1-f)*s); + float h = ((float)hue)/65535.0f; + float s = ((float)sat)/255.0f; + int i = floorf(h*6); + float f = h * 6.0f - i; + int p = int(255.0f * (1.0f-s)); + int q = int(255.0f * (1.0f-f*s)); + int t = int(255.0f * (1.0f-(1.0f-f)*s)); + p = constrain(p, 0, 255); + q = constrain(q, 0, 255); + t = constrain(t, 0, 255); switch (i%6) { - case 0: rgb[0]=255,rgb[1]=t,rgb[2]=p;break; - case 1: rgb[0]=q,rgb[1]=255,rgb[2]=p;break; - case 2: rgb[0]=p,rgb[1]=255,rgb[2]=t;break; - case 3: rgb[0]=p,rgb[1]=q,rgb[2]=255;break; - case 4: rgb[0]=t,rgb[1]=p,rgb[2]=255;break; - case 5: rgb[0]=255,rgb[1]=p,rgb[2]=q; + case 0: rgb[0]=255,rgb[1]=t, rgb[2]=p; break; + case 1: rgb[0]=q, rgb[1]=255,rgb[2]=p; break; + case 2: rgb[0]=p, rgb[1]=255,rgb[2]=t; break; + case 3: rgb[0]=p, rgb[1]=q, rgb[2]=255;break; + case 4: rgb[0]=t, rgb[1]=p, rgb[2]=255;break; + case 5: rgb[0]=255,rgb[1]=p, rgb[2]=q; break; } } //get RGB values from color temperature in K (https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html) void colorKtoRGB(uint16_t kelvin, byte* rgb) //white spectrum to rgb, calc { - float r = 0, g = 0, b = 0; - float temp = kelvin / 100; - if (temp <= 66) { + int r = 0, g = 0, b = 0; + float temp = kelvin / 100.0f; + if (temp <= 66.0f) { r = 255; - g = round(99.4708025861 * log(temp) - 161.1195681661); - if (temp <= 19) { + g = roundf(99.4708025861f * logf(temp) - 161.1195681661f); + if (temp <= 19.0f) { b = 0; } else { - b = round(138.5177312231 * log((temp - 10)) - 305.0447927307); + b = roundf(138.5177312231f * logf((temp - 10.0f)) - 305.0447927307f); } } else { - r = round(329.698727446 * pow((temp - 60), -0.1332047592)); - g = round(288.1221695283 * pow((temp - 60), -0.0755148492)); + r = roundf(329.698727446f * powf((temp - 60.0f), -0.1332047592f)); + g = roundf(288.1221695283f * powf((temp - 60.0f), -0.0755148492f)); b = 255; } //g += 12; //mod by Aircoookie, a bit less accurate but visibly less pinkish @@ -147,9 +150,9 @@ void colorXYtoRGB(float x, float y, byte* rgb) //coordinates to rgb (https://www b = 1.0f; } // Apply gamma correction - r = r <= 0.0031308f ? 12.92f * r : (1.0f + 0.055f) * pow(r, (1.0f / 2.4f)) - 0.055f; - g = g <= 0.0031308f ? 12.92f * g : (1.0f + 0.055f) * pow(g, (1.0f / 2.4f)) - 0.055f; - b = b <= 0.0031308f ? 12.92f * b : (1.0f + 0.055f) * pow(b, (1.0f / 2.4f)) - 0.055f; + r = r <= 0.0031308f ? 12.92f * r : (1.0f + 0.055f) * powf(r, (1.0f / 2.4f)) - 0.055f; + g = g <= 0.0031308f ? 12.92f * g : (1.0f + 0.055f) * powf(g, (1.0f / 2.4f)) - 0.055f; + b = b <= 0.0031308f ? 12.92f * b : (1.0f + 0.055f) * powf(b, (1.0f / 2.4f)) - 0.055f; if (r > b && r > g) { // red is biggest @@ -173,9 +176,9 @@ void colorXYtoRGB(float x, float y, byte* rgb) //coordinates to rgb (https://www b = 1.0f; } } - rgb[0] = 255.0*r; - rgb[1] = 255.0*g; - rgb[2] = 255.0*b; + rgb[0] = byte(255.0f*r); + rgb[1] = byte(255.0f*g); + rgb[2] = byte(255.0f*b); } void colorRGBtoXY(byte* rgb, float* xy) //rgb to coordinates (https://www.developers.meethue.com/documentation/color-conversions-rgb-xy) @@ -242,35 +245,13 @@ float maxf (float v, float w) return v; } -/* -uint32_t colorRGBtoRGBW(uint32_t c) -{ - byte rgb[4]; - rgb[0] = R(c); - rgb[1] = G(c); - rgb[2] = B(c); - rgb[3] = W(c); - colorRGBtoRGBW(rgb); - return RGBW32(rgb[0], rgb[1], rgb[2], rgb[3]); -} - -void colorRGBtoRGBW(byte* rgb) //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_MODE_LEGACY) -{ - float low = minf(rgb[0],minf(rgb[1],rgb[2])); - float high = maxf(rgb[0],maxf(rgb[1],rgb[2])); - if (high < 0.1f) return; - float sat = 100.0f * ((high - low) / high); // maximum saturation is 100 (corrected from 255) - rgb[3] = (byte)((255.0f - sat) / 255.0f * (rgb[0] + rgb[1] + rgb[2]) / 3); -} -*/ - -byte correctionRGB[4] = {0,0,0,0}; -uint16_t lastKelvin = 0; - // adjust RGB values based on color temperature in K (range [2800-10200]) (https://en.wikipedia.org/wiki/Color_balance) +// called from bus manager when color correction is enabled! uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb) { //remember so that slow colorKtoRGB() doesn't have to run for every setPixelColor() + static byte correctionRGB[4] = {0,0,0,0}; + static uint16_t lastKelvin = 0; if (lastKelvin != kelvin) colorKtoRGB(kelvin, correctionRGB); // convert Kelvin to RGB lastKelvin = kelvin; byte rgbw[4]; @@ -345,7 +326,7 @@ static byte gammaT[256] = { // https://github.com/Aircoookie/WLED/issues/2767#issuecomment-1310961308 // unfortunately NepixelsBu has its own internal table, that kills low brightness values similar to the original WLED table. // see https://github.com/Makuna/NeoPixelBus/blob/master/src/internal/NeoGamma.h -static byte gammaT[256] = { +static const byte gammaT[256] = { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, @@ -373,9 +354,11 @@ uint8_t gamma8_cal(uint8_t b, float gamma) // re-calculates & fills gamma table void calcGammaTable(float gamma) { +#if !defined(WLED_USE_CIE_BRIGHTNESS_TABLE) // WLEDMM not possible when using the CIE table for (uint16_t i = 0; i < 256; i++) { gammaT[i] = gamma8_cal(i, gamma); } +#endif } // used for individual channel or brightness gamma correction diff --git a/wled00/const.h b/wled00/const.h index f14bf5ed..7f361d07 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -5,7 +5,7 @@ * Readability defines and their associated numerical values + compile-time constants */ -#define GRADIENT_PALETTE_COUNT 61 //WLEDMM netmindz ar palette +3 +#define GRADIENT_PALETTE_COUNT 62 //WLEDMM netmindz ar palette +3, ewowi Random Smooth palette +1 //Defaults #define DEFAULT_CLIENT_SSID "Your_Network" @@ -79,6 +79,17 @@ #define WLED_MAX_COLOR_ORDER_MAPPINGS 10 #endif +#if defined(WLED_MAX_LEDMAPS) && (WLED_MAX_LEDMAPS > 32 || WLED_MAX_LEDMAPS < 10) + #undef WLED_MAX_LEDMAPS +#endif +#ifndef WLED_MAX_LEDMAPS + #ifdef ESP8266 + #define WLED_MAX_LEDMAPS 10 + #else + #define WLED_MAX_LEDMAPS 16 + #endif +#endif + //Usermod IDs #define USERMOD_ID_RESERVED 0 //Unused. Might indicate no usermod present #define USERMOD_ID_UNSPECIFIED 1 //Default value for a general user mod that does not specify a custom ID @@ -120,10 +131,12 @@ #define USERMOD_ID_SD_CARD 37 //Usermod "usermod_sd_card.h" #define USERMOD_ID_PWM_OUTPUTS 38 //Usermod "usermod_pwm_outputs.h #define USERMOD_ID_SHT 39 //Usermod "usermod_sht.h +#define USERMOD_ID_KLIPPER 40 // Usermod Klipper percentage //WLEDMM -#define USERMOD_ID_CUSTOMEFFECTS 90 //Usermod "usermod_v2_customeffects.h" +#define USERMOD_ID_ARTIFX 90 //Usermod "usermod_v2_artifx.h" #define USERMOD_ID_WEATHER 91 //Usermod "usermod_v2_weather.h" #define USERMOD_ID_GAMES 92 //Usermod "usermod_v2_games.h" +#define USERMOD_ID_FASTLED 93 //Usermod "usermod_v2_fastled.h" //Access point behavior #define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot @@ -141,17 +154,19 @@ #define CALL_MODE_FX_CHANGED 6 //no longer used #define CALL_MODE_HUE 7 #define CALL_MODE_PRESET_CYCLE 8 -#define CALL_MODE_BLYNK 9 +#define CALL_MODE_BLYNK 9 //no longer used #define CALL_MODE_ALEXA 10 #define CALL_MODE_WS_SEND 11 //special call mode, not for notifier, updates websocket only #define CALL_MODE_BUTTON_PRESET 12 //button/IR JSON preset/macro //RGB to RGBW conversion mode -#define RGBW_MODE_MANUAL_ONLY 0 //No automatic white channel calculation. Manual white channel slider -#define RGBW_MODE_AUTO_BRIGHTER 1 //New algorithm. Adds as much white as the darkest RGBW channel -#define RGBW_MODE_AUTO_ACCURATE 2 //New algorithm. Adds as much white as the darkest RGBW channel and subtracts this amount from each RGB channel -#define RGBW_MODE_DUAL 3 //Manual slider + auto calculation. Automatically calculates only if manual slider is set to off (0) -#define RGBW_MODE_LEGACY 4 //Old floating algorithm. Too slow for realtime and palette support +#define RGBW_MODE_MANUAL_ONLY 0 // No automatic white channel calculation. Manual white channel slider +#define RGBW_MODE_AUTO_BRIGHTER 1 // New algorithm. Adds as much white as the darkest RGBW channel +#define RGBW_MODE_AUTO_ACCURATE 2 // New algorithm. Adds as much white as the darkest RGBW channel and subtracts this amount from each RGB channel +#define RGBW_MODE_DUAL 3 // Manual slider + auto calculation. Automatically calculates only if manual slider is set to off (0) +#define RGBW_MODE_MAX 4 // Sets white to the value of the brightest RGB channel (good for white-only LEDs without any RGB) +//#define RGBW_MODE_LEGACY 4 // Old floating algorithm. Too slow for realtime and palette support (unused) +#define AW_GLOBAL_DISABLED 255 // Global auto white mode override disabled. Per-bus setting is used //realtime modes #define REALTIME_MODE_INACTIVE 0 @@ -163,6 +178,7 @@ #define REALTIME_MODE_ARTNET 6 #define REALTIME_MODE_TPM2NET 7 #define REALTIME_MODE_DDP 8 +#define REALTIME_MODE_DMX 9 //realtime override modes #define REALTIME_OVERRIDE_NONE 0 @@ -197,7 +213,9 @@ #define TYPE_NONE 0 //light is not configured #define TYPE_RESERVED 1 //unused. Might indicate a "virtual" light //Digital types (data pin only) (16-31) -#define TYPE_WS2812_1CH 20 //white-only chips +#define TYPE_WS2812_1CH 18 //white-only chips (1 channel per IC) (unused) +#define TYPE_WS2812_1CH_X3 19 //white-only chips (3 channels per IC) +#define TYPE_WS2812_2CH_X3 20 //CCT chips (1st IC controls WW + CW of 1st zone and CW of 2nd zone, 2nd IC controls WW of 2nd zone and WW + CW of 3rd zone) #define TYPE_WS2812_WWA 21 //amber + warm + cold white #define TYPE_WS2812_RGB 22 #define TYPE_GS8608 23 //same driver as WS2812, but will require signal 2x per second (else displays test pattern) @@ -294,6 +312,11 @@ //Playlist option byte #define PL_OPTION_SHUFFLE 0x01 +// Segment capability byte +#define SEG_CAPABILITY_RGB 0x01 +#define SEG_CAPABILITY_W 0x02 +#define SEG_CAPABILITY_CCT 0x04 + // WLED Error modes #define ERR_NONE 0 // All good :) #define ERR_EEP_COMMIT 2 // Could not commit to EEPROM (wrong flash layout?) @@ -342,11 +365,11 @@ #define MAX_LEDS_PER_BUS 2048 // may not be enough for fast LEDs (i.e. APA102) #endif -// string temp buffer (now stored in stack locally) +// string temp buffer (now stored in stack locally) // WLEDMM ...which is actually not the greatest design choice on ESP32 #ifdef ESP8266 #define SETTINGS_STACK_BUF_SIZE 2048 #else -#define SETTINGS_STACK_BUF_SIZE 3096 +#define SETTINGS_STACK_BUF_SIZE 3712 // WLEDMM added 512 bytes of margin (was 3096) #endif #ifdef WLED_USE_ETHERNET @@ -387,8 +410,8 @@ #define JSON_BUFFER_SIZE 24576 #endif -//#define MIN_HEAP_SIZE (MAX_LED_MEMORY+2048) -#define MIN_HEAP_SIZE (8192) +//#define MIN_HEAP_SIZE (8k for AsyncWebServer) +#define MIN_HEAP_SIZE 8192 // Maximum size of node map (list of other WLED instances) #ifdef ESP8266 diff --git a/wled00/data/index.css b/wled00/data/index.css index 6a9865f7..d12942b6 100644 --- a/wled00/data/index.css +++ b/wled00/data/index.css @@ -392,12 +392,16 @@ button { } .slider { - background-color: var(--c-2); max-width: 300px; - min-width: 280px; + min-width: 260px; margin: 0 auto; /* add 5px; if you want some vertical space but looks ugly */ border-radius: 24px; position: relative; + padding-bottom: 2px; +} + +#sliders .slider, #info .slider { + background-color: var(--c-2); } .filter, .option { @@ -425,14 +429,14 @@ button { box-shadow: 4px 4px 10px 4px var(--c-1); color: var(--c-f); text-align: center; - padding: 5px 10px; + padding: 4px 8px; border-radius: 6px; /* Position the tooltip text */ width: 160px; position: absolute; z-index: 1; - bottom: 100%; + bottom: 80%; left: 50%; margin-left: -92px; @@ -647,7 +651,7 @@ img { #wbal .sliderdisplay { background: linear-gradient(90deg, #ff8f1f 0%, #fff 50%, #cbdbff); } /* wrapper divs hidden by default */ -#rgbwrap, #kwrap, #wwrap, #wbal, #qcs-w, #hexw { +#rgbwrap, #swrap, #hwrap, #kwrap, #wwrap, #wbal, #qcs-w, #hexw { display: none; } @@ -731,9 +735,13 @@ input[type=range]::-moz-range-thumb { #Colors .sliderwrap { width: 260px; - margin: 10px 0 0; + margin: 4px 0 0; } +/* #Colors { + padding-top: 18px; +} + */ /* Dynamically hide brightness slider label */ .hd { display: var(--bhd); @@ -744,13 +752,14 @@ input[type=range]::-moz-range-thumb { margin-top: var(--bmt); } -#picker, #rgbwrap, #kwrap, #wwrap, #wbal, #vwrap, #qcs-w, #hexw, #pall, #ledmap { +#picker, #qcs-w, #hexw, #pall, #ledmap { margin: 0 auto; width: 260px; + /*background-color: unset;*/ } #picker { - margin-top: 10px; + margin-top: -10px !important; } /* buttons */ @@ -1189,7 +1198,7 @@ TD .checkmark, TD .radiomark { } .bp { - margin-bottom: 5px; + margin-bottom: 8px; } /* segment & preset wrapper */ @@ -1409,6 +1418,9 @@ TD .checkmark, TD .radiomark { .expanded { display: inline-block !important; } +.expanded .segin.hide, .expanded .presin.hide, .expanded .sbs.hide { + display: none !important; +} .m6 { margin: 6px 0; @@ -1505,7 +1517,7 @@ TD .checkmark, TD .radiomark { } } -@media all and (max-width: 1249px) { +@media all and (max-width: 1024px) { #buttonPcm { display: none; } diff --git a/wled00/data/index.htm b/wled00/data/index.htm index 930e084f..ebf0d711 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -6,6 +6,7 @@ + WLED - + @@ -65,7 +66,7 @@ - + @@ -88,94 +89,102 @@
-
-
- -
- -
-

-
-
- -
- -
-

-
-
- -
- -
-

-
-
- -
- -
-
-
-
-

RGB color

-
+
+
+
+
- + +
+
+ Hue +
+
+
+ +
+
+ Saturation +
+
+
+
+ Value/Brightness
-
+
- +
+ Kelvin/Temperature
-
-
- -
+
+ +
+
+ +
+
+ Red channel +
+
+
+ +
+
+ Green channel +
+
+
+ +
+
+ Blue channel
-
-
-

White channel

-
- -
+
+ +
+ +
+
+ White channel
-
-
-

White balance

-
- -
+
+ +
+ +
+
+ White balance
-
-
-
-
-
-
-
-

-
-
-
-
-
R
-
-
- - - -
-

-
- - - -
+
+
+
+
+
+
+

+
+
+
+
+
R
+
+
+ + + +
+

+
+ + + +
+

Color palette

@@ -198,6 +207,12 @@
+
+ +
+
+
+

Effect mode

@@ -303,15 +318,22 @@
+
+
+
+
+
+

Segments

Loading...
+
-

Transition:  s

+

Transition:  s

@@ -353,12 +375,13 @@ - +

- Made with ❤︎ by Aircoookie and the WLED community + WLED made with ❤︎ by Aircoookie and the WLED community
+ WLED MM made with ❤︎ by Softhack007 & Ewowi and the WLED 2D & Audio Dev community
- + @@ -390,6 +413,6 @@

- + diff --git a/wled00/data/index.js b/wled00/data/index.js index 5dcace93..8a3b164a 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -4,6 +4,7 @@ var isOn = false, nlA = false, isLv = false, isInfo = false, isNodes = false, sy var hasWhite = false, hasRGB = false, hasCCT = false; var nlDur = 60, nlTar = 0; var nlMode = false; +var segLmax = 0; // size (in pixels) of largest selected segment var selectedFx = 0; var selectedPal = 0; var csel = 0; // selected color slot (0-2) @@ -36,6 +37,9 @@ var hol = [ [0,6,4,1,"https://initiate.alphacoders.com/download/wallpaper/516792/images/jpg/510921363292536"], // 4th of July [0,0,1,1,"https://initiate.alphacoders.com/download/wallpaper/1198800/images/jpg/2522807481585600"] // new year ]; +var ctx = null; // WLEDMM +var ledmapNr = -1; //WLEDMM +var ledmapFileNames = []; //WLEDMM function handleVisibilityChange() {if (!d.hidden && new Date () - lastUpdate > 3000) requestJson();} function sCol(na, col) {d.documentElement.style.setProperty(na, col);} @@ -60,13 +64,10 @@ function setCSL(cs) let w = cs.dataset.w ? parseInt(cs.dataset.w) : 0; let hasShadow = getComputedStyle(cs).textShadow !== "none"; if (hasRGB && !isRgbBlack(cs.dataset)) { - cs.style.backgroundColor = rgbStr(cs.dataset); if (!hasShadow) cs.style.color = rgbBri(cs.dataset) > 127 ? "#000":"#fff"; // if text has no CSS "shadow" - if (hasWhite && w > 0) { - cs.style.background = `linear-gradient(180deg, ${rgbStr(cs.dataset)} 30%, rgb(${w},${w},${w}))`; - } + cs.style.background = (hasWhite && w > 0) ? `linear-gradient(180deg, ${rgbStr(cs.dataset)} 30%, rgb(${w},${w},${w}))` : rgbStr(cs.dataset); } else { - if (!hasWhite) w = 0; + if (hasRGB && !hasWhite) w = 0; cs.style.background = `rgb(${w},${w},${w})`; if (!hasShadow) cs.style.color = w > 127 ? "#000":"#fff"; } @@ -539,7 +540,7 @@ function populateQL() { var cn = ""; if (pQL.length > 0) { - pQL.sort((a,b) => (a[0]>b[0])); + pQL.sort((a,b) => (a[1]>b[1])); //WLEDMM do not sort on preset id but on ql name cn += `

Quick load

`; for (var key of (pQL||[])) { cn += ``; @@ -570,7 +571,8 @@ function populatePresets(fromls) cn += `
`; if (cfg.comp.pid) cn += `
${i}
`; - cn += `
${isPlaylist(i)?"":""}${pName(i)} + //WLEDMM: show ql if defined + cn += `
${isPlaylist(i)?"":""}${(pJson[i].ql?pJson[i].ql+' ':'') + pName(i)}
@@ -609,7 +611,9 @@ function parseInfo(i) { mw = i.leds.matrix ? i.leds.matrix.w : 0; mh = i.leds.matrix ? i.leds.matrix.h : 0; isM = mw>0 && mh>0; - if (!isM) { + if (isM) { + gId('buttonSr').className = "active"; isLv = true; //WLEDMM: on after load + } else { gId("filter1D").classList.add('hide'); //gId("filter2D").classList.add('hide'); hideModes("2D"); @@ -641,8 +645,10 @@ function populateInfo(i) { var cn=""; var heap = i.freeheap/1000; - heap = heap.toFixed(1); - var theap = (i.totalheap>0)?i.totalheap/1000:-1; theap = theap.toFixed(1); //WLEDMM - total heap is not available on 8266 + var heap = Math.round(i.freeheap/100)/10; // WLEDMM bugfix + var theap = (i.totalheap>0)?i.totalheap/1000:-1; //WLEDMM - total heap is not available on 8266 + var flashsize = i.getflash/1000; //WLEDMM and Athom + flashsize = flashsize.toFixed(1); //WLEDMM and Athom var pwr = i.leds.pwr; var pwru = "Not calculated"; if (pwr > 1000) {pwr /= 1000; pwr = pwr.toFixed((pwr > 10) ? 0 : 1); pwru = pwr + " A";} @@ -662,33 +668,40 @@ function populateInfo(i) if (i.cn) vcn = i.cn; //WLEDMM: add total heap and total PSRAM, and build number, add bin name - if (i.ver.includes("0.14.1.")) vcn = "Sitting Ducks"; // easter egg - if (i.ver.includes("0.14.0.")) vcn = "Lupo"; // check for MM versioning scheme + if (i.ver.includes("0.14.1")) vcn = "Sitting Ducks"; // easter egg + if (i.ver.includes("0.14.0")) vcn = "Lupo"; // check for MM versioning scheme + if (i.ver.includes("0.14.0-b2.2")) vcn = "Sitting Ducks"; // early easter egg + if (i.ver.includes("0.14.0-b15.22")) vcn = "Lupo"; cn += `v${i.ver}  "${vcn}"

(WLEDMM_${i.ver} ${i.rel}.bin)

build ${i.vid}

${urows} ${urows===""?'':''} -${i.opt&0x100?inforow("Debug",""):''} +${i.opt&0x100?inforow("Net Print ☾",""):''} +${i.serialOnline?inforow(i.serialOnline,"TX="+i.sTX,"; RX="+i.sRX):""} +${i.opt&0x100?'':''} ${inforow("Build",i.vid)} -${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")} -${inforow("Uptime",getRuntimeStr(i.uptime))} ${inforow("Estimated current",pwru)} ${inforow("Average FPS",i.leds.fps)} +${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")} +${inforow("Uptime",getRuntimeStr(i.uptime))} -${inforow("MAC address",i.mac)} ${inforow("Filesystem",i.fs.u + "/" + i.fs.t + " kB (" +Math.round(i.fs.u*100/i.fs.t) + "%)")} +${theap>0?inforow("Heap ☾",((i.totalheap-i.freeheap)/1000).toFixed(0)+"/"+theap.toFixed(0)+" kB"," ("+Math.round((i.totalheap-i.freeheap)/(10*theap))+"%)"):""} +${i.minfreeheap?inforow("Max used heap ☾",((i.totalheap-i.minfreeheap)/1000).toFixed(1)+" kB"," ("+Math.round((i.totalheap-i.minfreeheap)/(10*theap))+"%)"):""} +${inforow("Free heap",heap," kB")} +${inforow("Flash Size ☾",flashsize," kB")} +${i.tpram?inforow("PSRAM ☾",(i.tpram/1024).toFixed(1)," kB"):""} +${i.psram?((i.tpram-i.psram)>16383?inforow("Used PSRAM ☾",((i.tpram-i.psram)/1024).toFixed(1)," kB"):inforow("Used PSRAM ☾",(i.tpram-i.psram)," B")):""} +${i.psusedram?((i.tpram-i.psusedram)>16383?inforow("Max used PSRAM ☾",((i.tpram-i.psusedram)/1024).toFixed(1)," kB"):inforow("Max used PSRAM ☾",(i.tpram-i.psusedram)," B")):""} +${i.psram?inforow("Free PSRAM",(i.psram/1024).toFixed(1)," kB"):""} +${inforow("MAC address",i.mac)} ${inforow("Environment",i.arch + " " + i.core + " (" + i.lwip + ")")} -${theap>0?inforow("Total heap",theap," kB"):""} -${theap>0?inforow("used heap",((i.totalheap-i.freeheap)/1000).toFixed(1)," kB"):inforow("Free heap",heap," kB")} -${i.minfreeheap?inforow("Max used heap",((i.totalheap-i.minfreeheap)/1000).toFixed(1)," kB"):""} -${i.tpram?inforow("Total PSRAM",(i.tpram/1024).toFixed(1)," kB"):""} -${i.psram?((i.tpram-i.psram)>16383?inforow("Used PSRAM",((i.tpram-i.psram)/1024).toFixed(1)," kB"):inforow("Used PSRAM",(i.tpram-i.psram)," B")):""} -${i.psusedram?((i.tpram-i.psusedram)>16383?inforow("Max Used PSRAM",((i.tpram-i.psusedram)/1024).toFixed(1)," kB"):inforow("Max Used PSRAM",(i.tpram-i.psusedram)," B")):""} -${i.e32model?inforow(i.e32model,i.e32cores +" core(s)"," "+i.e32speed+" Mhz"):""} -${i.e32flash?inforow("Flash "+i.e32flash+" MB"+", mode "+i.e32flashmode+i.e32flashtext,i.e32flashspeed," Mhz"):""} -${i.e32core0code?inforow("Core0 rst reason",i.e32core0code, " "+i.e32core0text):""} -${i.e32core1code?inforow("Core1 rst reason",i.e32core1code, " "+i.e32core1text):""} +${i.e32model?inforow(i.e32model + " ☾",i.e32cores +" core(s)"," "+i.e32speed+" Mhz"):""} +${i.e32flash?inforow("Flash "+i.e32flash+"MB"+" mode "+i.e32flashmode+i.e32flashtext + " ☾",i.e32flashspeed," Mhz"):""} +${i.e32code?inforow("Last ESP Restart ☾",i.e32code+" "+i.e32text):""} +${i.e32core0code?inforow("Core0 rst reason ☾",i.e32core0code, " "+i.e32core0text):""} +${i.e32core1code?inforow("Core1 rst reason ☾",i.e32core1code, " "+i.e32core1text):""}




`; gId('kv').innerHTML = cn; @@ -705,6 +718,9 @@ function populateSegments(s) let li = lastinfo; segCount = 0; lowestUnused = 0; lSeg = 0; + ledmapNr = s.ledmap; //WLEDMM + ledmapFileNames = []; //WLEDMM + for (var inst of (s.seg||[])) { segCount++; @@ -715,6 +731,8 @@ function populateSegments(s) let sg = gId(`seg${i}`); let exp = sg ? (sg.classList.contains('expanded') || (i===0 && cfg.comp.segexp)) : false; + ledmapFileNames.push((inst.n?inst.n:"default") + ".json"); //WLEDMM + let segp = `
@@ -729,7 +747,7 @@ function populateSegments(s) let rvXck = ``; let miXck = ``; let rvYck = "", miYck =""; - if (isM) { + if (isM && staXReverse`; miYck = ``; } @@ -740,9 +758,9 @@ function populateSegments(s) - - - + + +
`; let sndSim = `
Sound sim
@@ -753,9 +771,9 @@ function populateSegments(s)
`; - //WLEDMM Custom Effects + //WLEDMM ARTIFX let fxName = eJson.find((o)=>{return o.id==selectedFx}).name; - let cusEff = `
`; + let cusEff = `
`; cn += `