Compare commits

...

303 Commits

Author SHA1 Message Date
cschwinne
7483d3b229 Fixed settings page broken by using "%" in input fields (fixes #1516 ) 2021-06-30 18:21:56 +02:00
cschwinne
8b6cc708e7 Fixed a problem with disabled buttons reverting to pin 0 causing conflict 2021-06-30 12:33:51 +02:00
cschwinne
200960899e Removed preset cycle 2021-06-30 01:48:38 +02:00
cschwinne
599a456c81 Version bump to 0.13.0-b0 "Toki"
-   Added playlist editor UI
-   Reordered segment UI and added offset field
-   Raised maximum MQTT password length to 64 (closes #1373)
2021-06-30 01:23:35 +02:00
Christian Schwinne
4b46502d22 Playlist UI (#2046)
* Test 1

* State 2

* Playlist UI progress

* Playlist saving

* Playlist saving

* Playlist object array

* Added Offset to segment options

* Positioning

* Playlist UI complete
2021-06-30 01:01:15 +02:00
cschwinne
7233c55428 Segment Offset 2021-06-30 00:45:36 +02:00
Christian Schwinne
a58c5cce78 Merge pull request #1967 from poelzi/max_segments
Allow to override the max segments setting when compiling
2021-06-29 23:43:20 +02:00
Christian Schwinne
0b23bf65b3 Merge pull request #2045 from blazoncek/better-num-handling
Better number handling in Usermod settings.
2021-06-29 23:39:33 +02:00
Blaz Kristan
bc0a3f8a47 Better number handling in Usermod settings. 2021-06-29 18:36:55 +02:00
Christian Schwinne
9b2a0102be Merge pull request #2043 from blazoncek/v2-usermod-settings
Usermod settings v2
2021-06-27 22:22:32 +02:00
Aircoookie
04b4ef6d85 Regenerate html_settings.h 2021-06-27 22:19:03 +02:00
Blaz Kristan
9e8aadb750 Usermod settings v2
- added POST field parsing
- simpler handling in readFromConfig()
2021-06-27 15:32:33 +02:00
cschwinne
0ae0f40628 Fixed four line display usermod compilation
Thanks @Serg74!
2021-06-25 23:42:53 +02:00
cschwinne
af9aa7d201 Do not compile in example usermod 2021-06-25 12:15:23 +02:00
cschwinne
4cd3a614de Fixed preset only disabling on second effect/color change (fixes #2038 ) 2021-06-25 10:12:21 +02:00
cschwinne
1e5420e6a7 readFromConfig() update 2021-06-25 01:26:15 +02:00
cschwinne
660de0b4e5 Auto-create segments based on configured busses 2021-06-24 02:29:14 +02:00
cschwinne
b73aaecd22 Fix 4MB 1MB FS partition table 2021-06-21 13:22:14 +02:00
cschwinne
c831d62bc3 Added 2 Ethernet boards and split Ethernet configs into separate file 2021-06-20 00:14:09 +02:00
cschwinne
1539e703e9 Use sprintf_P for color array generation 2021-06-19 11:13:05 +02:00
Christian Schwinne
f43bf03768 Merge pull request #2031 from DickSwart/dickdev
made sn_photorisistor configarable
2021-06-18 19:30:43 +02:00
cschwinne
495f7f190f Fixed DOS on Chrome tab restore causing reboot 2021-06-18 13:50:22 +02:00
Dick Swart
16216b9eb9 made sn_photorisistor configarable 2021-06-18 13:07:41 +12:00
cschwinne
dfdb22f584 Optimized JSON buffer usage (pre-serialized color arrays) 2021-06-17 19:46:18 +02:00
Christian Schwinne
0b264176bc Merge pull request #2024 from Aircoookie/dependabot/npm_and_yarn/normalize-url-4.5.1
Bump normalize-url from 4.5.0 to 4.5.1
2021-06-15 23:20:46 +02:00
Christian Schwinne
bde70a27f0 Merge pull request #2025 from Aircoookie/dependabot/npm_and_yarn/glob-parent-5.1.2
Bump glob-parent from 5.1.1 to 5.1.2
2021-06-15 23:19:57 +02:00
cschwinne
7d2f5f0799 Fixed div by 0 when using palette on single LED segment 2021-06-15 20:12:20 +02:00
cschwinne
7610ab7a8d Update logo 2021-06-14 01:58:12 +02:00
dependabot[bot]
51db653b1a Bump glob-parent from 5.1.1 to 5.1.2
Bumps [glob-parent](https://github.com/gulpjs/glob-parent) from 5.1.1 to 5.1.2.
- [Release notes](https://github.com/gulpjs/glob-parent/releases)
- [Changelog](https://github.com/gulpjs/glob-parent/blob/main/CHANGELOG.md)
- [Commits](https://github.com/gulpjs/glob-parent/compare/v5.1.1...v5.1.2)

---
updated-dependencies:
- dependency-name: glob-parent
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-11 20:27:21 +00:00
dependabot[bot]
dc4e4395a9 Bump normalize-url from 4.5.0 to 4.5.1
Bumps [normalize-url](https://github.com/sindresorhus/normalize-url) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/sindresorhus/normalize-url/releases)
- [Commits](https://github.com/sindresorhus/normalize-url/commits)

---
updated-dependencies:
- dependency-name: normalize-url
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-11 12:49:20 +00:00
cschwinne
623694ab73 Playlist additions 2021-06-10 02:52:20 +02:00
cschwinne
374457df70 A few small fixes 2021-06-09 12:57:16 +02:00
Christian Schwinne
7885dddef2 Create FUNDING.yml 2021-06-09 12:43:29 +02:00
Christian Schwinne
73d6cc1e54 Merge pull request #2020 from blazoncek/endless-playlist-fix
Endless playlist fix.
2021-06-07 22:29:17 +02:00
Blaz Kristan
8fdf84068d Optimisations. 2021-06-07 21:05:31 +02:00
Blaž Kristan
131625bb53 Endless playlist fix. 2021-06-07 14:16:29 +02:00
Christian Schwinne
29c9e5cb17 Merge pull request #2015 from RedNax67/master
Added support for raw 16bpp RGB565 images, and added elekstube env to examples in platformio.ini
2021-06-04 13:11:23 +02:00
Xander X
52b60fd6a6 Added elekstube env to examples in platformio.ini
Added support for raw 16bpp RGB565 images. Upload /0.bin etc.
See https://github.com/neptune2/EleksTube-IPS-Retro-Nixie-Digits for
how to get the original digits.
2021-06-04 10:39:31 +02:00
Christian Schwinne
625e04d208 Merge pull request #2007 from Aircoookie/dependabot/pip/urllib3-1.26.5
Bump urllib3 from 1.26.4 to 1.26.5
2021-06-02 14:25:13 +02:00
Christian Schwinne
6da657d3e2 Merge pull request #2008 from DickSwart/dickDev
added SN_Photoresistor usermod
2021-06-02 14:24:01 +02:00
Dick Swart
344c9e9238 added SN_Photoresistor usermod 2021-06-02 12:01:06 +12:00
Christian Schwinne
89b2b066ef Merge pull request #2004 from HyperAtlas/master
CRONIXIE check in json.cpp
2021-06-02 00:25:04 +02:00
dependabot[bot]
894e084c7f Bump urllib3 from 1.26.4 to 1.26.5
Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.4 to 1.26.5.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/1.26.4...1.26.5)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-01 22:24:59 +00:00
Aircoookie
2ba064b2a5 Merge branch 'master' of https://github.com/Aircoookie/WLED 2021-06-02 00:23:12 +02:00
Aircoookie
dfe065ef82 Fixed Pushbutton inverted not selectable 2021-06-02 00:22:57 +02:00
Christian Schwinne
7bd4b78470 Merge pull request #2006 from juanesf/master
Add diyHue repo to readme.md
2021-06-02 00:17:38 +02:00
juanesf
d7991a247d Add diyHue repo to readme.md
Wled has been supported by diyHue for some time, now it also supports Hue Sync (Entertainment) under UDP in the flask branch which will be the new master branch. 

https://github.com/diyhue/diyHue/pull/628
2021-06-01 12:31:52 -04:00
SpikeyHelmet
2178fd6ee9 CRONIXIE check in json.cpp 2021-06-01 06:37:35 +05:30
Christian Schwinne
7019ddb165 Merge pull request #1997 from Aircoookie/toki
More precise NTP timekeeping
2021-05-30 13:27:35 +02:00
Christian Schwinne
9f13763637 Merge branch 'master' into toki 2021-05-30 13:22:42 +02:00
Christian Schwinne
beeba27f46 Merge pull request #1977 from blazoncek/multi-button
Multi-button support.
2021-05-30 13:16:52 +02:00
cschwinne
315d4f225a Added analog button hue control 2021-05-30 02:03:32 +02:00
cschwinne
85489458d8 Small improvements
Settings cosmetic changes (spaces, smaller pin inputs, moved analog legend to wiki)
Uncommented STATUSPIN code (still defined out by default)
Removed adalight pin 3 lock
Added define for PSRAM
2021-05-30 01:31:04 +02:00
cschwinne
bfc7f56c4d Accurate UDP sync for NTP enabled instances 2021-05-30 00:08:24 +02:00
Blaz Kristan
7685f9b73d Ignore analogue pin -1 on ESP8266 2021-05-28 17:45:14 +02:00
Blaž Kristan
664fad96fa Fixed error-prone use of shifting. 2021-05-28 14:14:50 +02:00
Christian Schwinne
669a610e36 Merge branch 'master' into multi-button 2021-05-28 10:03:27 +02:00
Blaž Kristan
7e0d9cb48c Added:
- analogue FX speed
- analogue FX intensity
- analogue palette selection
- UI legend
2021-05-28 08:47:15 +02:00
cschwinne
7cbc9d21b5 Fixed difference calculation 2021-05-27 23:29:11 +02:00
cschwinne
55b26751ae Cache CORS preflight request 2021-05-27 11:09:57 +02:00
cschwinne
c2892d7887 Add UDP sync of system time 2021-05-27 02:02:02 +02:00
cschwinne
6c8bf090fe Small optimizations 2021-05-27 00:09:52 +02:00
Blaz Kristan
13bc378069 Fixes for analog. 2021-05-25 23:59:43 +02:00
cschwinne
8431d0bd5c Replace Time with Toki 2021-05-25 09:59:19 +02:00
cschwinne
852f758be3 Subsecond accuracy NTP 2021-05-24 19:05:34 +02:00
cschwinne
b455f432d5 Toki 1st experiment 2021-05-24 14:34:03 +02:00
stevelup
306cea60a1 Update usermod_v2_rotary_encoder_ui.h (#1988)
Typo DISLAY -vs- DISPLAY
2021-05-23 21:22:15 +02:00
stevelup
ba2e07c4b9 Update readme.md (#1987)
Typo DISLAY -vs- DISPLAY
2021-05-23 21:22:01 +02:00
cschwinne
9b796531b2 Fixed Sunrise calculation (atan_t approx. used outside of value range) 2021-05-23 18:49:23 +02:00
cschwinne
08d7a1c123 Set Nixie contents via JSON API
Elekstube mod support a subset of the cronixie display patterns
2021-05-21 22:23:12 +02:00
Christian Schwinne
1f70a735c7 Merge pull request #1980 from stevelup/patch-1
Update platformio_override.ini.sample
2021-05-21 20:38:00 +02:00
stevelup
6713fcfeb1 Update platformio_override.ini.sample
Typo on lines 12 and 31 (DISLAY -vs- DISPLAY)
2021-05-21 19:01:42 +01:00
Christian Schwinne
c3107d213a Merge pull request #1979 from Aircoookie/elekstube_usermod
Elekstube IPS and RTC usermods
2021-05-21 15:34:14 +02:00
cschwinne
adf5c8c278 Center smaller images 2021-05-21 15:19:18 +02:00
Blaž Kristan
5f86a8a15b Merge branch 'master' into multi-button 2021-05-21 13:59:33 +02:00
Blaž Kristan
042c756be8 Added analog button support.
Inverted switch fix.
2021-05-21 13:33:22 +02:00
cschwinne
2d586406da First working Elekstube IPS and RTC mod 2021-05-21 10:10:29 +02:00
cschwinne
371c4e0051 Fixed WS281x output on ESP32
Fixed potential out-of-bounds write in MQTT
Fixed IR pin not changeable if IR disabled
Fixed XML API <wv> containing -1 on Manual only RGBW mode (see #888, #1783)
2021-05-20 21:41:39 +02:00
Blaz Kristan
69099fcdd7 Multi button support. 2021-05-20 19:54:07 +02:00
Daniel Poelzleithner
57e50d0c33 Allow to override the max segments setting when compiling 2021-05-20 11:46:12 +02:00
Blaž Kristan
1617658bfe Per strip skip first LED implementation (#1871)
* Per strip "skip first LED".
Moved skip first led into bus manager.

* Update cfg.cpp

* Do not display Skip 1st on analog types

Rename skipFirstLed() to clarify what it does
Remove RGBW override

Co-authored-by: Aircoookie <dev.aircoookie@gmail.com>
Co-authored-by: Aircoookie <cschwinne@gmail.com>
2021-05-17 16:23:46 +02:00
Christian Schwinne
4bcfff780a Merge pull request #1965 from blazoncek/usermod-update
Updated usermod readme and usability enhancements.
2021-05-17 13:05:11 +02:00
Blaž Kristan
12f9ad8f7f Updated usermod readme and usability enhancements. 2021-05-17 12:52:27 +02:00
cschwinne
6f843fcb27 Added experimental {"on":"t"} (resolves #1952 ) 2021-05-17 12:29:30 +02:00
cschwinne
e0f17e1778 Always copy MQTT payload 2021-05-17 10:38:07 +02:00
cschwinne
bfb27c49a2 Fixed possibility of non-0-terminated MQTT payloads 2021-05-13 01:04:33 +02:00
cschwinne
cb7b7f1dca Add notice if no usermod installed 2021-05-11 23:21:57 +02:00
cschwinne
5ca8bc3f2a Fixed various Codacy code style and logic issues 2021-05-11 14:54:03 +02:00
Aircoookie
1ccc8eec0a Merge pull request #1922 from esev/tm1814
TM1814 requires refreshes to remain powered off and a slightly faster refresh speed
2021-05-11 12:10:52 +02:00
Aircoookie
9c5afda83a Merge branch 'master' into tm1814 2021-05-11 11:59:35 +02:00
cschwinne
d94d3d4bc5 Added experimental /json/cfg endpoint for changing settings from JSON 2021-05-11 01:11:16 +02:00
Aircoookie
119826cb9b Merge pull request #1951 from blazoncek/user-configurable-usermods
User configurable usermods.
2021-05-09 23:10:37 +02:00
cschwinne
6ab95ed4ef Remove Usermod inheritance from UsermodManager 2021-05-09 22:54:04 +02:00
cschwinne
4f1eb64ac6 Added changes to set.cpp and wled_server.cpp 2021-05-08 18:04:44 +02:00
Blaz Kristan
3f8dc76f84 Added missing config info. 2021-05-08 12:13:39 +02:00
cschwinne
f60579fd21 Remove outdated Visual Studio files (please make PR to re-add if still required) 2021-05-07 16:51:41 +02:00
Blaz Kristan
136a00a301 User configurable usermods.
- Utilizing addToConfig() & readFromConfig()
- UM configuration Settings page
- MQTT support for usermods
- A few sample usermods including rewritten Temparature and new Multi-Relay
2021-05-07 12:41:39 +02:00
cschwinne
fa075f6800 Revert TV reversion 2021-05-07 11:42:12 +02:00
cschwinne
277f395595 Merge branch 'master' of https://github.com/Aircoookie/WLED 2021-05-07 11:32:13 +02:00
cschwinne
e2061464a5 Fixed not turning on after pressing "Off" on IR remote twice (fixes #1950)
Fixed OTA update file selection from Android app (TODO: file type verification in JS, since android can't deal with accept='.bin' attribute)
2021-05-07 11:32:08 +02:00
Aircoookie
fcf5cd4655 Merge pull request #1946 from jaredgisin/br-remove-unused-configs
remove unused flags
2021-05-05 10:04:45 +02:00
Jared Gisin
3816f0b68b remove unused flags 2021-05-04 18:45:05 -07:00
Aircoookie
1a2543ddde Added FPS test utility 2021-04-30 00:00:46 +02:00
Eric Severance
7c9db7edeb Add comment about the TYWE3S controller (#1924)
The `codm-controller-0.6` env also works for the [`TYWE3S` controller](https://github.com/Aircoookie/WLED/issues/1519#issuecomment-826126116). This PR just adds a note so that folks who search for `TYWE3S` will find the proper env to use.
2021-04-27 09:05:40 +02:00
Eric Severance
8b759bc5d9 TM1814 requires refreshes to remain powered off and a slightly faster refresh speed. 2021-04-25 09:08:02 -07:00
acshef
9a0aac4745 Add IP Address to JsonInfo (#1912)
* Add IP Address to JsonInfo

The value is added to the JSON only if the device is connected to the network, and uses the JSON key `"sip"` to match [wled00/xml.cpp](wled00/xml.cpp#L249). The overarching goal of this is to expose the IP Address to the Home Assistant WLED Integration, so that Home Assistant can provide a link to the WLED device (either directly through the Integration/Device page 🤞 or *ad hoc* in Lovelace).

* IP in JSON info

Empty string if not connected

Co-authored-by: Aircoookie <cschwinne@gmail.com>
2021-04-25 01:37:16 +02:00
cschwinne
ced0cc1bac 0.12.1-b1 2021-04-22 01:12:31 +02:00
cschwinne
3c49f22266 Merge branch 'master' of https://github.com/Aircoookie/WLED 2021-04-22 00:47:58 +02:00
cschwinne
13ae99edec Reduce TV simulator flash usage 2021-04-22 00:47:50 +02:00
Andy Shinn
0f82730a78 Release engineering improvements (#1844)
* version set from package.json and release bin names

* support direnv virtualenv

* versioned PlatformIO environment

* matrix support for parralel CI

* gather artifacts

* release on tagging

* minor scripts formatting
2021-04-22 00:41:36 +02:00
cschwinne
ff083daf31 Merge branch 'master' of https://github.com/Aircoookie/WLED 2021-04-21 17:22:00 +02:00
cschwinne
7f6a554e1b Small adjustments 2021-04-21 17:21:55 +02:00
Eric Severance
eb99271120 Use 22.5 for the TM1814 max current (#1905)
* Configure TM1814 max current

* Use 22.5 mA as this seems to be a common value for the LEDs
2021-04-20 21:36:52 +02:00
Eric Severance
13e5c695c3 Initialize PixelSettings for TM1814 strips (#1847)
* Better handling for TM1814 strips

* Call SetPixelSettings after Begin as described on the NeoPixelBus wiki

* Use NeoTm1814Settings::MaxCurrent constant
2021-04-18 19:14:12 +02:00
Def3nder
12de47c923 Fix TV-Simmulator Effect flash usage (#1621)
* Add alternative TV-Sim version without tv_colors.h

...and safe 18k flash this way

* ...remove the define
2021-04-16 10:22:22 +02:00
cschwinne
afde7940d8 NUM_STRIPS no longer required with compile-time strip defaults 2021-04-16 01:01:24 +02:00
cschwinne
01dd41bdbf Added ability to add multiple busses as compile time defaults using the esp32_multistrip usermod define syntax 2021-04-15 10:55:22 +02:00
cschwinne
f3b84f1365 Switch trigonometric implementation, saves 460b memory 2021-04-14 16:49:47 +02:00
cschwinne
5751d5c1b0 Fixed Button, IR, Relay pin not assigned by default (resolves #1891)
Fixed instance discovery not working if MQTT not compiled in
2021-04-14 01:16:32 +02:00
Aircoookie
3d2336aac1 Merge pull request #1890 from blazoncek/holiday-list
Easier holiday/special events handling for background images.
2021-04-14 00:56:41 +02:00
cschwinne
afe5f19464 Update holiday BG durations
Fix semicolons
2021-04-14 00:44:07 +02:00
Aircoookie
4091a3c238 Merge pull request #1889 from blazoncek/sunrise-sunset-012
Sunrise and sunset calculation and sunrise/sunset triggered presets.
2021-04-13 23:41:03 +02:00
Blaz Kristan
f411e07fb4 Easier holiday/special events handling for background images. 2021-04-12 23:00:22 +02:00
Blaz Kristan
9bfe27dd5e Sunrise and sunset calculation and sunrise/sunset triggered presets. 2021-04-12 21:53:22 +02:00
Aircoookie
c4201d9a2a Merge pull request #1886 from betaphi/BME280_Patch
Usermod BME280_v2 refinement
2021-04-12 11:07:22 +02:00
Bastian Rössler
58e9817a6d Add PublishAlways to BME280_v2 Usermod 2021-04-12 10:31:59 +02:00
cschwinne
48d5584491 Add switch support 2021-04-12 00:45:33 +02:00
Aircoookie
5786f1d057 Merge pull request #1863 from fishbone-git/running_dual
Running dual
2021-04-11 00:59:56 +02:00
cschwinne
87c6f3c757 Add gap for Running Dual 2021-04-11 00:50:14 +02:00
cschwinne
0e99c948d6 Merge branch 'master' of https://github.com/Aircoookie/WLED 2021-04-10 12:22:12 +02:00
cschwinne
0f5e0f640b Updated codm environments
Added custom 8MB ESP32 partition csv
2021-04-10 12:22:09 +02:00
Aircoookie
3d2c6388de Merge pull request #1875 from zoide/patch-1
updated bottom/top sensor
2021-04-10 12:19:49 +02:00
Udo Waechter
ad8e614ae8 updated bottom/top sensor
bottomsensor and topsensor were incorrect: bottom-sensor / top-sensor according to API
2021-04-09 17:50:26 +02:00
Aircoookie
48c0360877 Merge pull request #1850 from eg321/VL53L0X-gestures
Support of simple gestures for VL53L0X laser sensor
2021-04-08 15:31:37 +02:00
Aircoookie
d230be3e1c Merge pull request #1851 from kolcun/patch-1
Increase buffer size
2021-04-07 01:15:42 +02:00
Aircoookie
daa77d40a3 Merge pull request #1862 from fishbone-git/drip
Drip: use constrain to keep in segment limits
2021-04-07 00:59:38 +02:00
Aircoookie
6ae743684f Merge pull request #1864 from blazoncek/ledmap-multisegment-fix
Ledmap multisegment fix.
2021-04-06 12:03:45 +02:00
cschwinne
789c00dde1 Change indexSet to signed 32 bit 2021-04-06 11:42:21 +02:00
Blaž Kristan
90da471084 Fix for mirrored segments. 2021-04-06 07:48:12 +02:00
Blaž Kristan
2d55056015 Merge branch 'master' into ledmap-multisegment-fix 2021-04-05 21:19:19 +02:00
Blaz Kristan
ffab9bb893 Ledmap multisegment fix. 2021-04-05 21:14:36 +02:00
fishbone-git
1192d04391 sync and merge master 2021-04-05 20:49:31 +02:00
fishbone-git
f18dced2f3 use constrain to keep in segment limits 2021-04-05 16:08:35 +02:00
cschwinne
ecdc0a3800 Fixed ESP32 crash on Drip effect with reversed segment (#1854) 2021-04-03 17:18:29 +02:00
cschwinne
46d66dded8 Release of WLED v0.12.0 2021-04-03 00:15:28 +02:00
Mike Kolcun
a69dcfc49d Increase buffer size
Avoid buffer overflows with longer mqtt topics
mqtDeviceTopic in wled.h is defined with a size of 33, so this should be set to 45 to accommodate the additional 12 characters - /temperature
2021-04-02 14:04:27 -04:00
Egor Chernodarov
601005f837 Support of simple gestures for VL53L0X laser sensor 2021-04-02 23:04:10 +07:00
cschwinne
de6f32e486 Merge branch 'master' of https://github.com/Aircoookie/WLED 2021-04-02 16:52:57 +02:00
cschwinne
fbe866198d Fixed iOS LED settings 2021-04-02 16:52:53 +02:00
Aircoookie
d603a8a9be Merge pull request #1840 from brammittendorff/patch-1
Updated readme.md
2021-03-30 22:55:50 +02:00
Bram Mittendorff
8f92077454 Updated readme.md
Updated readme.md because currenly the DallasTemperature and Onewire are under `lib_deps`.
2021-03-30 11:45:48 +02:00
cschwinne
8755a9bcda 0.12.0 Beta 5 (fixes #1837) 2021-03-30 02:04:02 +02:00
Aircoookie
4a1ecc7b72 Merge pull request #1836 from Aircoookie/blackmagic
Defeat the black magic
2021-03-29 02:53:37 +02:00
cschwinne
e46078286d 0.12.0 Beta 4 2021-03-29 02:28:34 +02:00
cschwinne
806221bc3f Move RGBW activation to wled.cpp 2021-03-28 17:31:05 +02:00
cschwinne
4264c2a173 Test for arcane curses 2021-03-25 20:00:31 +01:00
cschwinne
1f4a15ee6b Fixed current estimation 2021-03-23 14:08:01 +01:00
cschwinne
bab4f9963f Bump version code (never deploy at 3am) 2021-03-23 03:15:43 +01:00
cschwinne
1dbcac4f53 Framerate bug workaround 2021-03-23 03:10:24 +01:00
cschwinne
8d20a13776 Fixed chevron icon not included 2021-03-22 00:31:01 +01:00
cschwinne
213bc75ae1 Change cache control to revalidate each time 2021-03-21 02:40:12 +01:00
cschwinne
8a2256e0d8 0.12 beta 2021-03-20 18:43:05 +01:00
Aircoookie
eee2450c9e Merge pull request #1786 from DerFetzer/fix_fx_sync
Send notification for HTTP API calls with only effects parameters
2021-03-19 09:09:17 +01:00
Aircoookie
e2242f5d99 Merge pull request #1804 from blazoncek/chunchun-fix
Slight modification of Chunchun effect.
2021-03-19 00:05:21 +01:00
Aircoookie
30df67721d Merge pull request #1807 from Aircoookie/dev
Pull dev branch into master. 0.12 release soon!
2021-03-19 00:05:02 +01:00
Christophe Gagnier
4a20f43fbf Search bar and palettes preview (#1637)
* Fix swipe behavior on firefox when clicking on palette selection input

When clicking on the palette select element on firefox, it would often swipe to the next tab due to a bug in firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=1410816

* Update UI .h files and remove console log

* Fix indentation in requestJson

* Add palette preview
Add palette and effects search

* Add palette preview
Add palette and effects search

* Add palette preview

Add palette and effects search

* Add palette preview

Add palette and effects search

* Fix error with background image

* Add missing palettes

* Add expiration to cached palette data

* Remove extra console.log

* bug fixes for palettes

* Rename "Default" effect back to "Solid"

* Fix scrolling issue when selecting an effect

* Add sticky default and sticky selected item

* Change checkboxes for radio button

* build html .h files

* Design Iteration 1

* Palette preview style changes

* Add button for clearing search field

* Use version ID for caching palette data rather than 24h expiration

Co-authored-by: Aircoookie <dev.aircoookie@gmail.com>
2021-03-18 23:59:56 +01:00
Blaz Kristan
e06d269b82 Slight modification of Chunchun effect. 2021-03-15 17:01:56 +01:00
Aircoookie
ba4c3e3852 Merge pull request #1724 from blazoncek/playlist-fix
Playlist handling.
2021-03-13 22:46:32 +01:00
cschwinne
b8de36b340 Cleanup 2021-03-13 22:46:08 +01:00
Aircoookie
bbe3e8093c Merge pull request #1782 from aledthom/master
Usermod for Artemis udp control
2021-03-13 22:05:24 +01:00
cschwinne
26096bc136 Added options for Auto Node discovery
Optimized strings (no string both F() and raw)
2021-03-13 22:04:37 +01:00
cschwinne
3460f9d9cc Auto discovery follow-up 2021-03-09 18:00:02 +01:00
Aircoookie
6eacf8ed7e Merge pull request #1683 from blazoncek/autodiscovery
Auto node discovery.
2021-03-09 17:25:17 +01:00
Aircoookie
20bebe98b1 Merge branch 'dev' into autodiscovery 2021-03-09 17:24:57 +01:00
cschwinne
91d885eae4 Fix node type 2021-03-09 17:21:19 +01:00
cschwinne
ea3358ecb2 Autodiscovery improvements 2021-03-09 16:23:19 +01:00
cschwinne
9b3e6270d5 Auto start field population in bus config 2021-03-07 00:42:21 +01:00
cschwinne
71edc3a084 Fixed incorrect over-memory indication in LED settings on ESP32 2021-03-06 02:35:49 +01:00
cschwinne
05521bfd3a Added destructor for BusPwm (fixes #1789) 2021-03-04 22:17:25 +01:00
Blaz Kristan
6c997f573a Added vrsion info to autodiscovery packet. 2021-03-04 14:34:36 +01:00
cschwinne
c0dd98b6d7 Fixed relay mode inverted when upgrading from 0.11.0 2021-03-04 12:10:18 +01:00
DerFetzer
56cf1c818b Send notification for HTTP API calls with only effects parameters 2021-03-01 22:44:28 +01:00
Blaz Kristan
5553964d52 Moved nodes from Info to separate tab. 2021-03-01 11:13:40 +01:00
Blaz Kristan
90808ac67e Power off playlist unload. 2021-02-28 15:34:18 +01:00
Aled
7aab7678e9 Usermod for Artemis udp control
enables use as a device within the artemisRGB software
2021-02-27 09:05:30 +00:00
Aircoookie
195af002cf Merge pull request #1779 from Aircoookie/fixwarnings
Fix warnings
2021-02-27 02:08:01 +01:00
cschwinne
38db8d2bfd Update platformio.ini 2021-02-27 02:01:27 +01:00
cschwinne
44a9fed8a9 Fix more warnings 2021-02-27 01:50:08 +01:00
cschwinne
cfc346abad Fix warnings misc fixes 2021-02-27 01:16:06 +01:00
cschwinne
befeb55349 Memory check on bus creation 2021-02-27 00:57:12 +01:00
Louis Beaudoin
b460d0f533 Fix Warnings (#1744)
* Remove -w (Suppress all warnings, including those which GNU CPP issues by default.) and add back in -Wall (Turn on all optional warnings which are desirable for normal code.) from build_flags

* Fixes warning: suggest parentheses around '+' in operand of '&' [-Wparentheses]

* Fixes warning: "CONFIG_LITTLEFS_FOR_IDF_3_2" redefined

* Fixes warning: format '%x' expects argument of type 'unsigned int', but argument 3 has type 'uint64_t {aka long long unsigned int}' [-Wformat=]

* Fixes warning: enumeration value 'onoff' not handled in switch [-Wswitch]

* Fixes warning: "ntohl" redefined, warning: "ntohs" redefined, warning: "htonl" redefined, warning: "htons" redefined
- Original fix: 858f8f4ee9

* Fixes warning: unused variable 'mainSeg' [-Wunused-variable]

* Fixes warning: unused variable 'start' [-Wunused-variable]

* (untested!) Fixes warning: operation on '...' may be undefined [-Wsequence-point]

* Fixes warning: unused variable

* Fixes warning: unused variable and warning: narrowing conversion

* Fixes warning: unused variable

* Fixes warning: unused variable

* (untested!) Fixes warning: statement has no effect [-Wunused-value]

* Fixes warning: control reaches end of non-void function

* Fixes warning: unused variable

* Fixes warning: left operand of comma operator has no effect

* Fixes warning: no return statement in function returning non-void

* (untested!) Fixes warning: ISO C++ forbids converting a string constant to 'char*' and fixes warning: unused variable 'nPins'

* Fixes warning: deleting array 'dmxData'

* Fixes warning: unused variable

* Remove all warning suppression buildflags

Co-authored-by: Louis Beaudoin <louis@embedded-creations.com>
Co-authored-by: Aircoookie <dev.aircoookie@gmail.com>
2021-02-27 00:20:31 +01:00
Rolf
746a8badac Animated Staircase Usermod (#1763)
* Initial version of the PIR Staircase wipe up/down.

* Add pulldown reminder

* Workaround for missing D6 and D7 definitions on esp32dev

* Add pinouts for d1 mini (esp8266 and esp32) and NodeMCU (and Arduino)

* These IO pins on all these boards... NodeMCU and d1_mini esp32 supported. The others? Fingers crossed.

* Changed to not collide with led strip pins

* PIR on GPIO 15 and 16 on esp32 d1 mini

* Removed PIN number ifdefs, it is impossible to match all boards

* Settings in flash, info and API

* Update README.md

* Small doc changes

* Improved README

* Fixed error in reading configuration

* Add API documentation

* Documentation and code cleanup

* Add enable/disable to API settings

* Restore segment state when disabling plugin

* Add debounce

* Set segments in animation mode

* Set segments in animation mode

* Add support for HR04 sensors

* Add preliminary description for  using an HR04 sensor

* Fixed typenumber and linked to datasheet

* Moved config away from defines to prevent user error

* Trigger Sensors through API

* Rename scripts folder so that it's name doesn't clash with the `pio` command (prevents platformIO in VSCode to work properly on Mac)

* Bugfix for detection problems

* Separated config from code

* Renamed Signal to Trigger pin

* Filename adjusted

* Clarifications and additions to README

* Fixed references to pio scripts

* Fixed API trigger bug

* Adjustments to README.md

* More efficient use of flash cycles, better naming

* Bugfix: bottom sensor was not read properly

* Renamed to Animated_Staircase

* Add note on ultrasonic sensor and esp32

* Better naming of defines

* Bugfix: Swipe down started with two steps.

* Removed upload port in nodemcuv2 section

Co-authored-by: Rolf <rolf@phobos.local>
Co-authored-by: Rolf <>
2021-02-25 09:52:48 +01:00
cschwinne
d7790a04c5 5 configurable pins per bus (for analog) 2021-02-24 14:49:39 +01:00
cschwinne
2c0b07387b Added QuinLED-ESP32-Ethernet type
Set 19531 as default ESP32 PWM frequency
2021-02-23 00:47:48 +01:00
Aircoookie
06f2f9adbb Merge pull request #1771 from Aircoookie/mergedev_210222
Update dev
2021-02-22 22:31:22 +01:00
Aircoookie
37d5b9109f Merge branch 'dev' into mergedev_210222 2021-02-22 22:31:12 +01:00
Aircoookie
76e269ee21 Merge pull request #1764 from k7bbr/master
Add support for WESP32 board, change default Ethernet type to none, add macro to specify default Ethernet type at compile time
2021-02-21 22:38:15 +01:00
Aircoookie
3103939197 Merge pull request #1766 from edwinm/patch-1
Use ldscript_1m128k in platformio_override.ini.sample
2021-02-21 17:23:50 +01:00
Edwin Martin
3f01c87223 Use ldscript_1m128k in platformio_override.ini.sample
The current value ldscript_1m0m doesn't exist anymore and WLED won't compile. Using ldscript_1m128k makes it compile.
2021-02-20 23:21:55 +01:00
k7bbr
e4cda4bb99 change default Ethernet board to none 2021-02-20 02:18:37 -07:00
k7bbr
f333f867c5 Updated platformio.ini to reference esp32_eth env
change default ethernet type to none
add macro option to change default eth board at compile time
2021-02-20 01:54:54 -07:00
Kevin Dorff
75c46f7a0e Create the sorted index of modes and palettes at startup (#1741)
* Very incomplete work toward sorting.

* Sort modes and palettes upon startup so I don't have to maintain the static index array.

* Remove cpp test file I used for development

* Added ModeSortUsermod, modified the other two usermods to use it.

* Update platformio_override.ini.sample and readme for ModeSortUsermod

* restore methods accidentally removed.
2021-02-18 00:28:01 +01:00
Aircoookie
5c6cb41124 Merge pull request #1742 from WJCFerguson/geometric_IR_brightness_steps
IR: change to non-linear brightness steps
2021-02-17 23:59:38 +01:00
Aircoookie
94f7c03871 Merge pull request #1760 from embedded-creations/fixWrapperEsp32Spi
bus_wrapper fixes/improvements for ESP32 SPI
2021-02-17 23:38:19 +01:00
Louis Beaudoin
96d5c03a6d bus_wrapper improvements for SPI output
- Identifying ESP32 hardware SPI by pin number is broken and flawed, replace with temporary hack instead until a better method of assigned resources can be devised
  - NeoPixelBus doesn't support HSPI, only VSPI right now, so matching HSPI pins to enable a non-existent VSPI driver is broken
  - ESP32 SPI peripherals can use alternate pins, so choosing to use hardware SPI only on the default pins is flawed
- Specify pins during Begin() call to allow for alternate pins and avoid driving the chip select signal
- Dotstar Software/Hardware output tested on ESP32, other SPI protocols and ESP8266 support was not tested
2021-02-17 17:03:57 +00:00
Louis Beaudoin
3a03bc41a7 Fix bus_wrapper.h bugs:
- missing breaks in switch(busType identifying SPI LEDs)
- set correct pin order for begin()
2021-02-17 16:58:03 +00:00
Aircoookie
331844ff73 Merge pull request #1708 from Matchlighter/patch-1
Add DNRGBW Mode for Real Time UDP Control
2021-02-16 17:43:33 +01:00
James Ferguson
2bc38e3784 IR: change to non-linear brightness steps
Prior Incrementing/decrementing by fixed steps (typically 10) means large
perceptual jumps at low brightness and small jumps at high brightness.  This
change moves to a geometric progression, closer at the bottom of the range and
widely spaced at the top.

While it could be done as a relative jump (incrementing by bri/4 and
decrementing bri/5 works quite well), it makes for irregular behavior when
moving back and forth.
2021-02-13 18:29:56 -05:00
cschwinne
d580dedfc8 Remove stray endif 2021-02-13 01:48:05 +01:00
cschwinne
aa0f4c9985 Update dev branch (resolve conflicts) 2021-02-13 01:43:16 +01:00
b3nj1
aa242d897d DHT22/DHT11 humidity/temperature sensor usermod (#1719)
* DHT22/DHT11 humidity/temperature sensor usermod

* cleanup - don't report when usermod is auto-disabled since report isn't persistent

* track error count; retry once after error occurs

* for esp32, use esp32DHT library

* fix unreliable ESP32 readings by switching DHT library to https://github.com/alwynallan/DHT_nonblocking

* change default pin to Q2; don't burst readings if error occurs

Co-authored-by: Aircoookie <dev.aircoookie@gmail.com>
2021-02-13 01:21:13 +01:00
Aircoookie
d56ab6c971 Merge pull request #1738 from blazoncek/dynamic-led-map
Dynamic LED map creation from JSON file
2021-02-13 01:07:52 +01:00
cschwinne
2f7be3475d Updated mapping comment 2021-02-13 01:02:14 +01:00
Blaz Kristan
2544d2e068 Dynamic LED map creation from JSON file /ledmap.json in format {"map":[4,3,2,1,...]}.
Used for rearranging LEDs (matrices, awkward placement, ...)
2021-02-12 11:54:35 +01:00
k7bbr
a09f64aee5 Add Ethernet support for WESP32 board 2021-02-11 02:36:15 -07:00
Aircoookie
f7114fc2aa Merge pull request #1729 from blazoncek/tetris-fx
Tetris (falling bricks) effect & Colortwinkles low brightness fix.
2021-02-10 00:42:35 +01:00
cschwinne
7092f337ef Faster Tetrix mode
- Replaced a letter in name (copyright)
- 2x speed
- Replaced Merry christmas mode
2021-02-10 00:37:05 +01:00
Kevin Dorff
8e71c3ae17 Rotary Encoder, Four Line Display, and Auto Save Usermods (#1722)
* Ability to lookup Usermod by id so Usermods can use other Usermods.

* Rotary Encoder UI using two Usermods

* Updates. More to come, probably.

* Updated rotary usermod to honor USE_FOUR_LINE_DISPLAY if you want to use four line display. It should be truly optional, now.

* minor logic improvement to showing the current time in clock mode.

* improved 24 hour display foratting and ability to use the FourLineDisplayUsermod without the RotaryEncoderUIUsermod (option disable sleep and clock modes).

* Improved ordering of defines in the FourLineDisplayUsermod to put options people might need to change together toward the top.

* relocate plugins. add mention of the Wire requirement.

* usermod filenames changed, updating comment in const.h

* fix usermod locations.

* fix usermods_list to include changed folder.

* Improved for both usermods: install, config, and docs. Included sample platform_override.ini.

* Updated name of SDA and SCL defines for config of display

* update docs.

* Wrong year. Fixed.

* Fix youtube link, improve config of sleep/clock when the rotary usermod isn't installed.

* Minor fixes to four line display. Addition of Auto Save v2 usermod.

* Allow config for auto-save to set the preset number to use. Load preset at startup (so brightness is set correctly).

* Updated docs for Auto Save.

* Updated docs for Auto Save.

Co-authored-by: Kevin Dorff <kevin@macbookpro-kevin-wifi.local>
2021-02-09 17:15:43 +01:00
Blaz Kristan
a9c211d66c Tetris (falling bricks) effect & Colortwinkles low brightness fix. 2021-02-07 14:45:34 +01:00
Blaz Kristan
a1c2c04510 Playlist handling. 2021-02-05 17:36:53 +01:00
cschwinne
0902b628f8 Added FPS indication in info 2021-02-05 01:33:26 +01:00
Aircoookie
517a85f9e9 Merge pull request #1711 from Aircoookie/dev-multipin-select
Multipin select working!
2021-01-31 00:42:39 +01:00
Aircoookie
94941a7732 Merge pull request #1704 from Moustachauve/content-cache
Add Cache-Control to index
2021-01-31 00:42:24 +01:00
cschwinne
f3aa8d368e Multipin appears to work 2021-01-31 00:38:27 +01:00
cschwinne
9518c5f2e4 might work 2021-01-30 20:51:36 +01:00
cschwinne
e44173ff09 JSON ledDoc (does not work, crash on delete) 2021-01-30 13:23:38 +01:00
Matchlighter
fa1106d3cf Add DNRGBW Mode for Real Time UDP Control
Add DNRGBW mode so that >367 RGBW LEDs may be controlled, similar to the DNRGB mode.
2021-01-29 23:11:37 -07:00
Christophe Gagnier
c24d574f90 Add Cache-Control to index 2021-01-28 22:16:36 -05:00
cschwinne
8ed35652bc LED memory usage indicator, better pin checker 2021-01-29 02:34:03 +01:00
cschwinne
9bdcfc8a45 LED settings JS possibly complete (need to add saving logic to set.cpp) 2021-01-26 01:58:34 +01:00
cschwinne
89b76b514c Settings progress 2021-01-26 00:19:41 +01:00
Blaz Kristan
dd433d8af0 Auto node discovery. 2021-01-22 16:17:18 +01:00
cschwinne
c105f3b970 Mulipin select compiles
Need to work on JS and set.cpp next
2021-01-21 01:21:16 +01:00
Aircoookie
94471c0d1c Merge pull request #1675 from Aircoookie/blazoncek-dev
Dynamic bus configuration and pin management
2021-01-19 18:46:51 +01:00
cschwinne
849e04ab83 Better handling of DMA re-init 2021-01-19 17:22:37 +01:00
cschwinne
67833c5513 getPins() and reversed 2021-01-19 16:51:03 +01:00
cschwinne
b0306867b4 Resolve merge conflict 2021-01-18 21:03:10 +01:00
cschwinne
d5025fdbcc Fixed canAllShow() and removeAll() 2021-01-18 20:51:32 +01:00
Blaz Kristan
e6cf1dc98d Temperature usermod update (pin management). 2021-01-17 15:00:14 +01:00
Aircoookie
0266370218 Merge pull request #1662 from Matoran/patch-1
fix COO_MAX comment
2021-01-17 14:51:49 +01:00
cschwinne
aeb3f2b018 Fix ESP32 2021-01-17 00:34:34 +01:00
Blaz Kristan
bb8d5ac13f Dynamic allocation for bus manager. 2021-01-17 00:20:31 +01:00
cschwinne
d02bf37167 Fix a few warnings 2021-01-16 21:40:04 +01:00
cschwinne
99dbd9e649 Added bus cleanup 2021-01-16 19:53:08 +01:00
cschwinne
0d63dad8c2 Working getPixelColor() 2021-01-16 17:11:23 +01:00
Marco Lopes
6ce465664f fix COO_MAX comment
COO_MAX value changed but comment does not take modification into account.
2021-01-16 16:01:50 +01:00
cschwinne
b934634159 First light from busmanager! (still not usable though) 2021-01-16 00:50:43 +01:00
cschwinne
ef904e01ec Circular include problem 2021-01-15 15:43:11 +01:00
Aircoookie
25b77db4cd Merge pull request #1658 from Aircoookie/mergedev-210115
Update multistrip dev branch
2021-01-15 11:15:04 +01:00
Aircoookie
88c0a9e30a Merge branch 'dev' into mergedev-210115 2021-01-15 11:07:21 +01:00
THATDONFC
75c219d6c6 Update LedFx url to point to new repo (#1656)
Co-authored-by: Aircoookie <dev.aircoookie@gmail.com>
2021-01-15 11:01:05 +01:00
lonestriker
82e7328903 Add support for WT32-ETH01 ethernet board and make ethernet support configurable (#1583)
* Initial support for WT32-ETH01 board

* Initial ethernet config option, doesn't save yet

* Fixed saving/restoring ethernet option, works now!

* Fixed ESP32-POE pin config (thanks to tbnobody)

* Remove esp32_eth target (use poe), minor cleanup

* Fix BTNPIN for WT32-ETH01, as found by @k7bbr

* Various fixes to ethernet option

Co-authored-by: cschwinne <dev.aircoookie@gmail.com>
2021-01-15 10:37:45 +01:00
cschwinne
d6b366c77f Finished multi segment transitions 2021-01-13 11:24:27 +01:00
cschwinne
42a7c84a33 Merge branch 'master' of https://github.com/Aircoookie/WLED 2021-01-13 11:13:29 +01:00
cheffpj2
1f4c1f2af5 A build file and README for the QuinLED-Dig-Quad Preassembled (#1643)
* runs w/ multi and temp on my preasmbld QL-DQuad

* working build file

* runs w/ multi and temp on my preasmbld QL-DQuad

* working build file

* Remove custom info

* Added README, found the UI update wasn't necessary

* Rebase the precompiled HTML file

* Spellcheck

Co-authored-by: cheffpj2 <patrick.j.mcdonough@gmail.com>
2021-01-13 00:11:43 +01:00
cschwinne
e665e4dc57 Merge branch 'master' of https://github.com/Aircoookie/WLED 2021-01-09 00:35:51 +01:00
cschwinne
9551519a35 First working multisegment transitions 2021-01-09 00:35:48 +01:00
Christophe Gagnier
df51b80e07 Fix swipe behavior on firefox when clicking on palette selection input (#1631)
* Fix swipe behavior on firefox when clicking on palette selection input

When clicking on the palette select element on firefox, it would often swipe to the next tab due to a bug in firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=1410816

* Update UI .h files and remove console log
2021-01-08 16:49:56 +01:00
cschwinne
0df6826c91 Fixed HTTP changing segments uncommanded (#1618)
Replaced Red & Blue effect with Aurora effect (PR #1589)
Updated copyright year and contributor page link
2021-01-05 21:35:07 +01:00
Mazen
af61962314 Add Aurora Borealis effect (#1589)
* Added aurora effect

* Cosmetic changes prior to PR to match some guidelines

* Update FX.cpp

Moved mode_aurora function to end of FX.cpp

* Cosmetic changes prior to PR

- Changed new palette name to have a whitespace
- Undid changes to platformio.ini

* Removed commented out test palette

* Improved memory handling

Use SEGENV to allocate and manage dynamic memory needs of effect.
Only allocate as much memory as needed for current amount of waves set.

* Improvements to Aurora effect

- Smoother on low speed
- CRGB is trivially copiable
- Replaced Red & Blue mode
- Simplified logic and mem use a tiny bit
- Aurora2 palette a bit less yellowish

Co-authored-by: cschwinne <dev.aircoookie@gmail.com>
2021-01-04 11:11:36 +01:00
cschwinne
f5ed710c0b Same procedure as every year James 2020-12-31 20:47:38 +01:00
cschwinne
e5ae07b3e8 Fixed usermod info in Info screen (#1608) 2020-12-31 17:27:40 +01:00
cschwinne
f1535e1f71 Fixed info screen 2020-12-31 16:16:33 +01:00
ChessSpider
a6316b40d1 Usermod SensorsToMqtt: Add bmp280+ccs811+si7021 sensors to wled and publish them via MQTT to Home Assistant (#1588)
* Adding SensorsToMqtt usermod, which allows WLED to read bmp280/SI7021/CCS811 sensors and publish it via MQTT to e.g. Home Assistant.

* undo some changes we dont want upstream

* undo some changes we dont want upstream2

Co-authored-by: Machiel Pronk <machiel.pronk89@gmail.com>
2020-12-29 19:35:59 +01:00
Aircoookie
c2746a55e3 Merge pull request #1529 from GrumpyMeow/feature/live-preview-websockets
Added liveviewws-page for liveview via websockets
2020-12-27 19:47:51 +01:00
Aircoookie
e9c782bf9e Merge branch 'dev' into feature/live-preview-websockets 2020-12-27 19:36:58 +01:00
spiff72
ad6c154eb6 TTGO T-Display Usermod Updates (#1565)
* Adding new usermod for TTGO T-DIsplay.

* Finalizing the code

* Modified and cleaned up readme.md file

* Fixes to platformio.ini to restore back to previous state

* Cleaned up comments in usermod file

* Updated readme with clarifications on how to modify the library files.  Saving the platformio.ini file after uncommenting the line to add the TFT_eSPI library should download the necessary library files without needing to build (and fail) first.

* Cleanup

* Readme cleanup

* corrected error in commenting platformio.ini

* updated usermod to add more info to built-in display, revised readme, added images for readme

* readme file updates
2020-12-27 19:33:51 +01:00
Aircoookie
b747b10642 Merge pull request #1580 from Aircoookie/mergedev-201226
Update multipin dev branch
2020-12-26 21:52:51 +01:00
Aircoookie
2005a2abd4 Merge branch 'dev' into mergedev-201226 2020-12-26 14:23:14 +01:00
Aircoookie
c5818ff5e4 Merge pull request #1577 from Aircoookie/gh-actions
Github Actions
2020-12-26 12:00:52 +01:00
Aircoookie
b5e02e6ff9 Merge pull request #1579 from blazoncek/fire2012-mod
Fire2012 mod & optimization.
2020-12-26 11:59:47 +01:00
cschwinne
205f62e732 Try Github Actions 2020-12-26 01:04:59 +01:00
Aircoookie
fdc8b5eb54 Merge pull request #1576 from xBelladonna/BME280
Add BME280 v2 usermod
2020-12-26 00:58:05 +01:00
cschwinne
36abe8e808 Added PR message to BME280 usermod as readme 2020-12-26 00:57:39 +01:00
xBelladonna
fcf0e08e01 Add BME280 v2 usermod 2020-12-26 05:57:20 +09:30
Blaz Kristan
0edcf97e3f Fire2012 mod & optimization. 2020-12-25 17:53:00 +01:00
Aircoookie
0707b26303 Merge pull request #1567 from WoodyLetsCode/settingsSync
Remove not allowed attribute
2020-12-24 01:00:56 +01:00
Aircoookie
09662a4bcd Merge pull request #1566 from WoodyLetsCode/settingsUiFixes
Add lang attribute to the html start tag
2020-12-24 00:21:54 +01:00
Woody
cb06961b82 removed not allowed attribute 2020-12-23 21:58:38 +01:00
Woody
824fb4adae added lang attribute to the html start tag 2020-12-23 21:43:30 +01:00
Def3nder
a91d993c6d Dynamic fade effect (#1550)
* New Effect "TV Simulator"

based on "Fake TV Light for Engineers" by Phillip Burgess https://learn.adafruit.com/fake-tv-light-for-engineers/arduino-sketch

* removed some not-used functions 

...from my 1st attempt with "Phoney TV" - but this one did not look good.

* Dynamic Effect extended with a "smooth" variant

...to close #1114 / the "Dynamice fade effect" in the projects section

Co-authored-by: Aircoookie <dev.aircoookie@gmail.com>
2020-12-22 16:26:19 +01:00
Def3nder
e16bab8dd9 TV Simulator Effect (#1548)
* New Effect "TV Simulator"

based on "Fake TV Light for Engineers" by Phillip Burgess https://learn.adafruit.com/fake-tv-light-for-engineers/arduino-sketch

* removed some not-used functions 

...from my 1st attempt with "Phoney TV" - but this one did not look good.

* Created new version of tv_colors.h based on 18 movies

...and some corrections

* Add include guards for tv_colors.h and disable option

Co-authored-by: cschwinne <dev.aircoookie@gmail.com>
2020-12-22 13:15:57 +01:00
Mike
068c5851ef Add fields for customization Blynk host (#1543)
* Add fields for customization Blynk host

Add fields to 'Sync Interfaces' for customization Blynk host.
Now you can set you own Blynk server.
All you needs its set custom host and port to local Blync server.

* Lower blynk host length (memory usage)

Co-authored-by: cschwinne <dev.aircoookie@gmail.com>
2020-12-22 00:44:16 +01:00
Aircoookie
af48dcd884 Merge pull request #1551 from mherweg/master
minor bugfix for https://github.com/Aircoookie/WLED/issues/1525
2020-12-21 21:46:45 +01:00
Aircoookie
058806c241 Merge pull request #1552 from WoodyLetsCode/randomBg
Random Background image
2020-12-21 21:45:57 +01:00
Aircoookie
48f6e33bf2 Merge pull request #1553 from WoodyLetsCode/fasterUpload
Add option for faster upload speed in platformio.ini
2020-12-21 21:45:08 +01:00
Woody
6a3ef42d37 add option for faster upload speed 2020-12-21 21:23:52 +01:00
Woody
f10a9d7f61 random bg image 2020-12-21 21:12:07 +01:00
mherweg
c315c04b9a minor bugfix for https://github.com/Aircoookie/WLED/issues/1525 2020-12-21 21:04:21 +01:00
cschwinne
f7893d4e4b Fixed JShint warnings and minify HTML 2020-12-21 19:48:21 +01:00
Aircoookie
2e2f7fa6c3 Merge pull request #1542 from WoodyLetsCode/web-final
split up index.htm
2020-12-21 15:21:37 +01:00
Woody
7aff64f877 reverting additions in cdata.js 2020-12-21 14:53:03 +01:00
Aircoookie
0d05bc2676 Merge pull request #1541 from Master-MiShutka/patch-3
Change to utf-8 to support localization
2020-12-21 13:55:20 +01:00
Master-MiShutka
131b350ee7 Change to utf-8 to support localization 2020-12-20 22:23:20 +03:00
Woody
06651dbc4c split up index.htm 2020-12-20 20:18:07 +01:00
Aircoookie
0296247d82 Merge pull request #1536 from WoodyLetsCode/settings
Fixes in settings.htm
2020-12-20 18:56:03 +01:00
cschwinne
79e767ba79 Boot brightness 0 will now use the brightness from preset
Add iOS scrolling momentum (from PR #1528)
2020-12-20 18:51:46 +01:00
Woody
f771dee852 bug fixes in settings.htm 2020-12-20 15:16:22 +01:00
Sander Schutten
c4d8ef5954 Added liveviewws-page for liveview via websockets 2020-12-19 17:26:36 +00:00
cschwinne
854501385e Bus progress 2020-12-07 01:39:42 +01:00
Aircoookie
c930d6ddc0 Merge pull request #1432 from Aircoookie/master
Merge master to dev
2020-12-04 10:04:02 +01:00
cschwinne
36a0a240f9 First progress in busmnger 2020-12-01 00:33:47 +01:00
fishbone-git
cb0452964e bidirectional running lights effect 2020-02-18 21:28:46 +01:00
165 changed files with 19415 additions and 8689 deletions

1
.envrc Normal file
View File

@@ -0,0 +1 @@
layout python-venv python3

2
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
github: [Aircoookie]
custom: ['https://paypal.me/Aircoookie']

86
.github/workflows/wled-ci.yml vendored Normal file
View File

@@ -0,0 +1,86 @@
name: PlatformIO CI
on: [push, pull_request]
jobs:
get_default_envs:
name: Gather Environments
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Cache pip
uses: actions/cache@v2
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- uses: actions/setup-python@v2
- name: Install PlatformIO
run: pip install -r requirements.txt
- name: Get default environments
id: envs
run: |
echo "::set-output name=environments::$(pio project config --json-output | jq -cr '.[0][1][0][1]')"
outputs:
environments: ${{ steps.envs.outputs.environments }}
build:
name: Build Enviornments
runs-on: ubuntu-latest
needs: get_default_envs
strategy:
matrix:
environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }}
steps:
- uses: actions/checkout@v2
- name: Cache pip
uses: actions/cache@v2
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Cache PlatformIO
uses: actions/cache@v2
with:
path: ~/.platformio
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
- name: Set up Python
uses: actions/setup-python@v2
- name: Install PlatformIO
run: pip install -r requirements.txt
- name: Build firmware
env:
WLED_RELEASE: True
run: pio run -e ${{ matrix.environment }}
- uses: actions/upload-artifact@v2
with:
name: firmware-${{ matrix.environment }}
path: |
build_output/firmware/*.bin
build_output/firmware/*.gz
- uses: actions/upload-artifact@v2
if: startsWith(github.ref, 'refs/tags/')
with:
name: firmware-release
path: build_output/release/*.bin
release:
name: Create Release
runs-on: ubuntu-latest
needs: [get_default_envs, build]
if: startsWith(github.ref, 'refs/tags/')
steps:
- uses: actions/download-artifact@v2
with:
name: firmware-release
- name: Create draft release
uses: softprops/action-gh-release@v1
with:
draft: True
files: |
*.bin
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

4
.gitignore vendored
View File

@@ -7,9 +7,11 @@
/wled00/Release
/wled00/extLibs
/platformio_override.ini
/wled00/my_config.h
/wled00/my_config.h
/build_output
.DS_Store
.gitignore
.clang-format
node_modules
.idea
.direnv

View File

@@ -1,43 +0,0 @@
# Continuous Integration (CI) is the practice, in software
# engineering, of merging all developer working copies with a shared mainline
# several times a day < https://docs.platformio.org/page/ci/index.html >
#
# Documentation:
#
# * Travis CI Embedded Builds with PlatformIO
# < https://docs.travis-ci.com/user/integration/platformio/ >
#
# * PlatformIO integration with Travis CI
# < https://docs.platformio.org/page/ci/travis.html >
#
# * User Guide for `platformio ci` command
# < https://docs.platformio.org/page/userguide/cmd_ci.html >
#
#
# Please choose one of the following templates (proposed below) and uncomment
# it (remove "# " before each line) or use own configuration according to the
# Travis CI documentation (see above).
#
# * Test the Travis config here:
# < https://config.travis-ci.com/explore >
#
language: python
python:
# - "2.7"
- "3.5"
os: linux
cache:
bundler: true
ccache: true
directories:
- "~/.platformio"
- "~/.buildcache"
env:
- PLATFORMIO_CI_SRC=wled00
install:
- pip install -U platformio
- platformio update
script:
# - platformio ci --project-conf=./platformio.ini
- platformio run

View File

@@ -1,5 +1,297 @@
## WLED changelog
### Builds after release 0.12.0
#### Build 2106302
- Fixed settings page broken by using "%" in input fields
#### Build 2106301
- Fixed a problem with disabled buttons reverting to pin 0 causing conflict
#### Build 2106300
- Version bump to 0.13.0-b0 "Toki"
- BREAKING: Removed preset cycle (use playlists)
- BREAKING: Removed `nl.fade`, `leds.pin` and `ccnf` from JSON API
- Added playlist editor UI
- Reordered segment UI and added offset field
- Raised maximum MQTT password length to 64 (closes #1373)
#### Build 2106290
- Added Offset to segments, allows shifting the LED considered first within a segment
- Added `of` property to seg object in JSON API to set offset
- Usermod settings improvements (PR #2043, PR #2045)
#### Build 2106250
- Fixed preset only disabling on second effect/color change
#### Build 2106241
- BREAKING: Added ability for usermods to force a config save if config incomplete. `readFromConfig()` needs to return a `bool` to indicate if the config is complete
- Updated usermods implementing `readFromConfig()`
- Auto-create segments based on configured busses
#### Build 2106200
- Added 2 Ethernet boards and split Ethernet configs into separate file
#### Build 2106180
- Fixed DOS on Chrome tab restore causing reboot
#### Build 2106170
- Optimized JSON buffer usage (pre-serialized color arrays)
#### Build 2106140
- Updated main logo
- Reduced flash usage by 0.8kB by using 8-bit instead of 32-bit PNGs for welcome and 404 pages
- Added a check to stop Alexa reporting an error if state set by macro differs from the expected state
#### Build 2106100
- Added support for multiple buttons with various types (PR #1977)
- Fixed infinite playlists (PR #2020)
- Added `r` to playlist object, allows for shuffle regardless of the `repeat` value
- Improved accuracy of NTP time sync
- Added possibility for WLED UDP sync to sync system time
- Improved UDP sync accuracy, if both sender and receiver are NTP synced
- Fixed a cache issue with restored tabs
- Cache CORS request
- Disable WiFi sleep by default on ESP32
#### Build 2105230
- No longer retain MQTT `/v` topic to alleviate storage loads on MQTT broker
- Fixed Sunrise calculation (atan_t approx. used outside of value range)
#### Build 2105200
- Fixed WS281x output on ESP32
- Fixed potential out-of-bounds write in MQTT
- Fixed IR pin not changeable if IR disabled
- Fixed XML API <wv> containing -1 on Manual only RGBW mode (see #888, #1783)
#### Build 2105171
- Always copy MQTT payloads to prevent non-0-terminated strings
- Updated ArduinoJson to 6.18.0
- Added experimental support for `{"on":"t"}` to toggle on/off state via JSON
#### Build 2105120
- Fixed possibility of non-0-terminated MQTT payloads
- Fixed two warnings regarding integer comparison
#### Build 2105112
- Usermod settings page no usermods message
- Lowered min speed for Drip effect
#### Build 2105111
- Fixed various Codacy code style and logic issues
#### Build 2105110
- Added Usermod settings page and configurable usermods (PR #1951)
- Added experimental `/json/cfg` endpoint for changing settings from JSON (see #1944, not part of official API)
#### Build 2105070
- Fixed not turning on after pressing "Off" on IR remote twice (#1950)
- Fixed OTA update file selection from Android app (TODO: file type verification in JS, since android can't deal with accept='.bin' attribute)
#### Build 2104220
- Version bump to 0.12.1-b1 "Hikari"
- Release and build script improvements (PR #1844)
#### Build 2104211
- Replace default TV simulator effect with the version that saves 18k of flash and appears visually identical
#### Build 2104210
- Added `tb` to JSON state, allowing setting the timebase (set tb=0 to start e.g. wipe effect from the beginning). Receive only.
- Slightly raised Solid mode refresh rate to work with LEDs (TM1814) that require refresh rates of at least 2fps
- Added sunrise and sunset calculation to the backup JSON time source
#### Build 2104151
- `NUM_STRIPS` no longer required with compile-time strip defaults
- Further optimizations in wled_math.h
#### Build 2104150
- Added ability to add multiple busses as compile time defaults using the esp32_multistrip usermod define syntax
#### Build 2104141
- Reduced memory usage by 540b by switching to a different trigonometric approximation
#### Build 2104140
- Added dynamic location-based Sunrise/Sunset macros (PR #1889)
- Improved seasonal background handling (PR #1890)
- Fixed instance discovery not working if MQTT not compiled in
- Fixed Button, IR, Relay pin not assigned by default (resolves #1891)
#### Build 2104120
- Added switch support (button macro is switch closing action, long press macro switch opening)
- Replaced Circus effect with new Running Dual effect (Circus is Tricolor Chase with Red/White/Black)
- Fixed ledmap with multiple segments (PR #1864)
#### Build 2104030
- Fixed ESP32 crash on Drip effect with reversed segment (#1854)
- Added flag `WLED_DISABLE_BROWNOUT_DET` to disable ESP32 brownout detector (off by default)
### WLED release 0.12.0
#### Build 2104020
- Allow clearing button/IR/relay pin on platforms that don't support negative numbers
- Removed AUX pin
- Hid some easter eggs, only to be found at easter
### Development versions between 0.11.1 and 0.12.0 releases
#### Build 2103310
- Version bump to 0.12.0 "Hikari"
- Fixed LED settings submission in iOS app
#### Build 2103300
- Version bump to 0.12.0-b5 "Hikari"
- Update to core espressif32@3.2
- Fixed IR pin not configurable
#### Build 2103290
- Version bump to 0.12.0-b4 "Hikari"
- Experimental use of espressif32@3.1.1
- Fixed RGBW mode disabled after LED settings saved
- Fixed infrared support not compiled in if IRPIN is not defined
#### Build 2103230
- Fixed current estimation
#### Build 2103220
- Version bump to 0.12.0-b2 "Hikari"
- Worked around an issue causing a critical decrease in framerate (wled.cpp l.240 block)
- Bump to Espalexa v2.7.0, fixing discovery
#### Build 2103210
- Version bump to 0.12.0-b1 "Hikari"
- More colors visible on Palette preview
- Fixed chevron icon not included
- Fixed color order override
- Cleanup
#### Build 2103200
- Version bump to 0.12.0-b0 "Hikari"
- Added palette preview and search (PR #1637)
- Added Reverse checkbox for PWM busses - reverses logic level for on
- Fixed various problems with the Playlist feature (PR #1724)
- Replaced "Layer" icon with "i" icon for Info button
- Chunchun effect more fitting for various segment lengths (PR #1804)
- Removed global reverse (in favor of individual bus reverse)
- Removed some unused icons from UI icon font
#### Build 2103130
- Added options for Auto Node discovery
- Optimized strings (no string both F() and raw)
#### Build 2103090
- Added Auto Node discovery (PR #1683)
- Added tooltips to quick color selectors for accessibility
#### Build 2103060
- Auto start field population in bus config
#### Build 2103050
- Fixed incorrect over-memory indication in LED settings on ESP32
#### Build 2103041
- Added destructor for BusPwm (fixes #1789)
#### Build 2103040
- Fixed relay mode inverted when upgrading from 0.11.0
- Fixed no more than 2 pins per bus configurable in UI
- Changed to non-linear IR brightness steps (PR #1742)
- Fixed various warnings (PR #1744)
- Added UDP DNRGBW Mode (PR #1704)
- Added dynamic LED mapping with ledmap.json file (PR #1738)
- Added support for QuinLED-ESP32-Ethernet board
- Added support for WESP32 ethernet board (PR #1764)
- Added Caching for main UI (PR #1704)
- Added Tetrix mode (PR #1729)
- Added memory check on Bus creation
#### Build 2102050
- Version bump to 0.12.0-a0 "Hikari"
- Added FPS indication in info
- Bumped max outputs from 7 to 10 busses for ESP32
#### Build 2101310
- First alpha configurable multipin
#### Build 2101130
- Added color transitions for all segments and slots and for segment brightness
- Fixed bug that prevented setting a boot preset higher than 25
#### Build 2101040
- Replaced Red & Blue effect with Aurora effect (PR #1589)
- Fixed HTTP changing segments uncommanded (#1618)
- Updated copyright year and contributor page link
#### Build 2012311
- Fixed Countdown mode
#### Build 2012310
- (Hopefully actually) fixed display of usermod values in info screen
#### Build 2012240
- Fixed display of usermod values in info screen
- 4 more effects now use FRAMETIME
- Remove unsupported environments from platformio.ini
#### Build 2012210
- Split index.htm in separate CSS + JS files (PR #1542)
- Minify UI HTML, saving >1.5kB flash
- Fixed JShint warnings
#### Build 2012180
- Boot brightness 0 will now use the brightness from preset
- Add iOS scrolling momentum (from PR #1528)
### WLED release 0.11.1
#### Build 2012180
@@ -120,7 +412,7 @@
#### Build 2011153
- Fixed an ESP32 end-of-file issue
- Fixed useRGBW not read from cfg.json
- Fixed strip.isRgbw not read from cfg.json
#### Build 2011152

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 602 B

View File

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

14
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "wled",
"version": "0.11.0",
"version": "0.13.0-b0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -761,9 +761,9 @@
}
},
"glob-parent": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
"integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"requires": {
"is-glob": "^4.0.1"
}
@@ -1572,9 +1572,9 @@
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
},
"normalize-url": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz",
"integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ=="
"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",

View File

@@ -1,6 +1,6 @@
{
"name": "wled",
"version": "0.11.1",
"version": "0.13.0-b0",
"description": "Tools for WLED project",
"main": "tools/cdata.js",
"directories": {

View File

@@ -0,0 +1,69 @@
Import('env')
import os
import shutil
import gzip
OUTPUT_DIR = "build_output{}".format(os.path.sep)
def _get_cpp_define_value(env, define):
define_list = [item[-1] for item in env["CPPDEFINES"] if item[0] == define]
if define_list:
return define_list[0]
return None
def _create_dirs(dirs=["firmware", "map"]):
# check if output directories exist and create if necessary
if not os.path.isdir(OUTPUT_DIR):
os.mkdir(OUTPUT_DIR)
for d in dirs:
if not os.path.isdir("{}{}".format(OUTPUT_DIR, d)):
os.mkdir("{}{}".format(OUTPUT_DIR, d))
def bin_rename_copy(source, target, env):
_create_dirs()
variant = env["PIOENV"]
# create string with location and file names based on variant
map_file = "{}map{}{}.map".format(OUTPUT_DIR, os.path.sep, variant)
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
release_name = _get_cpp_define_value(env, "WLED_RELEASE_NAME")
if release_name and os.getenv("WLED_RELEASE"):
_create_dirs(["release"])
version = _get_cpp_define_value(env, "WLED_VERSION")
release_file = "{}release{}WLED_{}_{}.bin".format(OUTPUT_DIR, os.path.sep, version, release_name)
shutil.copy(str(target[0]), release_file)
# check if new target files exist and remove if necessary
for f in [map_file, bin_file]:
if os.path.isfile(f):
os.remove(f)
# copy firmware.bin to firmware/<variant>.bin
shutil.copy(str(target[0]), bin_file)
# copy firmware.map to map/<variant>.map
if os.path.isfile("firmware.map"):
shutil.move("firmware.map", map_file)
def bin_gzip(source, target, env):
_create_dirs()
variant = env["PIOENV"]
# create string with location and file names based on variant
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
gzip_file = "{}firmware{}{}.bin.gz".format(OUTPUT_DIR, os.path.sep, variant)
# check if new target files exist and remove if necessary
if os.path.isfile(gzip_file): os.remove(gzip_file)
# write gzip firmware file
with open(bin_file,"rb") as fp:
with gzip.open(gzip_file, "wb", compresslevel = 9) as f:
shutil.copyfileobj(fp, f)
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_rename_copy, bin_gzip])

View File

@@ -0,0 +1,8 @@
Import('env')
import json
PACKAGE_FILE = "package.json"
with open(PACKAGE_FILE, "r") as package:
version = json.load(package)["version"]
env.Append(BUILD_FLAGS=[f"-DWLED_VERSION={version}"])

View File

@@ -1,23 +0,0 @@
Import('env')
import os
import shutil
import gzip
OUTPUT_DIR = "build_output{}".format(os.path.sep)
def bin_gzip(source, target, env):
variant = str(target[0]).split(os.path.sep)[2]
# create string with location and file names based on variant
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
gzip_file = "{}firmware{}{}.bin.gz".format(OUTPUT_DIR, os.path.sep, variant)
# check if new target files exist and remove if necessary
if os.path.isfile(gzip_file): os.remove(gzip_file)
# write gzip firmware file
with open(bin_file,"rb") as fp:
with gzip.open(gzip_file, "wb", compresslevel = 9) as f:
shutil.copyfileobj(fp, f)
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_gzip])

View File

@@ -1,34 +0,0 @@
Import('env')
import os
import shutil
OUTPUT_DIR = "build_output{}".format(os.path.sep)
def bin_rename_copy(source, target, env):
variant = str(target[0]).split(os.path.sep)[2]
# check if output directories exist and create if necessary
if not os.path.isdir(OUTPUT_DIR):
os.mkdir(OUTPUT_DIR)
for d in ['firmware', 'map']:
if not os.path.isdir("{}{}".format(OUTPUT_DIR, d)):
os.mkdir("{}{}".format(OUTPUT_DIR, d))
# create string with location and file names based on variant
map_file = "{}map{}{}.map".format(OUTPUT_DIR, os.path.sep, variant)
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
# check if new target files exist and remove if necessary
for f in [map_file, bin_file]:
if os.path.isfile(f):
os.remove(f)
# copy firmware.bin to firmware/<variant>.bin
shutil.copy(str(target[0]), bin_file)
# copy firmware.map to map/<variant>.map
if os.path.isfile("firmware.map"):
shutil.move("firmware.map", map_file)
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_rename_copy])

View File

@@ -9,15 +9,14 @@
# ------------------------------------------------------------------------------
# Travis CI binaries (comment this out with a ';' when building for your own board)
default_envs = travis_esp8266, travis_esp32
;default_envs = travis_esp8266, travis_esp32
# Release binaries
; default_envs = nodemcuv2, esp01_1m_full, esp32dev, custom_WS2801, custom_APA102, custom_LEDPIN_16, custom_LEDPIN_4, custom_LEDPIN_3, custom32_LEDPIN_16, custom32_APA102
default_envs = nodemcuv2, esp01_1m_full, esp32dev, esp32_eth
# Single binaries (uncomment your board)
; default_envs = elekstube_ips
; default_envs = nodemcuv2
; default_envs = esp01
; default_envs = esp01_1m_ota
; default_envs = esp01_1m_full
; default_envs = esp07
; default_envs = d1_mini
@@ -32,7 +31,7 @@ default_envs = travis_esp8266, travis_esp32
; default_envs = d1_mini_5CH_Shojo_PCB
; default_envs = wemos_shield_esp32
; default_envs = m5atom
; default_envs = esp32_poe
; default_envs = esp32_eth
src_dir = ./wled00
data_dir = ./wled00/data
@@ -50,6 +49,7 @@ extra_configs =
# ------------------------------------------------------------------------------
arduino_core_2_6_3 = espressif8266@2.3.3
arduino_core_2_7_4 = espressif8266@2.6.2
arduino_core_3_0_0 = espressif8266@3.0.0
# Development platforms
arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop
@@ -97,16 +97,6 @@ debug_flags = -D DEBUG=1 -D WLED_DEBUG -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT
# This reduces the OTA size with ~45KB, so it's especially useful on low memory boards (512k/1m).
# ------------------------------------------------------------------------------
build_flags =
-Wno-switch
-Wno-deprecated-declarations
-Wno-write-strings
-Wno-unused-variable
-Wno-unused-value
-Wno-sign-compare
-Wno-unused-but-set-variable
-Wno-return-type
-Wno-sequence-point
-Wno-narrowing
-DMQTT_MAX_PACKET_SIZE=1024
-DSECURE_CLIENT=SECURE_CLIENT_BEARSSL
-DBEARSSL_SSL_BASIC
@@ -120,17 +110,12 @@ build_flags =
-D DECODE_SAMSUNG=true
-D DECODE_LG=true
-DWLED_USE_MY_CONFIG
; -D USERMOD_SENSORSTOMQTT
build_unflags =
-Wall
-Wreorder
-Wdeprecated-declarations
# enables all features for travis CI
build_flags_all_features =
-D WLED_USE_ANALOG_LED
-D WLED_USE_H801
-D WLED_ENABLE_5CH_LEDS
-D WLED_ENABLE_ADALIGHT
-D WLED_ENABLE_DMX
-D WLED_ENABLE_MQTT
@@ -139,8 +124,6 @@ build_flags_all_features =
build_flags_esp8266 = ${common.build_flags} ${esp8266.build_flags}
build_flags_esp32 = ${common.build_flags} ${esp32.build_flags}
ldscript_512k = eagle.flash.512k.ld ;for older versions change this to eagle.flash.512k0.ld
ldscript_1m0m = eagle.flash.1m.ld ;for older versions change this to eagle.flash.1m0.ld
ldscript_1m128k = eagle.flash.1m128.ld
ldscript_2m512k = eagle.flash.2m512.ld
ldscript_2m1m = eagle.flash.2m1m.ld
@@ -150,6 +133,8 @@ ldscript_4m1m = eagle.flash.4m1m.ld
build_flags =
-DESP8266
-DFP_IN_IROM
;-Wno-deprecated-declarations
;-Wno-register
; NONOSDK22x_190703 = 2.2.2-dev(38a443e)
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
; lwIP 2 - Higher Bandwidth no Features
@@ -162,15 +147,16 @@ build_flags =
-DMIMETYPE_MINIMAL
[esp32]
build_flags = -w -g
build_flags = -g
-DARDUINO_ARCH_ESP32
-DCONFIG_LITTLEFS_FOR_IDF_3_2
[scripts_defaults]
extra_scripts = pio/name-firmware.py
pio/gzip-firmware.py
pio/strip-floats.py
pio/user_config_copy.py
extra_scripts =
pre:pio-scripts/set_version.py
post:pio-scripts/output_bins.py
post:pio-scripts/strip-floats.py
pre:pio-scripts/user_config_copy.py
# ------------------------------------------------------------------------------
# COMMON SETTINGS:
@@ -179,7 +165,10 @@ extra_scripts = pio/name-firmware.py
framework = arduino
board_build.flash_mode = dout
monitor_speed = 115200
# slow upload speed (comment this out with a ';' when building for development use)
upload_speed = 115200
# fast upload speed (remove ';' when building for development use)
; upload_speed = 921600
# ------------------------------------------------------------------------------
# LIBRARIES: required dependencies
@@ -197,16 +186,20 @@ lib_deps =
AsyncTCP @ 1.0.3
IRremoteESP8266 @ 2.7.3
https://github.com/lorol/LITTLEFS.git
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.0
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.2
#For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line
#TFT_eSPI
#For use SSD1306 OLED display uncomment following
#U8g2@~2.27.2
#For Dallas sensor uncomment following 2 lines
#OneWire@~2.3.5
#milesburton/DallasTemperature@^3.9.0
OneWire@~2.3.5
milesburton/DallasTemperature@^3.9.0
#For BME280 sensor uncomment following
#BME280@~3.0.0
; adafruit/Adafruit BMP280 Library @ 2.1.0
; adafruit/Adafruit CCS811 Library @ 1.0.4
; adafruit/Adafruit Si7021 Library @ 1.4.0
lib_ignore =
AsyncTCP
@@ -222,27 +215,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}
# Unsupported environment due to insufficient flash
[env:esp01]
board = esp01
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA -D WLED_DISABLE_ALEXA -D WLED_DISABLE_BLYNK
-D WLED_DISABLE_CRONIXIE -D WLED_DISABLE_HUESYNC -D WLED_DISABLE_INFRARED -D WLED_DISABLE_MQTT -D WLED_DISABLE_WEBSOCKETS
# Unsupported environment due to insufficient flash
[env:esp01_1m_ota]
board = esp01_1m
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m0m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_ALEXA -D WLED_DISABLE_BLYNK -D WLED_DISABLE_CRONIXIE
-D WLED_DISABLE_HUESYNC -D WLED_DISABLE_INFRARED -D WLED_DISABLE_MQTT -D WLED_DISABLE_WEBSOCKETS
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266
[env:esp01_1m_full]
board = esp01_1m
@@ -250,7 +223,7 @@ 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
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA
[env:esp07]
board = esp07
@@ -288,19 +261,19 @@ build_flags = ${common.build_flags_esp8266} -D LEDPIN=1 -D WLED_DISABLE_INFRARED
[env:esp32dev]
board = esp32dev
platform = espressif32@2.0
platform = espressif32@3.2
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32}
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32
lib_ignore =
ESPAsyncTCP
ESPAsyncUDP
[env:esp32_poe]
[env:esp32_eth]
board = esp32-poe
platform = espressif32@2.0
platform = espressif32@3.2
upload_speed = 921600
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D RLYPIN=-1 -D WLED_USE_ETHERNET
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_Ethernet -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1
lib_ignore =
ESPAsyncTCP
ESPAsyncUDP
@@ -311,7 +284,7 @@ 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_USE_ANALOG_LEDS
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
[env:esp8285_4CH_H801]
board = esp8285
@@ -319,7 +292,7 @@ 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_USE_ANALOG_LEDS -D WLED_USE_H801
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
[env:esp8285_5CH_H801]
board = esp8285
@@ -327,7 +300,7 @@ 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_USE_ANALOG_LEDS -D WLED_USE_H801 -D WLED_ENABLE_5CH_LEDS
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
[env:d1_mini_5CH_Shojo_PCB]
board = d1_mini
@@ -335,7 +308,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_USE_ANALOG_LEDS -D WLED_USE_SHOJO_PCB -D WLED_ENABLE_5CH_LEDS
build_flags = ${common.build_flags_esp8266} -D WLED_USE_SHOJO_PCB
# ------------------------------------------------------------------------------
# DEVELOPMENT BOARDS
@@ -416,7 +389,7 @@ build_flags = ${common.build_flags_esp8266} -D USE_WS2801
[env:custom32_LEDPIN_16]
board = esp32dev
platform = espressif32@2.0
platform = espressif32@3.2
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D LEDPIN=16 -D RLYPIN=19
lib_ignore =
@@ -425,7 +398,7 @@ lib_ignore =
[env:custom32_APA102]
board = esp32dev
platform = espressif32@2.0
platform = espressif32@3.2
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D USE_APA102
lib_ignore =
@@ -434,7 +407,7 @@ lib_ignore =
[env:custom32_TOUCHPIN_T0]
board = esp32dev
platform = espressif32@2.0
platform = espressif32@3.2
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D TOUCHPIN=T0
lib_ignore =
@@ -443,7 +416,7 @@ lib_ignore =
[env:wemos_shield_esp32]
board = esp32dev
platform = espressif32@2.0
platform = espressif32@3.2
upload_port = /dev/cu.SLAB_USBtoUART
monitor_port = /dev/cu.SLAB_USBtoUART
upload_speed = 460800
@@ -460,7 +433,7 @@ build_flags = ${common.build_flags_esp32} -D LEDPIN=27 -D BTNPIN=39
lib_ignore =
ESPAsyncTCP
ESPAsyncUDP
platform = espressif32@2.0
platform = espressif32@3.2
[env:sp501e]
board = esp_wroom_02
@@ -486,33 +459,10 @@ build_flags = ${common.build_flags_esp32} ${common.debug_flags} ${common.build_f
# ------------------------------------------------------------------------------
# codm pixel controller board configurations
# codm-controller-0.6 can also be used for the TYWE3S controller
# ------------------------------------------------------------------------------
[env:codm-controller-0.4]
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D LEDPIN=3
[env:codm-controller-0.4-WS2801]
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D USE_WS2801 -D CLKPIN=13 -D DATAPIN=3
[env:codm-controller-0.4-APA102]
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D USE_APA102 -D CLKPIN=13 -D DATAPIN=3
[env:codm-controller-0.5]
[env:codm-controller-0.6]
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
@@ -520,18 +470,44 @@ board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
[env:codm-controller-0.5-WS2801]
[env:codm-controller-0.6-rev2]
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D USE_WS2801 #-D CLKPIN=0 -D DATAPIN=2
build_flags = ${common.build_flags_esp8266}
[env:codm-controller-0.5-APA102]
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D USE_APA102 #-D CLKPIN=0 -D DATAPIN=2
# ------------------------------------------------------------------------------
# EleksTube-IPS
# ------------------------------------------------------------------------------
[env:elekstube_ips]
board = esp32dev
platform = espressif32@3.2
upload_speed = 921600
lib_deps = ${env.lib_deps}
TFT_eSPI
build_flags = ${common.build_flags_esp32} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED
-D USERMOD_RTC
-D USERMOD_ELEKSTUBE_IPS
-D LEDPIN=12
-D RLYPIN=27
-D BTNPIN=34
-D WLED_DISABLE_INFRARED
-D DEFAULT_LED_COUNT=6
# Display config
-D ST7789_DRIVER
-D TFT_WIDTH=135
-D TFT_HEIGHT=240
-D CGRAM_OFFSET
-D TFT_SDA_READ
-D TFT_MOSI=23
-D TFT_SCLK=18
-D TFT_DC=25
-D TFT_RST=26
-D SPI_FREQUENCY=40000000
-D USER_SETUP_LOADED
monitor_filters = esp32_exception_decoder
lib_ignore =
ESPAsyncTCP
ESPAsyncUDP

View File

@@ -11,7 +11,7 @@ default_envs = WLED_tasmota_1M
board = esp01_1m
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m0m}
board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
; *********************************************************************
@@ -39,12 +39,8 @@ build_flags = ${common.build_flags_esp8266}
; PIN defines for 2 wire LEDs
-D CLKPIN=0
-D DATAPIN=2
; to drive analog LED strips (aka 5050), uncomment the following
; PWM pins 5,12,13,15 are used with Magic Home LED Controller (default)
-D WLED_USE_ANALOG_LEDS
; for the H801 controller (PINs 15,13,12,14 (W2 = 04)) uncomment this
; -D WLED_USE_H801
; for the BW-LT11 controller (PINs 12,4,14,5 ) uncomment this
; -D WLED_USE_BWLT11
; and to enable channel 5 for RGBW-CT led strips this
; -D WLED_USE_5CH_LEDS
; to drive analog LED strips (aka 5050) hardware configuration is no longer necessary
; configure the settings in the UI as follows (hard):
; for the Magic Home LED Controller use PWM pins 5,12,13,15
; for the H801 controller use PINs 15,13,12,14 (W2 = 04)
; for the BW-LT11 controller use PINs 12,4,14,5

View File

@@ -21,6 +21,7 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control
- Segments to set different effects and colors to parts of the LEDs
- Settings page - configuration over 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
@@ -36,6 +37,7 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control
- 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)

1
requirements.in Normal file
View File

@@ -0,0 +1 @@
platformio

54
requirements.txt Normal file
View File

@@ -0,0 +1,54 @@
#
# This file is autogenerated by pip-compile
# To update, run:
#
# pip-compile
#
aiofiles==0.6.0
# via platformio
ajsonrpc==1.1.0
# via platformio
bottle==0.12.19
# via platformio
certifi==2020.12.5
# via requests
chardet==4.0.0
# via requests
click==7.1.2
# via
# platformio
# uvicorn
colorama==0.4.4
# via platformio
h11==0.12.0
# via
# uvicorn
# wsproto
idna==2.10
# via requests
ifaddr==0.1.7
# via zeroconf
marshmallow==3.11.1
# via platformio
platformio==5.1.1
# via -r requirements.in
pyelftools==0.27
# via platformio
pyserial==3.5
# via platformio
requests==2.25.1
# via platformio
semantic-version==2.8.5
# via platformio
starlette==0.14.2
# via platformio
tabulate==0.8.9
# via platformio
urllib3==1.26.5
# via requests
uvicorn==0.13.4
# via platformio
wsproto==1.0.0
# via platformio
zeroconf==0.28.8
# via platformio

View File

@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, 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,0xBE0000,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0xe000 0x2000
4 app0 app ota_0 0x10000 0x200000
5 app1 app ota_1 0x210000 0x200000
6 spiffs data spiffs 0x410000 0xBE0000

View File

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

6
tools/WLED_ESP32_8MB.csv Normal file
View File

@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, 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,0x3F0000,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0xe000 0x2000
4 app0 app ota_0 0x10000 0x200000
5 app1 app ota_1 0x210000 0x200000
6 spiffs data spiffs 0x410000 0x3F0000

View File

@@ -69,6 +69,8 @@ function writeHtmlGzipped(sourceFile, resultFile) {
console.info("Reading " + sourceFile);
new inliner(sourceFile, function (error, html) {
console.info("Inlined " + html.length + " characters");
html = filter(html, "html-minify-ui");
console.info("Minified to " + html.length + " characters");
if (error) {
console.warn(error);
@@ -123,6 +125,16 @@ function filter(str, type) {
continueOnParseError: false,
removeComments: true,
});
} else if (type == "html-minify-ui") {
return MinifyHTML(str, {
collapseWhitespace: true,
conservativeCollapse: true,
maxLineLength: 80,
minifyCSS: true,
minifyJS: true,
continueOnParseError: false,
removeComments: true,
});
} else {
console.warn("Unknown filter: " + type);
return str;
@@ -132,7 +144,7 @@ function filter(str, type) {
function specToChunk(srcDir, s) {
if (s.method == "plaintext") {
const buf = fs.readFileSync(srcDir + "/" + s.file);
const str = buf.toString("ascii");
const str = buf.toString("utf-8");
const chunk = `
// Autogenerated from ${srcDir}/${s.file}, do not edit!!
const char ${s.name}[] PROGMEM = R"${s.prepend || ""}${filter(str, s.filter)}${
@@ -321,6 +333,22 @@ const char PAGE_settings_dmx[] PROGMEM = R"=====()=====";
"function GetV() {var d=document;\n"
),
},
{
file: "settings_um.htm",
name: "PAGE_settings_um",
prepend: "=====(",
append: ")=====",
method: "plaintext",
filter: "html-minify",
mangle: (str) =>
str
.replace(/\<link rel="stylesheet".*\>/gms, "")
.replace(/\<style\>.*\<\/style\>/gms, "%CSS%%SCSS%")
.replace(
/function GetV().*\<\/script\>/gms,
"function GetV() {var d=document;\n"
),
}
],
"wled00/html_settings.h"
);
@@ -386,6 +414,14 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()=====";
method: "plaintext",
filter: "html-minify",
},
{
file: "liveviewws.htm",
name: "PAGE_liveviewws",
prepend: "=====(",
append: ")=====",
method: "plaintext",
filter: "html-minify",
},
{
file: "404.htm",
name: "PAGE_404",

232
tools/fps_test.htm Normal file
View File

@@ -0,0 +1,232 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>WLED frame rate test tool</title>
<style>
body {
background-color: #222;
color: #fff;
font-family: Helvetica, Verdana, sans-serif;
}
input {
background-color: #333;
color: #fff;
}
#ip {
width: 100px;
}
#secs {
width: 36px;
}
#csva {
position: absolute;
top: -100px; /*gtfo*/
}
button {
background-color: #333;
color: #fff;
}
table, th, td {
border: 1px solid #aaa;
border-collapse: collapse;
text-align: center;
}
.red {
color: #d20;
}
</style>
<script>
var gotfx = false, running = false;
var pos = 0, prev = 0, min = 999, max = 0, fpslist = [], names = [], names_checked = [];
var to;
function S() {
document.getElementById('ip').value = localStorage.getItem('locIpFps');
if (document.getElementById('ip').value) req(false);
}
function loadC() {
hide(false);
var list = localStorage.getItem('fpsFxSelection');
if (!list) return;
list = JSON.parse(list);
var chks = document.querySelectorAll('.fxcheck');
for (let i = 0; i < chks.length; i++) {
if (i < list.length) chks[i].checked = list[i];
}
}
function saveC() {
var list = [];
var chks = document.querySelectorAll('.fxcheck');
for (let i = 0; i < chks.length; i++) {
list.push(chks[i].checked);
}
localStorage.setItem('fpsFxSelection', JSON.stringify(list));
}
function setC(c) {
hide(false);
var chks = document.querySelectorAll('.fxcheck');
for (let i = 0; i < chks.length; i++) {
chks[i].checked = (c == 255);
}
if (c == 1 && chks.length > 100) {
chks[1].checked = true; //Blink
chks[15].checked = true; //Running
chks[16].checked = true; //Saw
chks[37].checked = true; //Running 2
chks[44].checked = true; //Tetrix
chks[63].checked = true; //Pride 2015
chks[74].checked = true; //Colortwinkles
chks[101].checked = true;//Pacifica
}
}
function hide(h) {
var trs = document.querySelectorAll('.trs');
var chks = document.querySelectorAll('.fxcheck');
for (let i = 0; i < trs.length; i++) {
trs[i].style.display = (h && !chks[i].checked) ? "none":"table-row";
}
}
function run(init) {
if (init) {
running = !running;
document.getElementById('runbtn').innerText = running ? 'Stop':'Run';
if (running) {pos = 0; prev = -1; min = 999; max = 0; fpslist = []; names_checked = []; hide(true);}
clearTimeout(to);
if (!running) {req({seg:{fx:0},v:true,stop:true}); return;}
}
if (!gotfx) {req(false); return;}
var chks = document.querySelectorAll('.fxcheck');
var fpsb = document.querySelectorAll('.fps');
if (prev >= 0) {pos++};
if (pos >= chks.length) {run(true); return;} //end
while (!chks[pos].checked) {
fpsb[pos].innerText = "-";
pos++;
if (pos >= chks.length) {run(true); return;} //end
}
names_checked.push(names[pos]);
var extra = {};
try {
extra = JSON.parse(document.getElementById('ej').value);
} catch (e) {
}
var cmd = {seg:{fx:pos},v:true};
Object.assign(cmd, extra);
req(cmd);
}
function req(command) {
var ip = document.getElementById('ip').value;
if (!ip) {alert("Please enter WLED IP"); return;}
if (ip != localStorage.getItem('locIpFps')) localStorage.setItem('locIpFps', document.getElementById('ip').value);
var url = command ? `http://${ip}/json/si` : `http://${ip}/json/effects`;
var type = command ? 'post':'get';
var req = undefined;
if (command)
{
req = JSON.stringify(command);
}
fetch
(url, {
method: type,
headers: {
"Content-type": "application/json; charset=UTF-8"
},
body: req
})
.then(res => {
if (!res.ok) {
alert('Data malfunction');
}
return res.json();
})
.then(json => {
if (!json) {
alert('Empty response'); return;
}
if (!command) {
names = json;
var tblc = '';
for (let i = 0; i < json.length; i++) {
tblc += `<tr class="trs"><td><input type="checkbox" class="fxcheck" /></td><td>${i}</td><td>${json[i]}</td><td class="fps"></td></tr>`
}
var tbl = `<table>
<tr>
<th>Test?</th><th>ID</th><th>Effect Name</th><th>FPS</th>
</tr>
${tblc}
</table>`;
document.getElementById('tablecon').innerHTML = tbl;
setC(1);
loadC();
gotfx = true;
document.getElementById('runbtn').innerText = "Run";
} else {
if (!json.info) return;
document.getElementById('leds').innerText = json.info.leds.count;
document.getElementById('seg').innerText = json.state.seg[0].len;
document.getElementById('bri').innerText = json.state.bri;
if (prev >= 0) {
var lastfps = parseInt(json.info.leds.fps); //previous FX
if (lastfps < min) min = lastfps;
if (lastfps > max) max = lastfps;
fpslist.push(lastfps);
var sum = 0;
for (let i = 0; i < fpslist.length; i++) {
sum += fpslist[i];
}
sum /= fpslist.length;
document.getElementById('fps_min').innerText = min;
document.getElementById('fps_max').innerText = max;
document.getElementById('fps_avg').innerText = Math.round(sum*10)/10;
var fpsb = document.querySelectorAll('.fps');
fpsb[prev].innerHTML = lastfps;
}
prev = pos;
var delay = parseInt(document.getElementById('secs').value)*1000;
delay = Math.min(Math.max(delay, 2000), 15000)
if (!command.stop) to = setTimeout(run,delay);
}
})
.catch(function (error) {
alert('Comms malfunction');
console.log(error);
});
}
function csv(n) {
var txt = "";
for (let i = 0; i < fpslist.length; i++) {
if (!n) txt += names_checked[i] + ',';
txt += fpslist[i]; txt += "\n";
}
document.getElementById('csva').value = txt;
var copyText = document.getElementById('csva');
copyText.select();
copyText.setSelectionRange(0, 999999);
document.execCommand("copy");
}
</script>
</head>
<body onload="S()">
<h2>Starship monitoring dashboard</h2>
(or rather just a WLED frame rate tester lol)<br><br>
IP: <input id="ip" /><br>
Time per effect: <input type=number id=secs value=5 max=15 min=2 />s<br>
Effects to test:
<button type="button" onclick="setC(255)">All</button>
<button type="button" onclick="setC(1)">Selection 1</button>
<button type="button" onclick="setC(0)">None</button>
<button type="button" onclick="loadC()">Get LS</button>
<button type="button" class="red" onclick="saveC()">Save to LS</button><br>
Extra JSON: <input id="ej" /><br>
<button type="button" onclick="run(true)" id="runbtn">Fetch FX list</button><br>
LEDs: <span id="leds">-</span>, Seg: <span id="seg">-</span>, Bri: <span id="bri">-</span><br>
FPS min: <span id="fps_min">-</span>, max: <span id="fps_max">-</span>, avg: <span id="fps_avg">-</span><br><br>
<div id="tablecon">
</div><br>
<button type="button" onclick="csv(false)">Copy csv to clipboard</button>
<button type="button" onclick="csv(true)">Copy csv (FPS only)</button>
<textarea id=csva></textarea>
</body>
</html>

View File

@@ -0,0 +1,530 @@
/*
* Usermod for detecting people entering/leaving a staircase and switching the
* staircase on/off.
*
* Edit the Animated_Staircase_config.h file to compile this usermod for your
* specific configuration.
*
* See the accompanying README.md file for more info.
*/
#pragma once
#include "wled.h"
class Animated_Staircase : public Usermod {
private:
/* configuration (available in API and stored in flash) */
bool enabled = false; // Enable this usermod
unsigned long segment_delay_ms = 150; // Time between switching each segment
unsigned long on_time_ms = 30000; // The time for the light to stay on
int8_t topPIRorTriggerPin = -1; // disabled
int8_t bottomPIRorTriggerPin = -1; // disabled
int8_t topEchoPin = -1; // disabled
int8_t bottomEchoPin = -1; // disabled
bool useUSSensorTop = false; // using PIR or UltraSound sensor?
bool useUSSensorBottom = false; // using PIR or UltraSound sensor?
unsigned int topMaxDist = 50; // default maximum measured distance in cm, top
unsigned int bottomMaxDist = 50; // default maximum measured distance in cm, bottom
/* runtime variables */
bool initDone = false;
// Time between checking of the sensors
const unsigned int scanDelay = 100;
// Lights on or off.
// Flipping this will start a transition.
bool on = false;
// Swipe direction for current transition
#define SWIPE_UP true
#define SWIPE_DOWN false
bool swipe = SWIPE_UP;
// Indicates which Sensor was seen last (to determine
// the direction when swiping off)
#define LOWER false
#define UPPER true
bool lastSensor = LOWER;
// Time of the last transition action
unsigned long lastTime = 0;
// Time of the last sensor check
unsigned long lastScanTime = 0;
// Last time the lights were switched on or off
unsigned long lastSwitchTime = 0;
// segment id between onIndex and offIndex are on.
// controll the swipe by setting/moving these indices around.
// onIndex must be less than or equal to offIndex
byte onIndex = 0;
byte offIndex = 0;
// The maximum number of configured segments.
// Dynamically updated based on user configuration.
byte maxSegmentId = 1;
byte mainSegmentId = 0;
// These values are used by the API to read the
// last sensor state, or trigger a sensor
// through the API
bool topSensorRead = false;
bool topSensorWrite = false;
bool bottomSensorRead = false;
bool bottomSensorWrite = false;
bool topSensorState = false;
bool bottomSensorState = false;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _enabled[];
static const char _segmentDelay[];
static const char _onTime[];
static const char _useTopUltrasoundSensor[];
static const char _topPIRorTrigger_pin[];
static const char _topEcho_pin[];
static const char _useBottomUltrasoundSensor[];
static const char _bottomPIRorTrigger_pin[];
static const char _bottomEcho_pin[];
static const char _topEchoCm[];
static const char _bottomEchoCm[];
void publishMqtt(bool bottom, const char* state)
{
//Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED){
char subuf[64];
sprintf_P(subuf, PSTR("%s/motion/%d"), mqttDeviceTopic, (int)bottom);
mqtt->publish(subuf, 0, false, state);
}
}
void updateSegments() {
mainSegmentId = strip.getMainSegmentId();
WS2812FX::Segment* segments = strip.getSegments();
for (int i = 0; i < MAX_NUM_SEGMENTS; i++, segments++) {
if (!segments->isActive()) {
maxSegmentId = i - 1;
break;
}
if (i >= onIndex && i < offIndex) {
segments->setOption(SEG_OPTION_ON, 1, 1);
// We may need to copy mode and colors from segment 0 to make sure
// changes are propagated even when the config is changed during a wipe
// segments->mode = mainsegment.mode;
// segments->colors[0] = mainsegment.colors[0];
} else {
segments->setOption(SEG_OPTION_ON, 0, 1);
}
// Always mark segments as "transitional", we are animating the staircase
segments->setOption(SEG_OPTION_TRANSITIONAL, 1, 1);
}
colorUpdated(NOTIFIER_CALL_MODE_DIRECT_CHANGE);
}
/*
* Detects if an object is within ultrasound range.
* signalPin: The pin where the pulse is sent
* echoPin: The pin where the echo is received
* maxTimeUs: Detection timeout in microseconds. If an echo is
* received within this time, an object is detected
* and the function will return true.
*
* The speed of sound is 343 meters per second at 20 degress Celcius.
* Since the sound has to travel back and forth, the detection
* distance for the sensor in cm is (0.0343 * maxTimeUs) / 2.
*
* For practical reasons, here are some useful distances:
*
* Distance = maxtime
* 5 cm = 292 uS
* 10 cm = 583 uS
* 20 cm = 1166 uS
* 30 cm = 1749 uS
* 50 cm = 2915 uS
* 100 cm = 5831 uS
*/
bool ultrasoundRead(int8_t signalPin, int8_t echoPin, unsigned int maxTimeUs) {
if (signalPin<0 || echoPin<0) return false;
digitalWrite(signalPin, LOW);
delayMicroseconds(2);
digitalWrite(signalPin, HIGH);
delayMicroseconds(10);
digitalWrite(signalPin, LOW);
return pulseIn(echoPin, HIGH, maxTimeUs) > 0;
}
bool checkSensors() {
bool sensorChanged = false;
if ((millis() - lastScanTime) > scanDelay) {
lastScanTime = millis();
bottomSensorRead = bottomSensorWrite ||
(!useUSSensorBottom ?
(bottomPIRorTriggerPin<0 ? false : digitalRead(bottomPIRorTriggerPin)) :
ultrasoundRead(bottomPIRorTriggerPin, bottomEchoPin, bottomMaxDist*59) // cm to us
);
topSensorRead = topSensorWrite ||
(!useUSSensorTop ?
(topPIRorTriggerPin<0 ? false : digitalRead(topPIRorTriggerPin)) :
ultrasoundRead(topPIRorTriggerPin, topEchoPin, topMaxDist*59) // cm to us
);
if (bottomSensorRead != bottomSensorState) {
bottomSensorState = bottomSensorRead; // change previous state
sensorChanged = true;
publishMqtt(true, bottomSensorState ? "on" : "off");
DEBUG_PRINTLN(F("Bottom sensor changed."));
}
if (topSensorRead != topSensorState) {
topSensorState = topSensorRead; // change previous state
sensorChanged = true;
publishMqtt(false, topSensorState ? "on" : "off");
DEBUG_PRINTLN(F("Top sensor changed."));
}
// Values read, reset the flags for next API call
topSensorWrite = false;
bottomSensorWrite = false;
if (topSensorRead != bottomSensorRead) {
lastSwitchTime = millis();
if (on) {
lastSensor = topSensorRead;
} else {
// If the bottom sensor triggered, we need to swipe up, ON
swipe = bottomSensorRead;
DEBUG_PRINT(F("ON -> Swipe "));
DEBUG_PRINTLN(swipe ? F("up.") : F("down."));
if (onIndex == offIndex) {
// Position the indices for a correct on-swipe
if (swipe == SWIPE_UP) {
onIndex = mainSegmentId;
} else {
onIndex = maxSegmentId+1;
}
offIndex = onIndex;
}
on = true;
}
}
}
return sensorChanged;
}
void autoPowerOff() {
if (on && ((millis() - lastSwitchTime) > on_time_ms)) {
// if sensors are still on, do nothing
if (bottomSensorState || topSensorState) return;
// Swipe OFF in the direction of the last sensor detection
swipe = lastSensor;
on = false;
DEBUG_PRINT(F("OFF -> Swipe "));
DEBUG_PRINTLN(swipe ? F("up.") : F("down."));
}
}
void updateSwipe() {
if ((millis() - lastTime) > segment_delay_ms) {
lastTime = millis();
if (on) {
// Turn on all segments
onIndex = MAX(mainSegmentId, onIndex - 1);
offIndex = MIN(maxSegmentId + 1, offIndex + 1);
} else {
if (swipe == SWIPE_UP) {
onIndex = MIN(offIndex, onIndex + 1);
} else {
offIndex = MAX(onIndex, offIndex - 1);
}
}
updateSegments();
}
}
// send sesnor values to JSON API
void writeSensorsToJson(JsonObject& staircase) {
staircase[F("top-sensor")] = topSensorRead;
staircase[F("bottom-sensor")] = bottomSensorRead;
}
// allow overrides from JSON API
void readSensorsFromJson(JsonObject& staircase) {
bottomSensorWrite = bottomSensorState || (staircase[F("bottom-sensor")].as<bool>());
topSensorWrite = topSensorState || (staircase[F("top-sensor")].as<bool>());
}
void enable(bool enable) {
if (enable) {
DEBUG_PRINTLN(F("Animated Staircase enabled."));
DEBUG_PRINT(F("Delay between steps: "));
DEBUG_PRINT(segment_delay_ms);
DEBUG_PRINT(F(" milliseconds.\nStairs switch off after: "));
DEBUG_PRINT(on_time_ms / 1000);
DEBUG_PRINTLN(F(" seconds."));
if (!useUSSensorBottom)
pinMode(bottomPIRorTriggerPin, INPUT_PULLUP);
else {
pinMode(bottomPIRorTriggerPin, OUTPUT);
pinMode(bottomEchoPin, INPUT);
}
if (!useUSSensorTop)
pinMode(topPIRorTriggerPin, INPUT_PULLUP);
else {
pinMode(topPIRorTriggerPin, OUTPUT);
pinMode(topEchoPin, INPUT);
}
} else {
// Restore segment options
WS2812FX::Segment* segments = strip.getSegments();
for (int i = 0; i < MAX_NUM_SEGMENTS; i++, segments++) {
if (!segments->isActive()) {
maxSegmentId = i - 1;
break;
}
segments->setOption(SEG_OPTION_ON, 1, 1);
}
colorUpdated(NOTIFIER_CALL_MODE_DIRECT_CHANGE);
DEBUG_PRINTLN(F("Animated Staircase disabled."));
}
enabled = enable;
}
public:
void setup() {
// allocate pins
if (topPIRorTriggerPin >= 0) {
if (!pinManager.allocatePin(topPIRorTriggerPin,useUSSensorTop))
topPIRorTriggerPin = -1;
}
if (topEchoPin >= 0) {
if (!pinManager.allocatePin(topEchoPin,false))
topEchoPin = -1;
}
if (bottomPIRorTriggerPin >= 0) {
if (!pinManager.allocatePin(bottomPIRorTriggerPin,useUSSensorBottom))
bottomPIRorTriggerPin = -1;
}
if (bottomEchoPin >= 0) {
if (!pinManager.allocatePin(bottomEchoPin,false))
bottomEchoPin = -1;
}
enable(enabled);
initDone = true;
}
void loop() {
if (!enabled || strip.isUpdating()) return;
checkSensors();
autoPowerOff();
updateSwipe();
}
uint16_t getId() { return USERMOD_ID_ANIMATED_STAIRCASE; }
/**
* handling of MQTT message
* topic only contains stripped topic (part after /wled/MAC)
* topic should look like: /swipe with amessage of [up|down]
*/
bool onMqttMessage(char* topic, char* payload) {
if (strlen(topic) == 6 && strncmp_P(topic, PSTR("/swipe"), 6) == 0) {
String action = payload;
if (action == "up") {
bottomSensorWrite = true;
return true;
} else if (action == "down") {
topSensorWrite = true;
return true;
} else if (action == "on") {
enable(true);
return true;
} else if (action == "off") {
enable(false);
return true;
}
}
return false;
}
/**
* subscribe to MQTT topic for controlling usermod
*/
void onMqttConnect(bool sessionPresent) {
//(re)subscribe to required topics
char subuf[64];
if (mqttDeviceTopic[0] != 0) {
strcpy(subuf, mqttDeviceTopic);
strcat_P(subuf, PSTR("/swipe"));
mqtt->subscribe(subuf, 0);
}
}
void addToJsonState(JsonObject& root) {
JsonObject staircase = root[FPSTR(_name)];
if (staircase.isNull()) {
staircase = root.createNestedObject(FPSTR(_name));
}
writeSensorsToJson(staircase);
DEBUG_PRINTLN(F("Staircase sensor state exposed in API."));
}
/*
* Reads configuration settings from the json API.
* See void addToJsonState(JsonObject& root)
*/
void readFromJsonState(JsonObject& root) {
if (!initDone) return; // prevent crash on boot applyPreset()
JsonObject staircase = root[FPSTR(_name)];
if (!staircase.isNull()) {
if (staircase[FPSTR(_enabled)].is<bool>()) {
enabled = staircase[FPSTR(_enabled)].as<bool>();
} else {
String str = staircase[FPSTR(_enabled)]; // checkbox -> off or on
enabled = (bool)(str!="off"); // off is guaranteed to be present
}
readSensorsFromJson(staircase);
DEBUG_PRINTLN(F("Staircase sensor state read from API."));
}
}
/*
* Writes the configuration to internal flash memory.
*/
void addToConfig(JsonObject& root) {
JsonObject staircase = root[FPSTR(_name)];
if (staircase.isNull()) {
staircase = root.createNestedObject(FPSTR(_name));
}
staircase[FPSTR(_enabled)] = enabled;
staircase[FPSTR(_segmentDelay)] = segment_delay_ms;
staircase[FPSTR(_onTime)] = on_time_ms / 1000;
staircase[FPSTR(_useTopUltrasoundSensor)] = useUSSensorTop;
staircase[FPSTR(_topPIRorTrigger_pin)] = topPIRorTriggerPin;
staircase[FPSTR(_topEcho_pin)] = useUSSensorTop ? topEchoPin : -1;
staircase[FPSTR(_useBottomUltrasoundSensor)] = useUSSensorBottom;
staircase[FPSTR(_bottomPIRorTrigger_pin)] = bottomPIRorTriggerPin;
staircase[FPSTR(_bottomEcho_pin)] = useUSSensorBottom ? bottomEchoPin : -1;
staircase[FPSTR(_topEchoCm)] = topMaxDist;
staircase[FPSTR(_bottomEchoCm)] = bottomMaxDist;
DEBUG_PRINTLN(F("Staircase config saved."));
}
/*
* Reads the configuration to internal flash memory before setup() is called.
*
* The function should return true if configuration was successfully loaded or false if there was no configuration.
*/
bool readFromConfig(JsonObject& root) {
bool oldUseUSSensorTop = useUSSensorTop;
bool oldUseUSSensorBottom = useUSSensorBottom;
int8_t oldTopAPin = topPIRorTriggerPin;
int8_t oldTopBPin = topEchoPin;
int8_t oldBottomAPin = bottomPIRorTriggerPin;
int8_t oldBottomBPin = bottomEchoPin;
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
enabled = top[FPSTR(_enabled)] | enabled;
segment_delay_ms = top[FPSTR(_segmentDelay)] | segment_delay_ms;
segment_delay_ms = (unsigned long) min((unsigned long)10000,max((unsigned long)10,(unsigned long)segment_delay_ms)); // max delay 10s
on_time_ms = top[FPSTR(_onTime)] | on_time_ms/1000;
on_time_ms = min(900,max(10,(int)on_time_ms)) * 1000; // min 10s, max 15min
useUSSensorTop = top[FPSTR(_useTopUltrasoundSensor)] | useUSSensorTop;
topPIRorTriggerPin = top[FPSTR(_topPIRorTrigger_pin)] | topPIRorTriggerPin;
topEchoPin = top[FPSTR(_topEcho_pin)] | topEchoPin;
useUSSensorBottom = top[FPSTR(_useBottomUltrasoundSensor)] | useUSSensorBottom;
bottomPIRorTriggerPin = top[FPSTR(_bottomPIRorTrigger_pin)] | bottomPIRorTriggerPin;
bottomEchoPin = top[FPSTR(_bottomEcho_pin)] | bottomEchoPin;
topMaxDist = top[FPSTR(_topEchoCm)] | topMaxDist;
topMaxDist = min(150,max(30,(int)topMaxDist)); // max distnace ~1.5m (a lag of 9ms may be expected)
bottomMaxDist = top[FPSTR(_bottomEchoCm)] | bottomMaxDist;
bottomMaxDist = min(150,max(30,(int)bottomMaxDist)); // max distance ~1.5m (a lag of 9ms may be expected)
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
// first run: reading from cfg.json
DEBUG_PRINTLN(F(" config loaded."));
} else {
// changing parameters from settings page
DEBUG_PRINTLN(F(" config (re)loaded."));
bool changed = false;
if ((oldUseUSSensorTop != useUSSensorTop) ||
(oldUseUSSensorBottom != useUSSensorBottom) ||
(oldTopAPin != topPIRorTriggerPin) ||
(oldTopBPin != topEchoPin) ||
(oldBottomAPin != bottomPIRorTriggerPin) ||
(oldBottomBPin != bottomEchoPin)) {
changed = true;
pinManager.deallocatePin(oldTopAPin);
pinManager.deallocatePin(oldTopBPin);
pinManager.deallocatePin(oldBottomAPin);
pinManager.deallocatePin(oldBottomBPin);
}
if (changed) setup();
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return true;
}
/*
* Shows the delay between steps and power-off time in the "info"
* tab of the web-UI.
*/
void addToJsonInfo(JsonObject& root) {
JsonObject staircase = root["u"];
if (staircase.isNull()) {
staircase = root.createNestedObject("u");
}
JsonArray usermodEnabled = staircase.createNestedArray(F("Staircase")); // name
String btn = F("<button class=\"btn infobtn\" onclick=\"requestJson({staircase:{enabled:");
if (enabled) {
btn += F("false}},false,false);loadInfo();\">");
btn += F("enabled");
} else {
btn += F("true}},false,false);loadInfo();\">");
btn += F("disabled");
}
btn += F("</button>");
usermodEnabled.add(btn); // value
}
};
// strings to reduce flash memory usage (used more than twice)
const char Animated_Staircase::_name[] PROGMEM = "staircase";
const char Animated_Staircase::_enabled[] PROGMEM = "enabled";
const char Animated_Staircase::_segmentDelay[] PROGMEM = "segment-delay-ms";
const char Animated_Staircase::_onTime[] PROGMEM = "on-time-s";
const char Animated_Staircase::_useTopUltrasoundSensor[] PROGMEM = "useTopUltrasoundSensor";
const char Animated_Staircase::_topPIRorTrigger_pin[] PROGMEM = "topPIRorTrigger_pin";
const char Animated_Staircase::_topEcho_pin[] PROGMEM = "topEcho_pin";
const char Animated_Staircase::_useBottomUltrasoundSensor[] PROGMEM = "useBottomUltrasoundSensor";
const char Animated_Staircase::_bottomPIRorTrigger_pin[] PROGMEM = "bottomPIRorTrigger_pin";
const char Animated_Staircase::_bottomEcho_pin[] PROGMEM = "bottomEcho_pin";
const char Animated_Staircase::_topEchoCm[] PROGMEM = "top-dist-cm";
const char Animated_Staircase::_bottomEchoCm[] PROGMEM = "bottom-dist-cm";

View File

@@ -0,0 +1,131 @@
# Usermod Animated Staircase
This usermod makes your staircase look cool by switching it on with an animation. It uses
PIR or ultrasonic sensors at the top and bottom of your stairs to:
- Light up the steps in your walking direction, leading the way.
- Switch off the steps after you, in the direction of the last detected movement.
- Always switch on when one of the sensors detects movement, even if an effect
is still running. It can therewith handle multiple people on the stairs gracefully.
The Animated Staircase can be controlled by the WLED API. Change settings such as
speed, on/off time and distance settings by sending an HTTP request, see below.
## WLED integration
To include this usermod in your WLED setup, you have to be able to [compile WLED from source](https://github.com/Aircoookie/WLED/wiki/Compiling-WLED).
Before compiling, you have to make the following modifications:
Edit `usermods_list.cpp`:
1. Open `wled00/usermods_list.cpp`
2. add `#include "../usermods/Animated_Staircase/Animated_Staircase.h"` to the top of the file
3. add `usermods.add(new Animated_Staircase());` to the end of the `void registerUsermods()` function.
You can configure usermod using Usermods settings page.
Please enter GPIO pins for PIR sensors or ultrasonic sensor (trigger and echo).
If you use PIR sensor enter -1 for echo pin.
Maximum distance for ultrasonic sensor can be configured as a time needed for echo (see below).
## Hardware installation
1. Stick the LED strip under each step of the stairs.
2. Connect the ESP8266 pin D4 or ESP32 pin D2 to the first LED data pin at the bottom step
of your stairs.
3. Connect the data-out pin at the end of each strip per step to the data-in pin on the
other end of the next step, creating one large virtual LED strip.
4. Mount sensors of choice at the bottom and top of the stairs and connect them to the ESP.
5. To make sure all LEDs get enough power and have your staircase lighted evenly, power each
step from one side, using at least AWG14 or 2.5mm^2 cable. Don't connect them serial as you
do for the datacable!
You _may_ need to use 10k pull-down resistors on the selected PIR pins, depending on the sensor.
## WLED configuration
1. In the WLED UI, confgure a segment for each step. The lowest step of the stairs is the
lowest segment id.
2. Save your segments into a preset.
3. Ideally, add the preset in the config > LED setup menu to the "apply
preset **n** at boot" setting.
## Changing behavior through API
The Staircase settings can be changed through the WLED JSON api.
**NOTE:** We are using [curl](https://curl.se/) to send HTTP POSTs to the WLED API.
If you're using Windows and want to use the curl commands, replace the `\` with a `^`
or remove them and put everything on one line.
| Setting | Description | Default |
|------------------|---------------------------------------------------------------|---------|
| enabled | Enable or disable the usermod | true |
| bottom-sensor | Manually trigger a down to up animation via API | false |
| top-sensor | Manually trigger an up to down animation via API | false |
To read the current settings, open a browser to `http://xxx.xxx.xxx.xxx/json/state` (use your WLED
device IP address). The device will respond with a json object containing all WLED settings.
The staircase settings and sensor states are inside the WLED status element:
```json
{
"state": {
"staircase": {
"enabled": true,
"bottom-sensor": false,
"tops-ensor": false
},
}
```
### Enable/disable the usermod
By disabling the usermod you will be able to keep the LED's on, independent from the sensor
activity. This enables to play with the lights without the usermod switching them on or off.
To disable the usermod:
```bash
curl -X POST -H "Content-Type: application/json" \
-d {"staircase":{"enabled":false}} \
xxx.xxx.xxx.xxx/json/state
```
To enable the usermod again, use `"enabled":true`.
Alternatively you can use _Usermod_ Settings page where you can change other parameters as well.
### Changing animation parameters and detection range of the ultrasonic HC-SR04 sensor
Using _Usermod_ Settings page you can define different usermod parameters, includng sensor pins, delay between segment activation and so on.
When an ultrasonic sensor is enabled you can enter maximum detection distance in centimeters separately for top and bottom sensors.
**Please note:** that using an HC-SR04 sensor, particularly when detecting echos at longer
distances creates delays in the WLED software, and _might_ introduce timing hickups in your animations or
a less responsive web interface. It is therefore advised to keep the detection distance as short as possible.
### Animation triggering through the API
Instead of stairs activation by one of the sensors, you can also trigger the animation through
the API. To simulate triggering the bottom sensor, use:
```bash
curl -X POST -H "Content-Type: application/json" \
-d '{"staircase":{"bottom-sensor":true}}' \
xxx.xxx.xxx.xxx/json/state
```
Likewise, to trigger the top sensor, use:
```bash
curl -X POST -H "Content-Type: application/json" \
-d '{"staircase":{"top-sensor":true}}' \
xxx.xxx.xxx.xxx/json/state
```
**MQTT**
You can publish a message with either `up` or `down` on topic `/swipe` to trigger animation.
You can also use `on` or `off` for enabling or disabling usermod.
Have fun with this usermod.<br/>
www.rolfje.com
Modifications @blazoncek
## Change log
2021-04
* Adaptation for runtime configuration.

View File

@@ -0,0 +1,5 @@
Usermod to allow WLED to receive via UDP port from RGB.NET (and therefore add as a device to be controlled within artemis on PC)
This is only a very simple code to support a single led strip, it does not support the full function of the RGB.NET sketch for esp8266 only what is needed to be used with Artemis. It will show as a ws281x device in artemis when you provide the correct hostname or ip. Artemis queries the number of LEDs via the web interface (/config) but communication to set the LEDs is all done via the UDP interface.
To install, copy the usermod.cpp file to wled00 folder and recompile

View File

@@ -0,0 +1,93 @@
/*
* RGB.NET (artemis) receiver
*
* This works via the UDP, http is not supported apart from reporting LED count
*
*
*/
#include "wled.h"
#include <WiFiUdp.h>
WiFiUDP UDP;
const unsigned int RGBNET_localUdpPort = 1872; // local port to listen on
unsigned char RGBNET_packet[770];
long lastTime = 0;
int delayMs = 10;
bool isRGBNETUDPEnabled;
void RGBNET_readValues() {
int RGBNET_packetSize = UDP.parsePacket();
if (RGBNET_packetSize) {
// receive incoming UDP packets
int sequenceNumber = UDP.read();
int channel = UDP.read();
//channel data is not used we only supports one channel
int len = UDP.read(RGBNET_packet, ledCount*3);
if(len==0){
return;
}
for (int i = 0; i < len; i=i+3) {
strip.setPixelColor(i/3, RGBNET_packet[i], RGBNET_packet[i+1], RGBNET_packet[i+2], 0);
}
//strip.show();
}
}
//update LED strip
void RGBNET_show() {
strip.show();
lastTime = millis();
}
//This function provides a json with info on the number of LEDs connected
// it is needed by artemis to know how many LEDs to display on the surface
void handleConfig(AsyncWebServerRequest *request)
{
String config = (String)"{\
\"channels\": [\
{\
\"channel\": 1,\
\"leds\": " + ledCount + "\
},\
{\
\"channel\": 2,\
\"leds\": " + "0" + "\
},\
{\
\"channel\": 3,\
\"leds\": " + "0" + "\
},\
{\
\"channel\": 4,\
\"leds\": " + "0" + "\
}\
]\
}";
request->send(200, "application/json", config);
}
void userSetup()
{
server.on("/config", HTTP_GET, [](AsyncWebServerRequest *request){
handleConfig(request);
});
}
void userConnected()
{
// new wifi, who dis?
UDP.begin(RGBNET_localUdpPort);
isRGBNETUDPEnabled = true;
}
void userLoop()
{
RGBNET_readValues();
if (millis()-lastTime > delayMs) {
RGBNET_show();
}
}

View File

@@ -0,0 +1,40 @@
Hello! I have written a v2 usermod for the BME280/BMP280 sensor based on the [existing v1 usermod](https://github.com/Aircoookie/WLED/blob/master/usermods/Wemos_D1_mini%2BWemos32_mini_shield/usermod_bme280.cpp). It is not just a refactor, there are many changes which I made to fit my use case, and I hope they will fit the use cases of others as well! Most notably, this usermod is *just* for the BME280 and does not control a display like in the v1 usermod designed for the WeMos shield.
- Requires libraries `BME280@~3.0.0` (by [finitespace](https://github.com/finitespace/BME280)) and `Wire`. Please add these under `lib_deps` in your `platform.ini` (or `platform_override.ini`).
- Data is published over MQTT so make sure you've enabled the MQTT sync interface.
- This usermod also writes to serial (GPIO1 on ESP8266). Please make sure nothing else listening on the serial TX pin of your board will get confused by log messages!
To enable, compile with `USERMOD_BME280` defined (i.e. `platformio_override.ini`)
```ini
build_flags =
${common.build_flags_esp8266}
-D USERMOD_BME280
```
or define `USERMOD_BME280` in `my_config.h`
```c++
#define USERMOD_BME280
```
Changes include:
- Adjustable measure intervals
- Temperature and pressure have separate intervals due to pressure not frequently changing at any constant altitude
- Adjustment of number of decimal places in published sensor values
- Separate adjustment for temperature, humidity and pressure values
- Values are rounded to the specified number of decimal places
- Pressure measured in units of hPa instead of Pa
- Calculation of heat index (apparent temperature) and dew point
- These, along with humidity measurements, are disabled if the sensor is a BMP280
- 16x oversampling of sensor during measurement
- Values are only published if they are different from the previous value
- Values are published on startup (continually until the MQTT broker acknowledges a successful publication)
Adjustments are made through preprocessor definitions at the start of the class definition.
MQTT topics are as follows:
Measurement type | MQTT topic
--- | ---
Temperature | `<deviceTopic>/temperature`
Humidity | `<deviceTopic>/humidity`
Pressure | `<deviceTopic>/pressure`
Heat index | `<deviceTopic>/heat_index`
Dew point | `<deviceTopic>/dew_point`

View File

@@ -0,0 +1,216 @@
#pragma once
#include "wled.h"
#include <Arduino.h>
#include <Wire.h>
#include <BME280I2C.h> // BME280 sensor
#include <EnvironmentCalculations.h> // BME280 extended measurements
class UsermodBME280 : public Usermod
{
private:
// User-defined configuration
#define Celsius // Show temperature mesaurement in Celcius. Comment out for Fahrenheit
#define TemperatureDecimals 1 // Number of decimal places in published temperaure values
#define HumidityDecimals 2 // Number of decimal places in published humidity values
#define PressureDecimals 2 // Number of decimal places in published pressure values
#define TemperatureInterval 5 // Interval to measure temperature (and humidity, dew point if available) in seconds
#define PressureInterval 300 // Interval to measure pressure in seconds
#define PublishAlways 0 // Publish values even when they have not changed
// Sanity checks
#if !defined(TemperatureDecimals) || TemperatureDecimals < 0
#define TemperatureDecimals 0
#endif
#if !defined(HumidityDecimals) || HumidityDecimals < 0
#define HumidityDecimals 0
#endif
#if !defined(PressureDecimals) || PressureDecimals < 0
#define PressureDecimals 0
#endif
#if !defined(TemperatureInterval) || TemperatureInterval < 0
#define TemperatureInterval 1
#endif
#if !defined(PressureInterval) || PressureInterval < 0
#define PressureInterval TemperatureInterval
#endif
#if !defined(PublishAlways)
#define PublishAlways 0
#endif
#ifdef ARDUINO_ARCH_ESP32 // ESP32 boards
uint8_t SCL_PIN = 22;
uint8_t SDA_PIN = 21;
#else // ESP8266 boards
uint8_t SCL_PIN = 5;
uint8_t SDA_PIN = 4;
//uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8
#endif
// BME280 sensor settings
BME280I2C::Settings settings{
BME280::OSR_X16, // Temperature oversampling x16
BME280::OSR_X16, // Humidity oversampling x16
BME280::OSR_X16, // Pressure oversampling x16
// Defaults
BME280::Mode_Forced,
BME280::StandbyTime_1000ms,
BME280::Filter_Off,
BME280::SpiEnable_False,
BME280I2C::I2CAddr_0x76 // I2C address. I2C specific. Default 0x76
};
BME280I2C bme{settings};
uint8_t sensorType;
// Measurement timers
long timer;
long lastTemperatureMeasure = 0;
long lastPressureMeasure = 0;
// Current sensor values
float sensorTemperature;
float sensorHumidity;
float sensorHeatIndex;
float sensorDewPoint;
float sensorPressure;
// Track previous sensor values
float lastTemperature;
float lastHumidity;
float lastHeatIndex;
float lastDewPoint;
float lastPressure;
// Store packet IDs of MQTT publications
uint16_t mqttTemperaturePub = 0;
uint16_t mqttPressurePub = 0;
void UpdateBME280Data(int SensorType)
{
float _temperature, _humidity, _pressure;
#ifdef Celsius
BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Celsius);
#else
BME280::TempUnit tempUnit(BME280::TempUnit_Fahrenheit);
EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Fahrenheit);
#endif
BME280::PresUnit presUnit(BME280::PresUnit_hPa);
bme.read(_pressure, _temperature, _humidity, tempUnit, presUnit);
sensorTemperature = _temperature;
sensorHumidity = _humidity;
sensorPressure = _pressure;
if (sensorType == 1)
{
sensorHeatIndex = EnvironmentCalculations::HeatIndex(_temperature, _humidity, envTempUnit);
sensorDewPoint = EnvironmentCalculations::DewPoint(_temperature, _humidity, envTempUnit);
}
}
public:
void setup()
{
Wire.begin(SDA_PIN, SCL_PIN);
if (!bme.begin())
{
sensorType = 0;
Serial.println("Could not find BME280I2C sensor!");
}
else
{
switch (bme.chipModel())
{
case BME280::ChipModel_BME280:
sensorType = 1;
Serial.println("Found BME280 sensor! Success.");
break;
case BME280::ChipModel_BMP280:
sensorType = 2;
Serial.println("Found BMP280 sensor! No Humidity available.");
break;
default:
sensorType = 0;
Serial.println("Found UNKNOWN sensor! Error!");
}
}
}
void loop()
{
// BME280 sensor MQTT publishing
// Check if sensor present and MQTT Connected, otherwise it will crash the MCU
if (sensorType != 0 && mqtt != nullptr)
{
// Timer to fetch new temperature, humidity and pressure data at intervals
timer = millis();
if (timer - lastTemperatureMeasure >= TemperatureInterval * 1000 || mqttTemperaturePub == 0)
{
lastTemperatureMeasure = timer;
UpdateBME280Data(sensorType);
float temperature = roundf(sensorTemperature * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals);
float humidity, heatIndex, dewPoint;
// If temperature has changed since last measure, create string populated with device topic
// from the UI and values read from sensor, then publish to broker
if (temperature != lastTemperature || PublishAlways)
{
String topic = String(mqttDeviceTopic) + "/temperature";
mqttTemperaturePub = mqtt->publish(topic.c_str(), 0, false, String(temperature, TemperatureDecimals).c_str());
}
lastTemperature = temperature; // Update last sensor temperature for next loop
if (sensorType == 1) // Only if sensor is a BME280
{
humidity = roundf(sensorHumidity * pow(10, HumidityDecimals)) / pow(10, HumidityDecimals);
heatIndex = roundf(sensorHeatIndex * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals);
dewPoint = roundf(sensorDewPoint * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals);
if (humidity != lastHumidity || PublishAlways)
{
String topic = String(mqttDeviceTopic) + "/humidity";
mqtt->publish(topic.c_str(), 0, false, String(humidity, HumidityDecimals).c_str());
}
if (heatIndex != lastHeatIndex || PublishAlways)
{
String topic = String(mqttDeviceTopic) + "/heat_index";
mqtt->publish(topic.c_str(), 0, false, String(heatIndex, TemperatureDecimals).c_str());
}
if (dewPoint != lastDewPoint || PublishAlways)
{
String topic = String(mqttDeviceTopic) + "/dew_point";
mqtt->publish(topic.c_str(), 0, false, String(dewPoint, TemperatureDecimals).c_str());
}
lastHumidity = humidity;
lastHeatIndex = heatIndex;
lastDewPoint = dewPoint;
}
}
if (timer - lastPressureMeasure >= PressureInterval * 1000 || mqttPressurePub == 0)
{
lastPressureMeasure = timer;
float pressure = roundf(sensorPressure * pow(10, PressureDecimals)) / pow(10, PressureDecimals);
if (pressure != lastPressure || PublishAlways)
{
String topic = String(mqttDeviceTopic) + "/pressure";
mqttPressurePub = mqtt->publish(topic.c_str(), 0, true, String(pressure, PressureDecimals).c_str());
}
lastPressure = pressure;
}
}
}
};

View File

@@ -0,0 +1,22 @@
; Options
; -------
; USERMOD_DHT - define this to have this user mod included wled00\usermods_list.cpp
; USERMOD_DHT_DHTTYPE - DHT model: 11, 21, 22 for DHT11, DHT21, or DHT22, defaults to 22/DHT22
; USERMOD_DHT_PIN - pin to which DTH is connected, defaults to Q2 pin on QuinLed Dig-Uno's board
; USERMOD_DHT_CELSIUS - define this to report temperatures in degrees celsious, otherwise fahrenheit will be reported
; USERMOD_DHT_MEASUREMENT_INTERVAL - the number of milliseconds between measurements, defaults to 60 seconds
; USERMOD_DHT_FIRST_MEASUREMENT_AT - the number of milliseconds after boot to take first measurement, defaults to 90 seconds
; USERMOD_DHT_STATS - For debug, report delay stats
[env:d1_mini_usermod_dht_C]
extends = env:d1_mini
build_flags = ${env:d1_mini.build_flags} -D USERMOD_DHT -D USERMOD_DHT_CELSIUS
lib_deps = ${env.lib_deps}
https://github.com/alwynallan/DHT_nonblocking
[env:custom32_LEDPIN_16_usermod_dht_C]
extends = env:custom32_LEDPIN_16
build_flags = ${env:custom32_LEDPIN_16.build_flags} -D USERMOD_DHT -D USERMOD_DHT_CELSIUS -D USERMOD_DHT_STATS
lib_deps = ${env.lib_deps}
https://github.com/alwynallan/DHT_nonblocking

41
usermods/DHT/readme.md Normal file
View File

@@ -0,0 +1,41 @@
# DHT Temperature/Humidity sensor usermod
This usermod will read from an attached DHT22 or DHT11 humidity and temperature sensor.
The sensor readings are displayed in the Info section of the web UI.
If sensor is not detected after a while (10 update intervals), this usermod will be disabled.
## Installation
Copy the example `platformio_override.ini` to the root directory. This file should be placed in the same directory as `platformio.ini`.
### Define Your Options
* `USERMOD_DHT` - define this to have this user mod included wled00\usermods_list.cpp
* `USERMOD_DHT_DHTTYPE` - DHT model: 11, 21, 22 for DHT11, DHT21, or DHT22, defaults to 22/DHT22
* `USERMOD_DHT_PIN` - pin to which DTH is connected, defaults to Q2 pin on QuinLed Dig-Uno's board
* `USERMOD_DHT_CELSIUS` - define this to report temperatures in degrees celsious, otherwise fahrenheit will be reported
* `USERMOD_DHT_MEASUREMENT_INTERVAL` - the number of milliseconds between measurements, defaults to 60 seconds
* `USERMOD_DHT_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 90 seconds
* `USERMOD_DHT_STATS` - For debug, report delay stats
## Project link
* [QuinLED-Dig-Uno](https://quinled.info/2018/09/15/quinled-dig-uno/) - Project link
### PlatformIO requirements
If you are using `platformio_override.ini`, you should be able to refresh the task list and see your custom task, for example `env:d1_mini_usermod_dht_C`. If not, you can add the libraries and dependencies into `platformio.ini` as you see fit.
## Change Log
2020-02-04
* Change default QuinLed pin to Q2
* Instead of trying to keep updates at constant cadence, space readings out by measurement interval; hope this helps to avoid occasional bursts of readings with errors
* Add some more (optional) stats
2020-02-03
* Due to poor readouts on ESP32 with previous DHT library, rewrote to use https://github.com/alwynallan/DHT_nonblocking
* The new library serializes/delays up to 5ms for the sensor readout
2020-02-02
* Created

216
usermods/DHT/usermod_dht.h Normal file
View File

@@ -0,0 +1,216 @@
#pragma once
#include "wled.h"
#include <dht_nonblocking.h>
// USERMOD_DHT_DHTTYPE:
// 11 // DHT 11
// 21 // DHT 21
// 22 // DHT 22 (AM2302), AM2321 *** default
#ifndef USERMOD_DHT_DHTTYPE
#define USERMOD_DHT_DHTTYPE 22
#endif
#if USERMOD_DHT_DHTTYPE == 11
#define DHTTYPE DHT_TYPE_11
#elif USERMOD_DHT_DHTTYPE == 21
#define DHTTYPE DHT_TYPE_21
#elif USERMOD_DHT_DHTTYPE == 22
#define DHTTYPE DHT_TYPE_22
#endif
// Connect pin 1 (on the left) of the sensor to +5V
// NOTE: If using a board with 3.3V logic like an Arduino Due connect pin 1
// to 3.3V instead of 5V!
// Connect pin 2 of the sensor to whatever your DHTPIN is
// NOTE: Pin defaults below are for QuinLed Dig-Uno's Q2 on the board
// Connect pin 4 (on the right) of the sensor to GROUND
// NOTE: If using a bare sensor (AM*), Connect a 10K resistor from pin 2
// (data) to pin 1 (power) of the sensor. DHT* boards have the pullup already
#ifdef USERMOD_DHT_PIN
#define DHTPIN USERMOD_DHT_PIN
#else
#ifdef ARDUINO_ARCH_ESP32
#define DHTPIN 21
#else //ESP8266 boards
#define DHTPIN 4
#endif
#endif
// the frequency to check sensor, 1 minute
#ifndef USERMOD_DHT_MEASUREMENT_INTERVAL
#define USERMOD_DHT_MEASUREMENT_INTERVAL 60000
#endif
// how many seconds after boot to take first measurement, 90 seconds
// 90 gives enough time to OTA update firmware if this crashses
#ifndef USERMOD_DHT_FIRST_MEASUREMENT_AT
#define USERMOD_DHT_FIRST_MEASUREMENT_AT 90000
#endif
// from COOLDOWN_TIME in dht_nonblocking.cpp
#define DHT_TIMEOUT_TIME 10000
DHT_nonblocking dht_sensor(DHTPIN, DHTTYPE);
class UsermodDHT : public Usermod {
private:
unsigned long nextReadTime = 0;
unsigned long lastReadTime = 0;
float humidity, temperature = 0;
bool initializing = true;
bool disabled = false;
#ifdef USERMOD_DHT_STATS
unsigned long nextResetStatsTime = 0;
uint16_t updates = 0;
uint16_t clean_updates = 0;
uint16_t errors = 0;
unsigned long maxDelay = 0;
unsigned long currentIteration = 0;
unsigned long maxIteration = 0;
#endif
public:
void setup() {
nextReadTime = millis() + USERMOD_DHT_FIRST_MEASUREMENT_AT;
lastReadTime = millis();
#ifdef USERMOD_DHT_STATS
nextResetStatsTime = millis() + 60*60*1000;
#endif
}
void loop() {
if (disabled) {
return;
}
if (millis() < nextReadTime) {
return;
}
#ifdef USERMOD_DHT_STATS
if (millis() >= nextResetStatsTime) {
nextResetStatsTime += 60*60*1000;
errors = 0;
updates = 0;
clean_updates = 0;
}
unsigned long dcalc = millis();
if (currentIteration == 0) {
currentIteration = millis();
}
#endif
float tempC;
if (dht_sensor.measure(&tempC, &humidity)) {
#ifdef USERMOD_DHT_CELSIUS
temperature = tempC;
#else
temperature = tempC * 9 / 5 + 32;
#endif
nextReadTime = millis() + USERMOD_DHT_MEASUREMENT_INTERVAL;
lastReadTime = millis();
initializing = false;
#ifdef USERMOD_DHT_STATS
unsigned long icalc = millis() - currentIteration;
if (icalc > maxIteration) {
maxIteration = icalc;
}
if (icalc > DHT_TIMEOUT_TIME) {
errors += icalc/DHT_TIMEOUT_TIME;
} else {
clean_updates += 1;
}
updates += 1;
currentIteration = 0;
#endif
}
#ifdef USERMOD_DHT_STATS
dcalc = millis() - dcalc;
if (dcalc > maxDelay) {
maxDelay = dcalc;
}
#endif
if (((millis() - lastReadTime) > 10*USERMOD_DHT_MEASUREMENT_INTERVAL)) {
disabled = true;
}
}
void addToJsonInfo(JsonObject& root) {
if (disabled) {
return;
}
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
JsonArray temp = user.createNestedArray("Temperature");
JsonArray hum = user.createNestedArray("Humidity");
#ifdef USERMOD_DHT_STATS
JsonArray next = user.createNestedArray("next");
if (nextReadTime >= millis()) {
next.add((nextReadTime - millis()) / 1000);
next.add(" sec until read");
} else {
next.add((millis() - nextReadTime) / 1000);
next.add(" sec active reading");
}
JsonArray last = user.createNestedArray("last");
last.add((millis() - lastReadTime) / 60000);
last.add(" min since read");
JsonArray err = user.createNestedArray("errors");
err.add(errors);
err.add(" Errors");
JsonArray upd = user.createNestedArray("updates");
upd.add(updates);
upd.add(" Updates");
JsonArray cupd = user.createNestedArray("cleanUpdates");
cupd.add(clean_updates);
cupd.add(" Updates");
JsonArray iter = user.createNestedArray("maxIter");
iter.add(maxIteration);
iter.add(" ms");
JsonArray delay = user.createNestedArray("maxDelay");
delay.add(maxDelay);
delay.add(" ms");
#endif
if (initializing) {
// if we haven't read the sensor yet, let the user know
// that we are still waiting for the first measurement
temp.add((nextReadTime - millis()) / 1000);
temp.add(" sec until read");
hum.add((nextReadTime - millis()) / 1000);
hum.add(" sec until read");
return;
}
hum.add(humidity);
hum.add("%");
temp.add(temperature);
#ifdef USERMOD_DHT_CELSIUS
temp.add("°C");
#else
temp.add("°F");
#endif
}
uint16_t getId()
{
return USERMOD_ID_DHT;
}
};

View File

@@ -23,8 +23,12 @@
//class name. Use something descriptive and leave the ": public Usermod" part :)
class MyExampleUsermod : public Usermod {
private:
// sample usermod default value for variable (you can also use constructor)
int userVar0 = 42;
//Private class members. You can declare variables and functions only accessible to your usermod here
unsigned long lastTime = 0;
public:
//Functions called by WLED
@@ -133,11 +137,21 @@ class MyExampleUsermod : public Usermod {
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
*
* Return true in case your config was complete, or false if you'd like WLED to save your defaults to disk
*
* This function is guaranteed to be called on boot, but could also be called every time settings are updated
*/
void readFromConfig(JsonObject& root)
bool readFromConfig(JsonObject& root)
{
JsonObject top = root["top"];
userVar0 = top["great"] | 42; //The value right of the pipe "|" is the default value in case your setting was not present in cfg.json (e.g. first boot)
//set defaults for variables when declaring the variable (class definition or constructor)
JsonObject top = root["exampleUsermod"];
if (!top.isNull()) return false;
userVar0 = top["great"] | userVar0;
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return true;
}

View File

@@ -0,0 +1,70 @@
#ifndef CHIP_SELECT_H
#define CHIP_SELECT_H
#include "Hardware.h"
/*
* `digit`s are as defined in Hardware.h, 0 == seconds ones, 5 == hours tens.
*/
class ChipSelect {
private:
uint8_t digits_map;
const uint8_t all_on = 0x3F;
const uint8_t all_off = 0x00;
public:
ChipSelect() : digits_map(all_off) {}
void update() {
// Documented in README.md. Q7 and Q6 are unused. Q5 is Seconds Ones, Q0 is Hours Tens.
// Q7 is the first bit written, Q0 is the last. So we push two dummy bits, then start with
// Seconds Ones and end with Hours Tens.
// CS is Active Low, but digits_map is 1 for enable, 0 for disable. So we bit-wise NOT first.
uint8_t to_shift = (~digits_map) << 2;
digitalWrite(CSSR_LATCH_PIN, LOW);
shiftOut(CSSR_DATA_PIN, CSSR_CLOCK_PIN, LSBFIRST, to_shift);
digitalWrite(CSSR_LATCH_PIN, HIGH);
}
void begin()
{
pinMode(CSSR_LATCH_PIN, OUTPUT);
pinMode(CSSR_DATA_PIN, OUTPUT);
pinMode(CSSR_CLOCK_PIN, OUTPUT);
digitalWrite(CSSR_DATA_PIN, LOW);
digitalWrite(CSSR_CLOCK_PIN, LOW);
digitalWrite(CSSR_LATCH_PIN, LOW);
update();
}
// These speak the indexes defined in Hardware.h.
// So 0 is disabled, 1 is enabled (even though CS is active low, this gets mapped.)
// So bit 0 (LSB), is index 0, is SECONDS_ONES
// Translation to what the 74HC595 uses is done in update()
void setDigitMap(uint8_t map, bool update_=true) { digits_map = map; if (update_) update(); }
uint8_t getDigitMap() { return digits_map; }
// Helper functions
// Sets just the one digit by digit number
void setDigit(uint8_t digit, bool update_=true) { setDigitMap(0x01 << digit, update_); }
void setAll(bool update_=true) { setDigitMap(all_on, update_); }
void clear(bool update_=true) { setDigitMap(all_off, update_); }
void setSecondsOnes() { setDigit(SECONDS_ONES); }
void setSecondsTens() { setDigit(SECONDS_TENS); }
void setMinutesOnes() { setDigit(MINUTES_ONES); }
void setMinutesTens() { setDigit(MINUTES_TENS); }
void setHoursOnes() { setDigit(HOURS_ONES); }
void setHoursTens() { setDigit(HOURS_TENS); }
bool isSecondsOnes() { return (digits_map&SECONDS_ONES_MAP > 0); }
bool isSecondsTens() { return (digits_map&SECONDS_TENS_MAP > 0); }
bool isMinutesOnes() { return (digits_map&MINUTES_ONES_MAP > 0); }
bool isMinutesTens() { return (digits_map&MINUTES_TENS_MAP > 0); }
bool isHoursOnes() { return (digits_map&HOURS_ONES_MAP > 0); }
bool isHoursTens() { return (digits_map&HOURS_TENS_MAP > 0); }
};
#endif // CHIP_SELECT_H

View File

@@ -0,0 +1,52 @@
/*
* Define the hardware for the EleksTube IPS clock. Mostly pin definitions
*/
#ifndef ELEKSTUBEHAX_HARDWARE_H
#define ELEKSTUBEHAX_HARDWARE_H
#include <stdint.h>
#include <Arduino.h> // for HIGH and LOW
// Common indexing scheme, used to identify the digit
#define SECONDS_ONES (0)
#define SECONDS_TENS (1)
#define MINUTES_ONES (2)
#define MINUTES_TENS (3)
#define HOURS_ONES (4)
#define HOURS_TENS (5)
#define NUM_DIGITS (6)
#define SECONDS_ONES_MAP (0x01 << SECONDS_ONES)
#define SECONDS_TENS_MAP (0x01 << SECONDS_TENS)
#define MINUTES_ONES_MAP (0x01 << MINUTES_ONES)
#define MINUTES_TENS_MAP (0x01 << MINUTES_TENS)
#define HOURS_ONES_MAP (0x01 << HOURS_ONES)
#define HOURS_TENS_MAP (0x01 << HOURS_TENS)
// WS2812 (or compatible) LEDs on the back of the display modules.
#define BACKLIGHTS_PIN (12)
// Buttons, active low, externally pulled up (with actual resistors!)
#define BUTTON_LEFT_PIN (33)
#define BUTTON_MODE_PIN (32)
#define BUTTON_RIGHT_PIN (35)
#define BUTTON_POWER_PIN (34)
// I2C to DS3231 RTC.
#define RTC_SCL_PIN (22)
#define RTC_SDA_PIN (21)
// Chip Select shift register, to select the display
#define CSSR_DATA_PIN (14)
#define CSSR_CLOCK_PIN (16)
#define CSSR_LATCH_PIN (17)
// SPI to displays
// DEFINED IN User_Setup.h
// Look for: TFT_MOSI, TFT_SCLK, TFT_CS, TFT_DC, and TFT_RST
// Power for all TFT displays are grounded through a MOSFET so they can all be turned off.
// Active HIGH.
#define TFT_ENABLE_PIN (27)
#endif // ELEKSTUBEHAX_HARDWARE_H

View File

@@ -0,0 +1,218 @@
#ifndef TFTS_H
#define TFTS_H
#include "wled.h"
#include <FS.h>
#include <TFT_eSPI.h>
#include "Hardware.h"
#include "ChipSelect.h"
class TFTs : public TFT_eSPI {
private:
uint8_t digits[NUM_DIGITS];
// These read 16- and 32-bit types from the SD card file.
// BMP data is stored little-endian, Arduino is little-endian too.
// May need to reverse subscript order if porting elsewhere.
uint16_t read16(fs::File &f) {
uint16_t result;
((uint8_t *)&result)[0] = f.read(); // LSB
((uint8_t *)&result)[1] = f.read(); // MSB
return result;
}
uint32_t read32(fs::File &f) {
uint32_t result;
((uint8_t *)&result)[0] = f.read(); // LSB
((uint8_t *)&result)[1] = f.read();
((uint8_t *)&result)[2] = f.read();
((uint8_t *)&result)[3] = f.read(); // MSB
return result;
}
uint16_t output_buffer[TFT_HEIGHT][TFT_WIDTH];
// These BMP functions are stolen directly from the TFT_SPIFFS_BMP example in the TFT_eSPI library.
// Unfortunately, they aren't part of the library itself, so I had to copy them.
// I've modified drawBmp to buffer the whole image at once instead of doing it line-by-line.
//// BEGIN STOLEN CODE
// Draw directly from file stored in RGB565 format
bool drawBin(const char *filename) {
fs::File bmpFS;
// Open requested file on SD card
bmpFS = WLED_FS.open(filename, "r");
if (!bmpFS)
{
Serial.print(F("File not found: "));
Serial.println(filename);
return(false);
}
size_t sz = bmpFS.size();
if (sz <= 64800)
{
bool oldSwapBytes = getSwapBytes();
setSwapBytes(true);
int16_t h = sz / (135 * 2);
//draw img that is shorter than 240pix into the center
int16_t y = (height() - h) /2;
bmpFS.read((uint8_t *) output_buffer,sz);
if (!realtimeMode || realtimeOverride) strip.service();
pushImage(0, y, 135, h, (uint16_t *)output_buffer);
setSwapBytes(oldSwapBytes);
}
bmpFS.close();
return(true);
}
bool drawBmp(const char *filename) {
fs::File bmpFS;
// Open requested file on SD card
bmpFS = WLED_FS.open(filename, "r");
if (!bmpFS)
{
Serial.print(F("File not found: "));
Serial.println(filename);
return(false);
}
uint32_t seekOffset;
int16_t w, h, row;
uint8_t r, g, b;
uint16_t magic = read16(bmpFS);
if (magic == 0xFFFF) {
Serial.println(F("BMP not found!"));
bmpFS.close();
return(false);
}
if (magic != 0x4D42) {
Serial.print(F("File not a BMP. Magic: "));
Serial.println(magic);
bmpFS.close();
return(false);
}
read32(bmpFS);
read32(bmpFS);
seekOffset = read32(bmpFS);
read32(bmpFS);
w = read32(bmpFS);
h = read32(bmpFS);
if ((read16(bmpFS) != 1) || (read16(bmpFS) != 24) || (read32(bmpFS) != 0)) {
Serial.println(F("BMP format not recognized."));
bmpFS.close();
return(false);
}
//draw img that is shorter than 240pix into the center
int16_t y = (height() - h) /2;
bool oldSwapBytes = getSwapBytes();
setSwapBytes(true);
bmpFS.seek(seekOffset);
uint16_t padding = (4 - ((w * 3) & 3)) & 3;
uint8_t lineBuffer[w * 3 + padding];
uint8_t serviceStrip = (!realtimeMode || realtimeOverride) ? 7 : 0;
// row is decremented as the BMP image is drawn bottom up
for (row = h-1; row >= 0; row--) {
if ((row & 0b00000111) == serviceStrip) strip.service(); //still refresh backlight to mitigate stutter every few rows
bmpFS.read(lineBuffer, sizeof(lineBuffer));
uint8_t* bptr = lineBuffer;
// Convert 24 to 16 bit colours while copying to output buffer.
for (uint16_t col = 0; col < w; col++)
{
b = *bptr++;
g = *bptr++;
r = *bptr++;
output_buffer[row][col] = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}
}
pushImage(0, y, w, h, (uint16_t *)output_buffer);
setSwapBytes(oldSwapBytes);
bmpFS.close();
return(true);
}
public:
TFTs() : TFT_eSPI(), chip_select()
{ for (uint8_t digit=0; digit < NUM_DIGITS; digit++) digits[digit] = 0; }
// no == Do not send to TFT. yes == Send to TFT if changed. force == Send to TFT.
enum show_t { no, yes, force };
// A digit of 0xFF means blank the screen.
const static uint8_t blanked = 255;
void begin() {
pinMode(TFT_ENABLE_PIN, OUTPUT);
digitalWrite(TFT_ENABLE_PIN, HIGH); //enable displays on boot
// Start with all displays selected.
chip_select.begin();
chip_select.setAll();
// Initialize the super class.
init();
}
void showDigit(uint8_t digit) {
chip_select.setDigit(digit);
if (digits[digit] == blanked) {
fillScreen(TFT_BLACK);
}
else {
// Filenames are no bigger than "255.bmp\0"
char file_name[10];
sprintf(file_name, "/%d.bmp", digits[digit]);
if (WLED_FS.exists(file_name)) {
drawBmp(file_name);
} else {
sprintf(file_name, "/%d.bin", digits[digit]);
drawBin(file_name);
}
}
}
void setDigit(uint8_t digit, uint8_t value, show_t show=yes) {
uint8_t old_value = digits[digit];
digits[digit] = value;
if (show != no && (old_value != value || show == force)) {
showDigit(digit);
}
}
uint8_t getDigit(uint8_t digit) { return digits[digit]; }
void showAllDigits() { for (uint8_t digit=0; digit < NUM_DIGITS; digit++) showDigit(digit); }
// Making chip_select public so we don't have to proxy all methods, and the caller can just use it directly.
ChipSelect chip_select;
};
#endif // TFTS_H

View File

@@ -0,0 +1,47 @@
/*
* This is intended to over-ride `User_Setup.h` that comes with the TFT_eSPI library.
* I hate having to modify the library code.
*/
// ST7789 135 x 240 display with no chip select line
#define ST7789_DRIVER // Configure all registers
#define TFT_WIDTH 135
#define TFT_HEIGHT 240
#define CGRAM_OFFSET // Library will add offsets required
//#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue
//#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red
//#define TFT_INVERSION_ON
//#define TFT_INVERSION_OFF
// EleksTube IPS
#define TFT_SDA_READ // Read and write on the MOSI/SDA pin, no separate MISO pin
#define TFT_MOSI 23
#define TFT_SCLK 18
//#define TFT_CS -1 // Not connected
#define TFT_DC 25 // Data Command, aka Register Select or RS
#define TFT_RST 26 // Connect reset to ensure display initialises
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
//#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
//#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
//#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
//#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:.
//#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT
//#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
//#define SMOOTH_FONT
//#define SPI_FREQUENCY 27000000
#define SPI_FREQUENCY 40000000
/*
* To make the Library not over-write all this:
*/
#define USER_SETUP_LOADED

View File

@@ -0,0 +1,31 @@
# EleksTube IPS Clock usermod
This usermod allows WLED to run on the EleksTube IPS clock.
It enables running all WLED effects on the background SK6812 lighting, while displaying digit bitmaps on the 6 IPS screens.
Code is largely based on https://github.com/SmittyHalibut/EleksTubeHAX by Mark Smith!
Supported:
- Display with custom bitmaps or raw RGB565 images (.bin) from filesystem
- Background lighting
- Power button
- RTC (with RTC usermod)
- Standard WLED time features (NTP, DST, timezones)
Not supported:
- 3 navigation buttons, on-device setup
Your images must be exactly 135 pixels wide and 1-240 pixels high.
## Installation
Compile and upload to clock using the `elekstube_ips` PlatformIO environment
Once uploaded (the clock can be flashed like any ESP32 module), go to `[WLED-IP]/edit` and upload the 0-9.bin files from [here](https://github.com/Aircoookie/NixieThemes/tree/master/themes/RealisticNixie/bin).
You can find more clockfaces in the [NixieThemes](https://github.com/Aircoookie/NixieThemes/) repo.
Use LED pin 12, relay pin 27 and button pin 34.
## Use of RGB565 images
Binary 16-bit per pixel RGB565 format `.bin` images are now supported. This has the benefit of only using 2/3rds of the file size a `.bmp` has.
The drawback is that this format cannot be handled by common image programs and that an extra conversion step is needed.
You can use https://lvgl.io/tools/imageconverter to convert your .bmp to a .bin file (settings `True color` and `Binary RGB565`)
Thank you to @RedNax67 for adding .bin support.

View File

@@ -0,0 +1,60 @@
#pragma once
#include "TFTs.h"
#include "wled.h"
//Large parts of the code are from https://github.com/SmittyHalibut/EleksTubeHAX
class ElekstubeIPSUsermod : public Usermod {
private:
TFTs tfts;
void updateClockDisplay(TFTs::show_t show=TFTs::yes) {
bool set[6] = {false};
for (uint8_t i = 0; i<6; i++) {
char c = cronixieDisplay[i];
if (c >= '0' && c <= '9') {
tfts.setDigit(5-i, c - '0', show); set[i] = true;
} else if (c >= 'A' && c <= 'G') {
tfts.setDigit(5-i, c - 'A' + 10, show); set[i] = true; //10.bmp to 16.bmp static display
} else if (c == '-' || c == '_' || c == ' ') {
tfts.setDigit(5-i, 255, show); set[i] = true; //blank
} else {
set[i] = false; //display HHMMSS time
}
}
uint8_t hr = hour(localTime);
uint8_t hrTens = hr/10;
uint8_t mi = minute(localTime);
uint8_t mittens = mi/10;
uint8_t s = second(localTime);
uint8_t sTens = s/10;
if (!set[0]) tfts.setDigit(HOURS_TENS, hrTens, show);
if (!set[1]) tfts.setDigit(HOURS_ONES, hr - hrTens*10, show);
if (!set[2]) tfts.setDigit(MINUTES_TENS, mittens, show);
if (!set[3]) tfts.setDigit(MINUTES_ONES, mi - mittens*10, show);
if (!set[4]) tfts.setDigit(SECONDS_TENS, sTens, show);
if (!set[5]) tfts.setDigit(SECONDS_ONES, s - sTens*10, show);
}
unsigned long lastTime = 0;
public:
void setup() {
tfts.begin();
tfts.fillScreen(TFT_BLACK);
for (int8_t i = 5; i >= 0; i--) {
tfts.setDigit(i, 255, TFTs::force); //turn all off
}
}
void loop() {
if (toki.isTick()) {
updateLocalTime();
updateClockDisplay();
}
}
uint16_t getId()
{
return USERMOD_ID_ELEKSTUBE_IPS;
}
};

View File

@@ -149,11 +149,14 @@ Delay <input type=\"number\" min=\"5\" max=\"300\" value=\"";
/**
* restore the changeable values
*/
void readFromConfig(JsonObject &root)
bool readFromConfig(JsonObject &root)
{
JsonObject top = root["FixUnreachableNetServices"];
if (top.isNull()) return false;
m_pingDelayMs = top["PingDelayMs"] | m_pingDelayMs;
m_pingDelayMs = max(5000UL, min(18000000UL, m_pingDelayMs));
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return true;
}
/**

View File

@@ -9,28 +9,13 @@ The LED strip is switched [using a relay](https://github.com/Aircoookie/WLED/wik
## Webinterface
The info page in the web interface shows the items below
- the state of the sensor. By clicking on the state the sensor can be deactivated/activated. Changes persist after a reboot.
**I recommend to deactivate the sensor before an OTA update and activate it again afterwards**.
- the remaining time of the off timer.
## JSON API
The usermod supports the following state changes:
| JSON key | Value range | Description |
|------------|-------------|---------------------------------|
| PIRenabled | bool | Deactivdate/activate the sensor |
| PIRoffSec | 60 to 43200 | Off timer seconds |
Changes also persist after a reboot.
The info page in the web interface shows the remaining time of the off timer.
## Sensor connection
My setup uses an HC-SR501 sensor, a HC-SR505 should also work.
The usermod uses GPIO13 (D1 mini pin D7) for the sensor signal.
The usermod uses GPIO13 (D1 mini pin D7) by default for the sensor signal but can be changed in the Usermod settings page.
[This example page](http://www.esp8266learning.com/wemos-mini-pir-sensor-example.php) describes how to connect the sensor.
Use the potentiometers on the sensor to set the time-delay to the minimum and the sensitivity to about half, or slightly above.
@@ -76,9 +61,10 @@ void registerUsermods()
## API to enable/disable the PIR sensor from outside. For example from another usermod.
The class provides the static method `PIRsensorSwitch* PIRsensorSwitch::GetInstance()` to get a pointer to the usermod object.
To query or change the PIR sensor state the methods `bool PIRsensorEnabled()` and `void EnablePIRsensor(bool enable)` are available.
To query or change the PIR sensor state the methods `bool PIRsensorEnabled()` and `void EnablePIRsensor(bool enable)` are available.
When the PIR sensor state changes an MQTT message is broadcasted with topic `wled/deviceMAC/motion` and message `on` or `off`.
Usermod can also be configured to just send MQTT message and not change WLED state using settings page as well as responding to motion only during nighttime (assuming NTP and lattitude/longitude are set to determine sunrise/sunset times).
### There are two options to get access to the usermod instance:
@@ -98,12 +84,19 @@ class MyUsermod : public Usermod {
//...
void togglePIRSensor() {
if (PIRsensorSwitch::GetInstance() != nullptr) {
PIRsensorSwitch::GetInstance()->EnablePIRsensor(!PIRsensorSwitch::GetInstance()->PIRsensorEnabled());
#ifdef USERMOD_PIR_SENSOR_SWITCH
PIRsensorSwitch *PIRsensor = (PIRsensorSwitch::*) usermods.lookup(USERMOD_ID_PIRSWITCH);
if (PIRsensor != nullptr) {
PIRsensor->EnablePIRsensor(!PIRsensor->PIRsensorEnabled());
}
#endif
}
//...
};
```
Have fun - @gegu
## Change log
2021-04
* Adaptation for runtime configuration.

View File

@@ -2,6 +2,15 @@
#include "wled.h"
#ifndef PIR_SENSOR_PIN
// compatible with QuinLED-Dig-Uno
#ifdef ARDUINO_ARCH_ESP32
#define PIR_SENSOR_PIN 23 // Q4
#else //ESP8266 boards
#define PIR_SENSOR_PIN 13 // Q4 (D7 on D1 mini)
#endif
#endif
/*
* This usermod handles PIR sensor states.
* The strip will be switched on and the off timer will be resetted when the sensor goes HIGH.
@@ -30,104 +39,128 @@ public:
/**
* constructor
*/
PIRsensorSwitch()
{
// set static instance pointer
PIRsensorSwitchInstance(this);
}
PIRsensorSwitch() {}
/**
* desctructor
*/
~PIRsensorSwitch()
{
PIRsensorSwitchInstance(nullptr, true);
;
}
/**
* return the instance pointer of the class
*/
static PIRsensorSwitch *GetInstance() { return PIRsensorSwitchInstance(); }
~PIRsensorSwitch() {}
/**
* Enable/Disable the PIR sensor
*/
void EnablePIRsensor(bool enable) { m_PIRenabled = enable; }
void EnablePIRsensor(bool en) { enabled = en; }
/**
* Get PIR sensor enabled/disabled state
*/
bool PIRsensorEnabled() { return m_PIRenabled; }
bool PIRsensorEnabled() { return enabled; }
private:
// PIR sensor pin
const uint8_t PIRsensorPin = 13; // D7 on D1 mini
int8_t PIRsensorPin = PIR_SENSOR_PIN;
// notification mode for colorUpdated()
const byte NotifyUpdateMode = NOTIFIER_CALL_MODE_NO_NOTIFY; // NOTIFIER_CALL_MODE_DIRECT_CHANGE
// delay before switch off after the sensor state goes LOW
uint32_t m_switchOffDelay = 600000;
uint32_t m_switchOffDelay = 600000; // 10min
// off timer start time
uint32_t m_offTimerStart = 0;
// current PIR sensor pin state
byte m_PIRsensorPinState = LOW;
// PIR sensor enabled - ISR attached
bool m_PIRenabled = true;
// state if serializeConfig() should be called
bool m_updateConfig = false;
byte sensorPinState = LOW;
// PIR sensor enabled
bool enabled = true;
// status of initialisation
bool initDone = false;
// on and off presets
uint8_t m_onPreset = 0;
uint8_t m_offPreset = 0;
// flag to indicate that PIR sensor should activate WLED during nighttime only
bool m_nightTimeOnly = false;
// flag to send MQTT message only (assuming it is enabled)
bool m_mqttOnly = false;
unsigned long lastLoop = 0;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _switchOffDelay[];
static const char _enabled[];
static const char _onPreset[];
static const char _offPreset[];
static const char _nightTime[];
static const char _mqttOnly[];
/**
* return or change if new PIR sensor state is available
* check if it is daytime
* if sunrise/sunset is not defined (no NTP or lat/lon) default to nighttime
*/
static volatile bool newPIRsensorState(bool changeState = false, bool newState = false);
bool isDayTime() {
bool isDayTime = false;
updateLocalTime();
uint8_t hr = hour(localTime);
uint8_t mi = minute(localTime);
/**
* PIR sensor state has changed
*/
static void IRAM_ATTR ISR_PIRstateChange();
/**
* Set/get instance pointer
*/
static PIRsensorSwitch *PIRsensorSwitchInstance(PIRsensorSwitch *pInstance = nullptr, bool bRemoveInstance = false);
if (sunrise && sunset) {
if (hour(sunrise)<hr && hour(sunset)>hr) {
isDayTime = true;
} else {
if (hour(sunrise)==hr && minute(sunrise)<mi) {
isDayTime = true;
}
if (hour(sunset)==hr && minute(sunset)>mi) {
isDayTime = true;
}
}
}
return isDayTime;
}
/**
* switch strip on/off
*/
void switchStrip(bool switchOn)
{
if (switchOn && bri == 0)
{
if (switchOn && m_onPreset) {
applyPreset(m_onPreset);
} else if (!switchOn && m_offPreset) {
applyPreset(m_offPreset);
} else if (switchOn && bri == 0) {
bri = briLast;
colorUpdated(NotifyUpdateMode);
}
else if (!switchOn && bri != 0)
{
} else if (!switchOn && bri != 0) {
briLast = bri;
bri = 0;
colorUpdated(NotifyUpdateMode);
}
}
void publishMqtt(const char* state)
{
//Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED){
char subuf[64];
strcpy(subuf, mqttDeviceTopic);
strcat_P(subuf, PSTR("/motion"));
mqtt->publish(subuf, 0, false, state);
}
}
/**
* Read and update PIR sensor state.
* Initilize/reset switch off timer
*/
bool updatePIRsensorState()
{
if (newPIRsensorState())
{
m_PIRsensorPinState = digitalRead(PIRsensorPin);
bool pinState = digitalRead(PIRsensorPin);
if (pinState != sensorPinState) {
sensorPinState = pinState; // change previous state
if (m_PIRsensorPinState == HIGH)
{
if (sensorPinState == HIGH) {
m_offTimerStart = 0;
switchStrip(true);
}
else if (bri != 0)
{
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true);
publishMqtt("on");
} else /*if (bri != 0)*/ {
// start switch off timer
m_offTimerStart = millis();
}
newPIRsensorState(true, false);
return true;
}
return false;
@@ -140,9 +173,10 @@ private:
{
if (m_offTimerStart > 0 && millis() - m_offTimerStart > m_switchOffDelay)
{
if (m_PIRenabled == true)
if (enabled == true)
{
switchStrip(false);
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(false);
publishMqtt("off");
}
m_offTimerStart = 0;
return true;
@@ -159,13 +193,19 @@ public:
*/
void setup()
{
// PIR Sensor mode INPUT_PULLUP
pinMode(PIRsensorPin, INPUT_PULLUP);
if (m_PIRenabled)
{
// assign interrupt function and set CHANGE mode
attachInterrupt(digitalPinToInterrupt(PIRsensorPin), ISR_PIRstateChange, CHANGE);
// pin retrieved from cfg.json (readFromConfig()) prior to running setup()
if (!pinManager.allocatePin(PIRsensorPin,false)) {
PIRsensorPin = -1; // allocation failed
enabled = false;
DEBUG_PRINTLN(F("PIRSensorSwitch pin allocation failed."));
} else {
// PIR Sensor mode INPUT_PULLUP
pinMode(PIRsensorPin, INPUT_PULLUP);
if (enabled) {
sensorPinState = digitalRead(PIRsensorPin);
}
}
initDone = true;
}
/**
@@ -181,14 +221,12 @@ public:
*/
void loop()
{
if (!updatePIRsensorState())
{
// only check sensors 10x/s
if (millis() - lastLoop < 100 || strip.isUpdating()) return;
lastLoop = millis();
if (!updatePIRsensorState()) {
handleOffTimer();
if (m_updateConfig)
{
serializeConfig();
m_updateConfig = false;
}
}
}
@@ -199,132 +237,145 @@ public:
*/
void addToJsonInfo(JsonObject &root)
{
//this code adds "u":{"&#x23F2; PIR sensor state":uiDomString} to the info object
// the value contains a button to toggle the sensor enabled/disabled
JsonObject user = root["u"];
if (user.isNull())
user = root.createNestedObject("u");
if (user.isNull()) user = root.createNestedObject("u");
JsonArray infoArr = user.createNestedArray("&#x23F2; PIR sensor state"); //name
String uiDomString = "<button class=\"btn infobtn\" onclick=\"requestJson({PIRenabled:";
String sensorStateInfo;
// PIR sensor state
if (m_PIRenabled)
if (enabled)
{
uiDomString += "false";
sensorStateInfo = (m_PIRsensorPinState != LOW ? "active" : "inactive"); //value
}
else
{
uiDomString += "true";
sensorStateInfo = "Disabled !";
}
uiDomString += "});return false;\">";
uiDomString += sensorStateInfo;
uiDomString += "</button>";
infoArr.add(uiDomString); //value
//this code adds "u":{"&#x23F2; switch off timer":uiDomString} to the info object
uiDomString = "&#x23F2; switch off timer<span style=\"display:block;padding-left:25px;\">\
after <input type=\"number\" min=\"1\" max=\"720\" value=\"";
uiDomString += (m_switchOffDelay / 60000);
uiDomString += "\" onchange=\"requestJson({PIRoffSec:parseInt(this.value)*60});\">min</span>";
infoArr = user.createNestedArray(uiDomString); //name
// off timer
if (m_offTimerStart > 0)
{
uiDomString = "";
unsigned int offSeconds = (m_switchOffDelay - (millis() - m_offTimerStart)) / 1000;
if (offSeconds >= 3600)
// off timer
String uiDomString = F("PIR <i class=\"icons\">&#xe325;</i>");
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
if (m_offTimerStart > 0)
{
uiDomString += (offSeconds / 3600);
uiDomString += " hours ";
offSeconds %= 3600;
uiDomString = "";
unsigned int offSeconds = (m_switchOffDelay - (millis() - m_offTimerStart)) / 1000;
if (offSeconds >= 3600)
{
uiDomString += (offSeconds / 3600);
uiDomString += F("h ");
offSeconds %= 3600;
}
if (offSeconds >= 60)
{
uiDomString += (offSeconds / 60);
offSeconds %= 60;
}
else if (uiDomString.length() > 0)
{
uiDomString += 0;
}
if (uiDomString.length() > 0)
{
uiDomString += F("min ");
}
uiDomString += (offSeconds);
infoArr.add(uiDomString + F("s"));
} else {
infoArr.add(sensorPinState ? F("sensor on") : F("inactive"));
}
if (offSeconds >= 60)
{
uiDomString += (offSeconds / 60);
offSeconds %= 60;
}
else if (uiDomString.length() > 0)
{
uiDomString += 0;
}
if (uiDomString.length() > 0)
{
uiDomString += " min ";
}
uiDomString += (offSeconds);
infoArr.add(uiDomString + " sec");
}
else
{
infoArr.add("inactive");
} else {
String uiDomString = F("PIR sensor");
JsonArray infoArr = user.createNestedArray(uiDomString);
infoArr.add(F("disabled"));
}
}
/**
* 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
* Add "PIRenabled" to json state. This can be used to disable/enable the sensor.
* Add "PIRoffSec" to json state. This can be used to adjust <m_switchOffDelay> milliseconds.
*/
/*
void addToJsonState(JsonObject &root)
{
root["PIRenabled"] = m_PIRenabled;
root["PIRoffSec"] = (m_switchOffDelay / 1000);
}
*/
/**
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
* Read "PIRenabled" from json state and switch enable/disable the PIR sensor.
* Read "PIRoffSec" from json state and adjust <m_switchOffDelay> milliseconds.
*/
/*
void readFromJsonState(JsonObject &root)
{
if (root["PIRoffSec"] != nullptr)
{
m_switchOffDelay = (1000 * max(60UL, min(43200UL, root["PIRoffSec"].as<unsigned long>())));
m_updateConfig = true;
}
if (root["PIRenabled"] != nullptr)
{
if (root["PIRenabled"] && !m_PIRenabled)
{
attachInterrupt(digitalPinToInterrupt(PIRsensorPin), ISR_PIRstateChange, CHANGE);
newPIRsensorState(true, true);
}
else if (m_PIRenabled)
{
detachInterrupt(PIRsensorPin);
}
m_PIRenabled = root["PIRenabled"];
m_updateConfig = true;
}
}
*/
/**
* provide the changeable values
*/
void addToConfig(JsonObject &root)
{
JsonObject top = root.createNestedObject("PIRsensorSwitch");
top["PIRenabled"] = m_PIRenabled;
top["PIRoffSec"] = m_switchOffDelay;
JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled;
top[FPSTR(_switchOffDelay)] = m_switchOffDelay / 1000;
top["pin"] = PIRsensorPin;
top[FPSTR(_onPreset)] = m_onPreset;
top[FPSTR(_offPreset)] = m_offPreset;
top[FPSTR(_nightTime)] = m_nightTimeOnly;
top[FPSTR(_mqttOnly)] = m_mqttOnly;
DEBUG_PRINTLN(F("PIR config saved."));
}
/**
* restore the changeable values
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
*
* The function should return true if configuration was successfully loaded or false if there was no configuration.
*/
void readFromConfig(JsonObject &root)
bool readFromConfig(JsonObject &root)
{
JsonObject top = root["PIRsensorSwitch"];
m_PIRenabled = (top["PIRenabled"] != nullptr ? top["PIRenabled"] : true);
m_switchOffDelay = top["PIRoffSec"] | m_switchOffDelay;
bool oldEnabled = enabled;
int8_t oldPin = PIRsensorPin;
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
PIRsensorPin = top["pin"] | PIRsensorPin;
enabled = top[FPSTR(_enabled)] | enabled;
m_switchOffDelay = (top[FPSTR(_switchOffDelay)] | m_switchOffDelay/1000) * 1000;
m_onPreset = top[FPSTR(_onPreset)] | m_onPreset;
m_onPreset = max(0,min(250,(int)m_onPreset));
m_offPreset = top[FPSTR(_offPreset)] | m_offPreset;
m_offPreset = max(0,min(250,(int)m_offPreset));
m_nightTimeOnly = top[FPSTR(_nightTime)] | m_nightTimeOnly;
m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly;
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
// reading config prior to setup()
DEBUG_PRINTLN(F(" config loaded."));
} else {
if (oldPin != PIRsensorPin || oldEnabled != enabled) {
// check if pin is OK
if (oldPin != PIRsensorPin && oldPin >= 0) {
// if we are changing pin in settings page
// deallocate old pin
pinManager.deallocatePin(oldPin);
if (pinManager.allocatePin(PIRsensorPin,false)) {
pinMode(PIRsensorPin, INPUT_PULLUP);
} else {
// allocation failed
PIRsensorPin = -1;
enabled = false;
}
}
if (enabled) {
sensorPinState = digitalRead(PIRsensorPin);
}
}
DEBUG_PRINTLN(F(" config (re)loaded."));
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return true;
}
/**
@@ -337,30 +388,11 @@ after <input type=\"number\" min=\"1\" max=\"720\" value=\"";
}
};
//////////////////////////////////////////////////////
// PIRsensorSwitch static method implementations
volatile bool PIRsensorSwitch::newPIRsensorState(bool changeState, bool newState)
{
static volatile bool s_PIRsensorState = false;
if (changeState)
{
s_PIRsensorState = newState;
}
return s_PIRsensorState;
}
void IRAM_ATTR PIRsensorSwitch::ISR_PIRstateChange()
{
newPIRsensorState(true, true);
}
PIRsensorSwitch *PIRsensorSwitch::PIRsensorSwitchInstance(PIRsensorSwitch *pInstance, bool bRemoveInstance)
{
static PIRsensorSwitch *s_pPIRsensorSwitch = nullptr;
if (pInstance != nullptr || bRemoveInstance)
{
s_pPIRsensorSwitch = pInstance;
}
return s_pPIRsensorSwitch;
}
// strings to reduce flash memory usage (used more than twice)
const char PIRsensorSwitch::_name[] PROGMEM = "PIRsensorSwitch";
const char PIRsensorSwitch::_enabled[] PROGMEM = "PIRenabled";
const char PIRsensorSwitch::_switchOffDelay[] PROGMEM = "PIRoffSec";
const char PIRsensorSwitch::_onPreset[] PROGMEM = "on-preset";
const char PIRsensorSwitch::_offPreset[] PROGMEM = "off-preset";
const char PIRsensorSwitch::_nightTime[] PROGMEM = "nighttime-only";
const char PIRsensorSwitch::_mqttOnly[] PROGMEM = "mqtt-only";

8
usermods/RTC/readme.md Normal file
View File

@@ -0,0 +1,8 @@
# DS1307/DS3231 Real time clock
Gets the time from I2C RTC module on boot. This allows clocks to operate e.g. if temporarily no WiFi is available.
The stored time is updated each time NTP is synced.
## Installation
Add the build flag `-D USERMOD_RTC` to your platformio environment.

View File

@@ -0,0 +1,35 @@
#pragma once
#include "src/dependencies/time/DS1307RTC.h"
#include "wled.h"
//Connect DS1307 to standard I2C pins (ESP32: GPIO 21 (SDA)/GPIO 22 (SCL))
class RTCUsermod : public Usermod {
private:
unsigned long lastTime = 0;
bool disabled = false;
public:
void setup() {
time_t rtcTime = RTC.get();
if (rtcTime) {
toki.setTime(rtcTime,TOKI_NO_MS_ACCURACY,TOKI_TS_RTC);
updateLocalTime();
} else {
if (!RTC.chipPresent()) disabled = true; //don't waste time if H/W error
}
}
void loop() {
if (!disabled && toki.isTick()) {
time_t t = toki.second();
if (t != RTC.get()) RTC.set(t); //set RTC to NTP/UI-provided value
}
}
uint16_t getId()
{
return USERMOD_ID_RTC;
}
};

View File

@@ -0,0 +1,16 @@
; Options
; -------
; USERMOD_SN_PHOTORESISTOR - define this to have this user mod included wled00\usermods_list.cpp
; USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL - the number of milliseconds between measurements, defaults to 60 seconds
; USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT - the number of milliseconds after boot to take first measurement, defaults to 20 seconds
; USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE - the voltage supplied to the sensor, defaults to 5v
; USERMOD_SN_PHOTORESISTOR_ADC_PRECISION - the ADC precision is the number of distinguishable ADC inputs, defaults to 1024.0 (10 bits)
; USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE - the resistor size, defaults to 10000.0 (10K hms)
; USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE - the offset value to report on, defaults to 25
;
[env:usermod_sn_photoresistor_d1_mini]
extends = env:d1_mini
build_flags =
${common.build_flags_esp8266}
-D USERMOD_SN_PHOTORESISTOR
lib_deps = ${env.lib_deps}

View File

@@ -0,0 +1,30 @@
# SN_Photoresistor usermod
This usermod will read from an attached photoresistor sensor like the KY-018 sensor.
The luminance is displayed both in the Info section of the web UI as well as published to the `/luminance` MQTT topic if enabled.
## Installation
Copy the example `platformio_override.ini` to the root directory. This file should be placed in the same directory as `platformio.ini`.
### Define Your Options
* `USERMOD_SN_PHOTORESISTOR` - define this to have this user mod included wled00\usermods_list.cpp
* `USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL` - the number of milliseconds between measurements, defaults to 60 seconds
* `USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 20 seconds
* `USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE` - the voltage supplied to the sensor, defaults to 5v
* `USERMOD_SN_PHOTORESISTOR_ADC_PRECISION` - the ADC precision is the number of distinguishable ADC inputs, defaults to 1024.0 (10 bits)
* `USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE` - the resistor size, defaults to 10000.0 (10K hms)
* `USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE` - the offset value to report on, defaults to 25
All parameters can be configured at runtime using Usermods settings page.
## Project link
* [QuinLED-Dig-Uno](https://quinled.info/2018/09/15/quinled-dig-uno/) - Project link
### PlatformIO requirements
If you are using `platformio_override.ini`, you should be able to refresh the task list and see your custom task, for example `env:usermod_sn_photoresistor_d1_mini`.
## Change Log

View File

@@ -0,0 +1,203 @@
#pragma once
#include "wled.h"
//Pin defaults for QuinLed Dig-Uno (A0)
#define PHOTORESISTOR_PIN A0
// the frequency to check photoresistor, 10 seconds
#ifndef USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL
#define USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL 10000
#endif
// how many seconds after boot to take first measurement, 10 seconds
#ifndef USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT
#define USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT 10000
#endif
// supplied voltage
#ifndef USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE
#define USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE 5
#endif
// 10 bits
#ifndef USERMOD_SN_PHOTORESISTOR_ADC_PRECISION
#define USERMOD_SN_PHOTORESISTOR_ADC_PRECISION 1024.0
#endif
// resistor size 10K hms
#ifndef USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE
#define USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE 10000.0
#endif
// only report if differance grater than offset value
#ifndef USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE
#define USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE 5
#endif
class Usermod_SN_Photoresistor : public Usermod
{
private:
float referenceVoltage = USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE;
float resistorValue = USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE;
float adcPrecision = USERMOD_SN_PHOTORESISTOR_ADC_PRECISION;
int8_t offset = USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE;
unsigned long readingInterval = USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL;
// set last reading as "40 sec before boot", so first reading is taken after 20 sec
unsigned long lastMeasurement = UINT32_MAX - (USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL - USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT);
// flag to indicate we have finished the first getTemperature call
// allows this library to report to the user how long until the first
// measurement
bool getLuminanceComplete = false;
uint16_t lastLDRValue = -1000;
// flag set at startup
bool disabled = 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 _referenceVoltage[];
static const char _resistorValue[];
static const char _adcPrecision[];
static const char _offset[];
bool checkBoundSensor(float newValue, float prevValue, float maxDiff)
{
return isnan(prevValue) || newValue <= prevValue - maxDiff || newValue >= prevValue + maxDiff;
}
uint16_t getLuminance()
{
// http://forum.arduino.cc/index.php?topic=37555.0
// https://forum.arduino.cc/index.php?topic=185158.0
float volts = analogRead(PHOTORESISTOR_PIN) * (referenceVoltage / adcPrecision);
float amps = volts / resistorValue;
float lux = amps * 1000000 * 2.0;
lastMeasurement = millis();
getLuminanceComplete = true;
return uint16_t(lux);
}
public:
void setup()
{
// set pinmode
pinMode(PHOTORESISTOR_PIN, INPUT);
}
void loop()
{
if (disabled || strip.isUpdating())
return;
unsigned long now = millis();
// check to see if we are due for taking a measurement
// lastMeasurement will not be updated until the conversion
// is complete the the reading is finished
if (now - lastMeasurement < readingInterval)
{
return;
}
uint16_t currentLDRValue = getLuminance();
if (checkBoundSensor(currentLDRValue, lastLDRValue, offset))
{
lastLDRValue = currentLDRValue;
if (WLED_MQTT_CONNECTED)
{
char subuf[45];
strcpy(subuf, mqttDeviceTopic);
strcat_P(subuf, PSTR("/luminance"));
mqtt->publish(subuf, 0, true, String(lastLDRValue).c_str());
}
else
{
DEBUG_PRINTLN("Missing MQTT connection. Not publishing data");
}
}
}
void addToJsonInfo(JsonObject &root)
{
JsonObject user = root[F("u")];
if (user.isNull())
user = root.createNestedObject(F("u"));
JsonArray lux = user.createNestedArray(F("Luminance"));
if (!getLuminanceComplete)
{
// if we haven't read the sensor yet, let the user know
// that we are still waiting for the first measurement
lux.add((USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT - millis()) / 1000);
lux.add(F(" sec until read"));
return;
}
lux.add(lastLDRValue);
lux.add(F(" lux"));
}
uint16_t getId()
{
return USERMOD_ID_SN_PHOTORESISTOR;
}
/**
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
*/
void addToConfig(JsonObject &root)
{
// we add JSON object.
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
top[FPSTR(_enabled)] = !disabled;
top[FPSTR(_readInterval)] = readingInterval / 1000;
top[FPSTR(_referenceVoltage)] = referenceVoltage;
top[FPSTR(_resistorValue)] = resistorValue;
top[FPSTR(_adcPrecision)] = adcPrecision;
top[FPSTR(_offset)] = offset;
DEBUG_PRINTLN(F("Photoresistor config saved."));
}
/**
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
*/
bool readFromConfig(JsonObject &root)
{
// we look for JSON object.
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
disabled = !(top[FPSTR(_enabled)] | !disabled);
readingInterval = (top[FPSTR(_readInterval)] | readingInterval/1000) * 1000; // convert to ms
referenceVoltage = top[FPSTR(_referenceVoltage)] | referenceVoltage;
resistorValue = top[FPSTR(_resistorValue)] | resistorValue;
adcPrecision = top[FPSTR(_adcPrecision)] | adcPrecision;
offset = top[FPSTR(_offset)] | offset;
DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(" config (re)loaded."));
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return true;
}
};
// strings to reduce flash memory usage (used more than twice)
const char Usermod_SN_Photoresistor::_name[] PROGMEM = "Photoresistor";
const char Usermod_SN_Photoresistor::_enabled[] PROGMEM = "enabled";
const char Usermod_SN_Photoresistor::_readInterval[] PROGMEM = "read-interval-s";
const char Usermod_SN_Photoresistor::_referenceVoltage[] PROGMEM = "supplied-voltage";
const char Usermod_SN_Photoresistor::_resistorValue[] PROGMEM = "resistor-value";
const char Usermod_SN_Photoresistor::_adcPrecision[] PROGMEM = "adc-precision";
const char Usermod_SN_Photoresistor::_offset[] PROGMEM = "offset";

View File

@@ -0,0 +1,14 @@
#include "wled.h"
/*
* Register your v2 usermods here!
*/
#ifdef USERMOD_SN_PHOTORESISTOR
#include "../usermods/SN_Photoresistor/usermod_sn_photoresistor.h"
#endif
void registerUsermods()
{
#ifdef USERMOD_SN_PHOTORESISTOR
usermods.add(new Usermod_SN_Photoresistor());
#endif
}

View File

@@ -3,14 +3,24 @@ This usermod allows use of the TTGO T-Display ESP32 module with integrated 240x1
for controlling WLED and showing the following information:
* Current SSID
* IP address if obtained
* in AP mode and turned off lightning AP password is shown
* If connected to a network, current brightness % is shown
* in AP mode AP IP and password are shown
* Current effect
* Current palette
* Estimated current in mA is shown (NOTE: for this to be a reasonable value, the correct LED type must be specified in the LED Prefs section)
Button pin is mapped to the onboard button next to the side actuated reset button of the TTGO T-Display board.
I have designed a 3D printed case around this board and an ["ElectroCookie"](https://amzn.to/2WCNeeA) project board, a [level shifter](https://amzn.to/3hbKu18), a [buck regulator](https://amzn.to/3mLMy0W), and a DC [power jack](https://amzn.to/3phj9NZ). I use 12V WS2815 LED strips for my projects, and power them with 12V power supplies, so the regulator drops the voltage to the 5V level I need to power the ESP module and the level shifter. If there is any interest in this case, which elevates the board and display on some custom extended headers to make place the screen at the top of the enclosure (with accessible buttons), let me know, and I could post the STL files. It is a bit tricky to get the height correct, so I also designed a one-time use 3D printed solder fixture to set the board in the right location and at the correct height for the housing. (It is one-time use because it has to be cut off after soldering to be able to remove it). I didn't think the effort to make it in multiple pieces was worthwhile.
Usermod based on a rework of the ssd1306_i2c_oled_u8g2 usermod from the WLED repo.
## Hardware
![Hardware](assets/ttgo_hardware1.png)
![Hardware](assets/ttgo-tdisplay-enclosure1a.png)
![Hardware](assets/ttgo-tdisplay-enclosure2a.png)
![Hardware](assets/ttgo-tdisplay-enclosure3a.png)
![Hardware](assets/ttgo-tdisplay-enclosure3a.png)
## Github reference for TTGO-Tdisplay
@@ -20,7 +30,11 @@ Usermod based on a rework of the ssd1306_i2c_oled_u8g2 usermod from the WLED rep
Functionality checked with:
* TTGO T-Display
* PlatformIO
* Group of 4 individual Neopixels from Adafruit, and a full string of 68 LEDs.
* Group of 4 individual Neopixels from Adafruit, and a several full strings of 12v WS2815 LEDs.
* The hardware design shown above should be limited to shorter strings. For larger strings, I use a different setup with a dedicated 12v power supply and power them directly off the supply (in addition to dropping the 12v supply down to 5v with a buck regulator for the ESP module and level shifter).
## Setup Needed:
* As with all usermods, copy the usermod.cpp file from the TTGO-T-Display usermod folder to the wled00 folder (replacing the default usermod.cpp file).
## Platformio Requirements
### Platformio.ini changes

Binary file not shown.

After

Width:  |  Height:  |  Size: 708 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 848 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 876 KiB

View File

@@ -56,7 +56,7 @@ void userSetup() {
tft.setTextColor(TFT_WHITE);
tft.setCursor(1, 10);
tft.setTextDatum(MC_DATUM);
tft.setTextSize(2);
tft.setTextSize(3);
tft.print("Loading...");
if (TFT_BL > 0) { // TFT_BL has been set in the TFT_eSPI library in the User Setup file TTGO_T_Display.h
@@ -142,22 +142,41 @@ void userLoop() {
tft.fillScreen(TFT_BLACK);
tft.setTextSize(2);
// First row with Wifi name
tft.setCursor(1, 10);
tft.setCursor(1, 1);
tft.print(knownSsid.substring(0, tftcharwidth > 1 ? tftcharwidth - 1 : 0));
// Print `~` char to indicate that SSID is longer, than our dicplay
if (knownSsid.length() > tftcharwidth)
tft.print("~");
// Second row with IP or Psssword
tft.setCursor(1, 40);
// Print password in AP mode and if led is OFF.
if (apActive && bri == 0)
tft.print(apPass);
else
// Second row with AP IP and Password or IP
tft.setTextSize(2);
tft.setCursor(1, 24);
// Print AP IP and password in AP mode or knownIP if AP not active.
// if (apActive && bri == 0)
// tft.print(apPass);
// else
// tft.print(knownIp);
if (apActive) {
tft.print("AP IP: ");
tft.print(knownIp);
tft.setCursor(1,46);
tft.print("AP Pass:");
tft.print(apPass);
}
else {
tft.print("IP: ");
tft.print(knownIp);
tft.setCursor(1,46);
//tft.print("Signal Strength: ");
//tft.print(i.wifi.signal);
tft.print("Brightness: ");
tft.print(((float(bri)/255)*100));
tft.print("%");
}
// Third row with mode name
tft.setCursor(1, 70);
tft.setCursor(1, 68);
uint8_t qComma = 0;
bool insideQuotes = false;
uint8_t printedChars = 0;
@@ -184,7 +203,7 @@ void userLoop() {
break;
}
// Fourth row with palette name
tft.setCursor(1, 100);
tft.setCursor(1, 90);
qComma = 0;
insideQuotes = false;
printedChars = 0;
@@ -210,5 +229,10 @@ void userLoop() {
if ((qComma > knownPalette) || (printedChars > tftcharwidth - 1))
break;
}
}
// Fifth row with estimated mA usage
tft.setCursor(1, 112);
// Print estimated milliamp usage (must specify the LED type in LED prefs for this to be a reasonable estimate).
tft.print(strip.currentMilliamps);
tft.print("mA (estimated)");
}

View File

@@ -3,7 +3,7 @@
Based on the excellent `QuinLED_Dig_Uno_Temp_MQTT` by srg74 and 400killer!
This usermod will read from an attached DS18B20 temperature sensor (as available on the QuinLED Dig-Uno)
The temperature is displayed both in the Info section of the web UI as well as published to the `/temperature` MQTT topic if enabled.
This usermod will be expanded with support for different sensor types in the future.
This usermod may be expanded with support for different sensor types in the future.
If temperature sensor is not detected during boot, this usermod will be disabled.
@@ -14,20 +14,21 @@ Copy the example `platformio_override.ini` to the root directory. This file sho
### Define Your Options
* `USERMOD_DALLASTEMPERATURE` - define this to have this user mod included wled00\usermods_list.cpp
* `USERMOD_DALLASTEMPERATURE_CELSIUS` - define this to report temperatures in degrees celsious, otherwise fahrenheit will be reported
* `USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL` - the number of milliseconds between measurements, defaults to 60 seconds
* `USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 20 seconds
All parameters can be configured at runtime using Usermods settings page, including pin, selection to display temerature in degrees Celsius or Farenheit mand measurement interval.
## Project link
* [QuinLED-Dig-Uno](https://quinled.info/2018/09/15/quinled-dig-uno/) - Project link
* [Srg74-WLED-Wemos-shield](https://github.com/srg74/WLED-wemos-shield) - another great DIY WLED board
### PlatformIO requirements
If you are using `platformio_override.ini`, you should be able to refresh the task list and see your custom task, for example `env:d1_mini_usermod_dallas_temperature_C`.
If you are not using `platformio_override.ini`, you might have to uncomment `DallasTemperature@~3.8.0`,`OneWire@~2.3.5 under` `[common]` section in `platformio.ini`:
If you are not using `platformio_override.ini`, you might have to uncomment `OneWire@~2.3.5 under` `[common]` section in `platformio.ini`:
```ini
# platformio.ini
@@ -39,12 +40,9 @@ default_envs = d1_mini
...
[common]
...
lib_deps_external =
lib_deps =
...
#For use SSD1306 OLED display uncomment following
U8g2@~2.27.3
#For Dallas sensor uncomment following 2 lines
DallasTemperature@~3.8.0
#For Dallas sensor uncomment following line
OneWire@~2.3.5
...
```
@@ -55,4 +53,6 @@ lib_deps_external =
* Changed to use async, non-blocking implementation
* Do not report low temperatures that indicate an error to mqtt
* Disable plugin if temperature sensor not detected
* Report the number of seconds until the first read in the info screen instead of sensor error
* Report the number of seconds until the first read in the info screen instead of sensor error
2021-04
* Adaptation for runtime configuration.

View File

@@ -1,14 +1,15 @@
#pragma once
#include "wled.h"
#include "OneWire.h"
#include <DallasTemperature.h> //DS18B20
//Pin defaults for QuinLed Dig-Uno
#ifdef ARDUINO_ARCH_ESP32
#define TEMPERATURE_PIN 18
#else //ESP8266 boards
#define TEMPERATURE_PIN 14
//Pin defaults for QuinLed Dig-Uno if not overriden
#ifndef TEMPERATURE_PIN
#ifdef ARDUINO_ARCH_ESP32
#define TEMPERATURE_PIN 18
#else //ESP8266 boards
#define TEMPERATURE_PIN 14
#endif
#endif
// the frequency to check temperature, 1 minute
@@ -16,23 +17,22 @@
#define USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL 60000
#endif
// how many seconds after boot to take first measurement, 20 seconds
#ifndef USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT
#define USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT 20000
#endif
OneWire oneWire(TEMPERATURE_PIN);
DallasTemperature sensor(&oneWire);
class UsermodTemperature : public Usermod {
private:
// The device's unique 64-bit serial code stored in on-board ROM.
// Reading directly from the sensor device address is faster than
// reading from index. When reading by index, DallasTemperature
// must first look up the device address at the specified index.
DeviceAddress sensorDeviceAddress;
bool initDone = false;
OneWire *oneWire;
// GPIO pin used for sensor (with a default compile-time fallback)
int8_t temperaturePin = TEMPERATURE_PIN;
// measurement unit (true==°C, false==°F)
bool degC = true;
// using parasite power on the sensor
bool parasite = false;
// how often do we read from sensor?
unsigned long readingInterval = USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL;
// set last reading as "40 sec before boot", so first reading is taken after 20 sec
unsigned long lastMeasurement = UINT32_MAX - (USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL - USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT);
unsigned long lastMeasurement = UINT32_MAX - USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL;
// last time requestTemperatures was called
// used to determine when we can read the sensors temperature
// we have to wait at least 93.75 ms after requestTemperatures() is called
@@ -40,93 +40,123 @@ class UsermodTemperature : public Usermod {
float temperature = -100; // default to -100, DS18B20 only goes down to -50C
// indicates requestTemperatures has been called but the sensor measurement is not complete
bool waitingForConversion = false;
// flag to indicate we have finished the first getTemperature call
// allows this library to report to the user how long until the first
// measurement
bool getTemperatureComplete = false;
// flag set at startup if DS18B20 sensor not found, avoids trying to keep getting
// temperature if flashed to a board without a sensor attached
bool disabled = false;
bool enabled = true;
void requestTemperatures() {
// there is requestTemperaturesByAddress however it
// appears to do more work,
// TODO: measure exection time difference
sensor.requestTemperatures();
lastTemperaturesRequest = millis();
waitingForConversion = true;
// 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[];
//Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013
float readDallas() {
byte i;
byte data[2];
int16_t result; // raw data from sensor
if (!oneWire->reset()) return -127.0f; // send reset command and fail fast
oneWire->skip(); // skip ROM
oneWire->write(0xBE); // read (temperature) from EEPROM
for (i=0; i < 2; i++) data[i] = oneWire->read(); // first 2 bytes contain temperature
for (i=2; i < 8; i++) oneWire->read(); // read unused bytes
result = (data[1]<<4) | (data[0]>>4); // we only need whole part, we will add fraction when returning
if (data[1]&0x80) result |= 0xFF00; // fix negative value
oneWire->reset();
oneWire->skip(); // skip ROM
oneWire->write(0x44,parasite); // request new temperature reading (without parasite power)
return (float)result + ((data[0]&0x0008) ? 0.5f : 0.0f);
}
void getTemperature() {
#ifdef USERMOD_DALLASTEMPERATURE_CELSIUS
temperature = sensor.getTempC(sensorDeviceAddress);
#else
temperature = sensor.getTempF(sensorDeviceAddress);
#endif
void requestTemperatures() {
readDallas();
lastTemperaturesRequest = millis();
waitingForConversion = true;
DEBUG_PRINTLN(F("Requested temperature."));
}
void readTemperature() {
temperature = readDallas();
lastMeasurement = millis();
waitingForConversion = false;
getTemperatureComplete = true;
DEBUG_PRINTF("Read temperature %2.1f.\n", temperature);
}
bool findSensor() {
DEBUG_PRINTLN(F("Searching for sensor..."));
uint8_t deviceAddress[8] = {0,0,0,0,0,0,0,0};
// find out if we have DS18xxx sensor attached
oneWire->reset_search();
while (oneWire->search(deviceAddress)) {
if (oneWire->crc8(deviceAddress, 7) == deviceAddress[7]) {
switch (deviceAddress[0]) {
case 0x10: // DS18S20
case 0x22: // DS18B20
case 0x28: // DS1822
case 0x3B: // DS1825
case 0x42: // DS28EA00
DEBUG_PRINTLN(F("Sensor found."));
return true;
}
}
}
return false;
}
public:
void setup() {
sensor.begin();
// get the unique 64-bit serial code stored in on-board ROM
// if getAddress returns false, the sensor was not found
disabled = !sensor.getAddress(sensorDeviceAddress, 0);
if (!disabled) {
DEBUG_PRINTLN("Dallas Temperature found");
// set the resolution for this specific device
sensor.setResolution(sensorDeviceAddress, 9, true);
// do not block waiting for reading
sensor.setWaitForConversion(false);
int retries = 10;
// pin retrieved from cfg.json (readFromConfig()) prior to running setup()
if (!pinManager.allocatePin(temperaturePin,false)) {
temperaturePin = -1; // allocation failed
enabled = false;
DEBUG_PRINTLN(F("Temperature pin allocation failed."));
} else {
DEBUG_PRINTLN("Dallas Temperature not found");
if (enabled) {
// config says we are enabled
oneWire = new OneWire(temperaturePin);
if (!oneWire->reset())
enabled = false; // resetting 1-Wire bus yielded an error
else
while ((enabled=findSensor()) && retries--) delay(25); // try to find sensor
}
}
initDone = true;
}
void loop() {
if (disabled) {
return;
}
if (!enabled || strip.isUpdating()) return;
unsigned long now = millis();
// check to see if we are due for taking a measurement
// lastMeasurement will not be updated until the conversion
// is complete the the reading is finished
if (now - lastMeasurement < USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL)
{
return;
}
if (now - lastMeasurement < readingInterval) return;
// we are due for a measurement, if we are not already waiting
// we are due for a measurement, if we are not already waiting
// for a conversion to complete, then make a new request for temps
if (!waitingForConversion)
{
if (!waitingForConversion) {
requestTemperatures();
return;
}
// we were waiting for a conversion to complete, have we waited log enough?
if (now - lastTemperaturesRequest >= 94 /* 93.75ms per the datasheet */)
{
getTemperature();
if (now - lastTemperaturesRequest >= 100 /* 93.75ms per the datasheet but can be up to 750ms */) {
readTemperature();
if (WLED_MQTT_CONNECTED) {
char subuf[38];
char subuf[64];
strcpy(subuf, mqttDeviceTopic);
if (-100 <= temperature) {
// dont publish super low temperature as the graph will get messed up
// the DallasTemperature library returns -127C or -196.6F when problem
// reading the sensor
strcat(subuf, "/temperature");
mqtt->publish(subuf, 0, true, String(temperature).c_str());
strcat_P(subuf, PSTR("/temperature"));
mqtt->publish(subuf, 0, false, String(temperature).c_str());
strcat_P(subuf, PSTR("_f"));
mqtt->publish(subuf, 0, false, String((float)temperature * 1.8f + 32).c_str());
} else {
// publish something else to indicate status?
}
@@ -134,37 +164,115 @@ class UsermodTemperature : public Usermod {
}
}
/*
* API calls te enable data exchange between WLED modules
*/
inline float getTemperatureC() {
return (float)temperature;
}
inline float getTemperatureF() {
return (float)temperature * 1.8f + 32;
}
/*
* 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) {
// dont add temperature to info if we are disabled
if (disabled) {
return;
}
if (!enabled) return;
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
JsonArray temp = user.createNestedArray("Temperature");
if (!getTemperatureComplete) {
// if we haven't read the sensor yet, let the user know
// that we are still waiting for the first measurement
temp.add((USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT - millis()) / 1000);
temp.add(" sec until read");
return;
}
JsonArray temp = user.createNestedArray(FPSTR(_name));
//temp.add(F("Loaded."));
if (temperature <= -100) {
temp.add(0);
temp.add(" Sensor Error!");
temp.add(F(" Sensor Error!"));
return;
}
temp.add(temperature);
#ifdef USERMOD_DALLASTEMPERATURE_CELSIUS
temp.add("°C");
#else
temp.add("°F");
#endif
temp.add(degC ? temperature : (float)temperature * 1.8f + 32);
if (degC) temp.add(F("°C"));
else temp.add(F("°F"));
}
/**
* 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
*/
//void addToJsonState(JsonObject &root)
//{
//}
/**
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
* Read "<usermodname>_<usermodparam>" from json state and and change settings (i.e. GPIO pin) used.
*/
//void readFromJsonState(JsonObject &root) {
// if (!initDone) return; // prevent crash on boot applyPreset()
//}
/**
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
*/
void addToConfig(JsonObject &root) {
// 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;
top[FPSTR(_parasite)] = parasite;
DEBUG_PRINTLN(F("Temperature config saved."));
}
/**
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
*
* 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;
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
DEBUG_PRINT(FPSTR(_name));
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;
readingInterval = min(120,max(10,(int)readingInterval)) * 1000; // convert to ms
parasite = top[FPSTR(_parasite)] | parasite;
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
// first run: reading from cfg.json
temperaturePin = newTemperaturePin;
DEBUG_PRINTLN(F(" config loaded."));
} else {
// changing parameters from settings page
if (newTemperaturePin != temperaturePin) {
// deallocate pin and release memory
delete oneWire;
pinManager.deallocatePin(temperaturePin);
temperaturePin = newTemperaturePin;
// initialise
setup();
}
DEBUG_PRINTLN(F(" config (re)loaded."));
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_parasite)].isNull();
}
uint16_t getId()
@@ -172,3 +280,9 @@ class UsermodTemperature : public Usermod {
return USERMOD_ID_TEMPERATURE;
}
};
// 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";

View File

@@ -85,7 +85,7 @@ public:
if (m_pD2D && (999000000L != ntpLastSyncTime))
{
// to prevent needing to import all the timezone stuff from other modules, work completely in UTC
time_t timeUTC = now();
time_t timeUTC = toki.second();
tmElements_t tmNow;
breakTime(timeUTC, tmNow);
int nCurMinute = tmNow.Minute;

View File

@@ -0,0 +1,35 @@
# Description
That usermod implements support of simple hand gestures with VL53L0X sensor: on/off and brightness correction.
It can be useful for kitchen strips to avoid any touches.
- on/off - just swipe a hand below your sensor ("shortPressAction" is called and can be customized through WLED macros)
- brightness correction - keep your hand below sensor for 1 second to switch to "brightness" mode.
Configure brightness by changing distance to the sensor (see parameters below for customization).
"macroLongPress" is also called here.
## Installation
1. Attach VL53L0X sensor to i2c pins according to default pins for your board.
2. Add `-D USERMOD_VL53L0X_GESTURES` to your build flags at platformio.ini (plaformio_override.ini) for needed environment.
In my case, for example: `build_flags = ${common.build_flags_esp8266} -D RLYPIN=12 -D USERMOD_VL53L0X_GESTURES`
3. Add "pololu/VL53L0X" dependency below to `lib_deps` like this:
```ini
lib_deps = ${env.lib_deps}
pololu/VL53L0X @ ^1.3.0
```
My entire `platformio_override.ini` for example (for nodemcu board):
```ini
[platformio]
default_envs = nodemcu
[env:nodemcu]
board = nodemcu
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 RLYPIN=12 -D USERMOD_VL53L0X_GESTURES
lib_deps = ${env.lib_deps}
pololu/VL53L0X @ ^1.3.0
```

View File

@@ -0,0 +1,121 @@
/*
* That usermod implements support of simple hand gestures with VL53L0X sensor: on/off and brightness correction.
* It can be useful for kitchen strips to avoid any touches.
* - on/off - just swipe a hand below your sensor ("shortPressAction" is called and can be customized through WLED macros)
* - brightness correction - keep your hand below sensor for 1 second to switch to "brightness" mode.
* Configure brightness by changing distance to the sensor (see parameters below for customization).
* "macroLongPress" is also called here.
*
* Enabling this mod usermod:
* 1. Attach VL53L0X sensor to i2c pins according to default pins for your board.
* 2. Add "-D USERMOD_VL53L0X_GESTURES" to your build flags at platformio.ini (plaformio_override.ini) for needed environment.
* In my case, for example: build_flags = ${common.build_flags_esp8266} -D RLYPIN=12 -D USERMOD_VL53L0X_GESTURES
* 3. Add "pololu/VL53L0X" dependency to lib_deps like this:
* lib_deps = ${env.lib_deps}
* pololu/VL53L0X @ ^1.3.0
*/
#pragma once
#include "wled.h"
#include <Wire.h>
#include <VL53L0X.h>
#ifndef VL53L0X_MAX_RANGE_MM
#define VL53L0X_MAX_RANGE_MM 230 // max height in millimiters to react for motions
#endif
#ifndef VL53L0X_MIN_RANGE_OFFSET
#define VL53L0X_MIN_RANGE_OFFSET 60 // minimal range in millimiters that sensor can detect. Used in long motions to correct brightnes calculation.
#endif
#ifndef VL53L0X_DELAY_MS
#define VL53L0X_DELAY_MS 100 // how often to get data from sensor
#endif
#ifndef VL53L0X_LONG_MOTION_DELAY_MS
#define VL53L0X_LONG_MOTION_DELAY_MS 1000 // how often to get data from sensor
#endif
class UsermodVL53L0XGestures : public Usermod {
private:
//Private class members. You can declare variables and functions only accessible to your usermod here
unsigned long lastTime = 0;
VL53L0X sensor;
bool wasMotionBefore = false;
bool isLongMotion = false;
unsigned long motionStartTime = 0;
public:
void setup() {
Wire.begin();
sensor.setTimeout(150);
if (!sensor.init())
{
DEBUG_PRINTLN(F("Failed to detect and initialize VL53L0X sensor!"));
} else {
sensor.setMeasurementTimingBudget(20000); // set high speed mode
}
}
void loop() {
if (millis() - lastTime > VL53L0X_DELAY_MS)
{
lastTime = millis();
int range = sensor.readRangeSingleMillimeters();
DEBUG_PRINTF(F("range: %d, brightness: %d"), range, bri);
if (range < VL53L0X_MAX_RANGE_MM)
{
if (!wasMotionBefore)
{
motionStartTime = millis();
DEBUG_PRINTF(F("motionStartTime: %d"), motionStartTime);
}
wasMotionBefore = true;
if (millis() - motionStartTime > VL53L0X_LONG_MOTION_DELAY_MS) //long motion
{
DEBUG_PRINTF(F("long motion: %d"), motionStartTime);
if (!isLongMotion)
{
if (macroLongPress)
{
applyMacro(macroLongPress);
}
isLongMotion = true;
}
// set brightness according to range
bri = (VL53L0X_MAX_RANGE_MM - max(range, VL53L0X_MIN_RANGE_OFFSET)) * 255 / (VL53L0X_MAX_RANGE_MM - VL53L0X_MIN_RANGE_OFFSET);
DEBUG_PRINTF(F("new brightness: %d"), bri);
colorUpdated(1);
}
} else if (wasMotionBefore) { //released
long dur = millis() - motionStartTime;
if (!isLongMotion)
{ //short press
DEBUG_PRINTF(F("shortPressAction..."));
shortPressAction();
}
wasMotionBefore = false;
isLongMotion = false;
}
}
}
/*
* 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_VL53L0X;
}
};

View File

@@ -0,0 +1,79 @@
# Multi Relay
This usermod-v2 modification allows the connection of multiple relays each with individual delay and on/off mode.
## HTTP API
All responses are returned as JSON.
Status Request: `http://[device-ip]/relays`
Switch Command: `http://[device-ip]/relays?switch=1,0,1,1`
The number of numbers behind the switch parameter must correspond to the number of relays. The number 1 switches the relay on. The number 0 switches the relay off.
Toggle Command: `http://[device-ip]/relays?toggle=1,0,1,1`
The number of numbers behind the parameter switch must correspond to the number of relays. The number 1 causes a toggling of the relay. The number 0 leaves the state of the device.
Examples
1. 4 relays at all, relay 2 will be toggled: `http://[device-ip]/relays?toggle=0,1,0,0`
2. 3 relays at all, relay 1&3 will be switched on: `http://[device-ip]/relays?switch=1,0,1`
## MQTT API
wled/deviceMAC/relay/0/command on|off|toggle
wled/deviceMAC/relay/1/command on|off|toggle
When relay is switched it will publish a message:
wled/deviceMAC/relay/0 on|off
## Usermod installation
1. Register the usermod by adding `#include "../usermods/multi_relay/usermod_multi_relay.h"` at the top and `usermods.add(new MultiRelay());` at the bottom of `usermods_list.cpp`.
or
2. Use `#define USERMOD_MULTI_RELAY` in wled.h or `-D USERMOD_MULTI_RELAY`in your platformio.ini
You can override the default maximum number (4) of relays by defining MULTI_RELAY_MAX_RELAYS.
Example **usermods_list.cpp**:
```cpp
#include "wled.h"
/*
* Register your v2 usermods here!
* (for v1 usermods using just usermod.cpp, you can ignore this file)
*/
/*
* Add/uncomment your usermod filename here (and once more below)
* || || ||
* \/ \/ \/
*/
//#include "usermod_v2_example.h"
//#include "usermod_temperature.h"
#include "../usermods/usermod_multi_relay.h"
void registerUsermods()
{
/*
* Add your usermod class name here
* || || ||
* \/ \/ \/
*/
//usermods.add(new MyExampleUsermod());
//usermods.add(new UsermodTemperature());
usermods.add(new MultiRelay());
}
```
## Configuration
Usermod can be configured in Usermods settings page.
If there is no MultiRelay section, just save current configuration and re-open Usermods settings page.
Have fun - @blazoncek
## Change log
2021-04
* First implementation.

View File

@@ -0,0 +1,416 @@
#pragma once
#include "wled.h"
#ifndef MULTI_RELAY_MAX_RELAYS
#define MULTI_RELAY_MAX_RELAYS 4
#endif
#define ON true
#define OFF false
/*
* This usermod handles multiple relay outputs.
* These outputs complement built-in relay output in a way that the activation can be delayed.
* They can also activate/deactivate in reverse logic independently.
*/
typedef struct relay_t {
int8_t pin;
bool active;
bool mode;
bool state;
bool external;
uint16_t delay;
} Relay;
class MultiRelay : public Usermod {
private:
// array of relays
Relay _relay[MULTI_RELAY_MAX_RELAYS];
// switch timer start time
uint32_t _switchTimerStart = 0;
// old brightness
bool _oldBrightness = 0;
// usermod enabled
bool enabled = false; // needs to be configured (no default config)
// status of initialisation
bool initDone = false;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _enabled[];
static const char _relay_str[];
static const char _delay_str[];
static const char _activeHigh[];
static const char _external[];
void publishMqtt(const char* state, int relay) {
//Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED){
char subuf[64];
sprintf_P(subuf, PSTR("%s/relay/%d"), mqttDeviceTopic, relay);
mqtt->publish(subuf, 0, false, state);
}
}
/**
* switch off the strip if the delay has elapsed
*/
void handleOffTimer() {
bool activeRelays = false;
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].active && _switchTimerStart > 0 && millis() - _switchTimerStart > (_relay[i].delay*1000)) {
if (!_relay[i].external) toggleRelay(i);
_relay[i].active = false;
}
activeRelays = activeRelays || _relay[i].active;
}
if (!activeRelays) _switchTimerStart = 0;
}
/**
* HTTP API handler
* borrowed from:
* https://github.com/gsieben/WLED/blob/master/usermods/GeoGab-Relays/usermod_GeoGab.h
*/
#define GEOGABVERSION "0.1.3"
void InitHtmlAPIHandle() { // https://github.com/me-no-dev/ESPAsyncWebServer
DEBUG_PRINTLN(F("Relays: Initialize HTML API"));
server.on("/relays", HTTP_GET, [this](AsyncWebServerRequest *request) {
DEBUG_PRINTLN("Relays: HTML API");
String janswer;
String error = "";
//int params = request->params();
janswer = F("{\"NoOfRelays\":");
janswer += String(MULTI_RELAY_MAX_RELAYS) + ",";
if (getActiveRelayCount()) {
// Commands
if(request->hasParam("switch")) {
/**** Switch ****/
AsyncWebParameter* p = request->getParam("switch");
// Get Values
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
int value = getValue(p->value(), ',', i);
if (value==-1) {
error = F("There must be as much arugments as relays");
} else {
// Switch
if (_relay[i].external) switchRelay(i, (bool)value);
}
}
} else if(request->hasParam("toggle")) {
/**** Toggle ****/
AsyncWebParameter* p = request->getParam("toggle");
// Get Values
for (int i=0;i<MULTI_RELAY_MAX_RELAYS;i++) {
int value = getValue(p->value(), ',', i);
if (value==-1) {
error = F("There must be as mutch arugments as relays");
} else {
// Toggle
if (value && _relay[i].external) toggleRelay(i);
}
}
} else {
error = F("No valid command found");
}
} else {
error = F("No active relays");
}
// Status response
char sbuf[16];
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
sprintf_P(sbuf, PSTR("\"%d\":%d,"), i, (_relay[i].pin<0 ? -1 : (int)_relay[i].state));
janswer += sbuf;
}
janswer += F("\"error\":\"");
janswer += error;
janswer += F("\",");
janswer += F("\"SW Version\":\"");
janswer += String(GEOGABVERSION);
janswer += F("\"}");
request->send(200, "application/json", janswer);
});
}
int getValue(String data, char separator, int index) {
int found = 0;
int strIndex[] = {0, -1};
int maxIndex = data.length()-1;
for(int i=0; i<=maxIndex && found<=index; i++){
if(data.charAt(i)==separator || i==maxIndex){
found++;
strIndex[0] = strIndex[1]+1;
strIndex[1] = (i == maxIndex) ? i+1 : i;
}
}
return found>index ? data.substring(strIndex[0], strIndex[1]).toInt() : -1;
}
public:
/**
* constructor
*/
MultiRelay() {
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
_relay[i].pin = -1;
_relay[i].delay = 0;
_relay[i].mode = false;
_relay[i].active = false;
_relay[i].state = false;
_relay[i].external = false;
}
}
/**
* desctructor
*/
~MultiRelay() {}
/**
* Enable/Disable the usermod
*/
inline void enable(bool enable) { enabled = enable; }
/**
* Get usermod enabled/disabled state
*/
inline bool isEnabled() { return enabled; }
/**
* switch relay on/off
*/
void switchRelay(uint8_t relay, bool mode) {
if (relay>=MULTI_RELAY_MAX_RELAYS || _relay[relay].pin<0) return;
_relay[relay].state = mode;
pinMode(_relay[relay].pin, OUTPUT);
digitalWrite(_relay[relay].pin, mode ? !_relay[relay].mode : _relay[relay].mode);
publishMqtt(mode ? "on" : "off", relay);
}
/**
* toggle relay
*/
inline void toggleRelay(uint8_t relay) {
switchRelay(relay, !_relay[relay].state);
}
uint8_t getActiveRelayCount() {
uint8_t count = 0;
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) if (_relay[i].pin>=0) count++;
return count;
}
//Functions called by WLED
/**
* handling of MQTT message
* topic only contains stripped topic (part after /wled/MAC)
* topic should look like: /relay/X/command; where X is relay number, 0 based
*/
bool onMqttMessage(char* topic, char* payload) {
if (strlen(topic) > 8 && strncmp_P(topic, PSTR("/relay/"), 7) == 0 && strncmp_P(topic+8, PSTR("/command"), 8) == 0) {
uint8_t relay = strtoul(topic+7, NULL, 10);
if (relay<MULTI_RELAY_MAX_RELAYS) {
String action = payload;
if (action == "on") {
if (_relay[relay].external) switchRelay(relay, true);
return true;
} else if (action == "off") {
if (_relay[relay].external) switchRelay(relay, false);
return true;
} else if (action == "toggle") {
if (_relay[relay].external) toggleRelay(relay);
return true;
}
}
}
return false;
}
/**
* subscribe to MQTT topic for controlling relays
*/
void onMqttConnect(bool sessionPresent) {
//(re)subscribe to required topics
char subuf[64];
if (mqttDeviceTopic[0] != 0) {
strcpy(subuf, mqttDeviceTopic);
strcat_P(subuf, PSTR("/relay/#"));
mqtt->subscribe(subuf, 0);
}
}
/**
* setup() is called once at boot. WiFi is not yet connected at this point.
* You can use it to initialize variables, sensors or similar.
*/
void setup() {
// pins retrieved from cfg.json (readFromConfig()) prior to running setup()
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].pin<0) continue;
if (!pinManager.allocatePin(_relay[i].pin,true)) {
_relay[i].pin = -1; // allocation failed
} else {
switchRelay(i, _relay[i].state = (bool)bri);
_relay[i].active = false;
}
}
_oldBrightness = (bool)bri;
initDone = true;
}
/**
* connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces
*/
void connected() {
InitHtmlAPIHandle();
}
/**
* loop() is called continuously. Here you can check for events, read sensors, etc.
*/
void loop() {
if (!enabled || strip.isUpdating()) return;
static unsigned long lastUpdate = 0;
if (millis() - lastUpdate < 200) return; // update only 5 times/s
lastUpdate = millis();
//set relay when LEDs turn on
if (_oldBrightness != (bool)bri) {
_oldBrightness = (bool)bri;
_switchTimerStart = millis();
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].pin>=0) _relay[i].active = true;
}
}
handleOffTimer();
}
/**
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
*/
void addToJsonInfo(JsonObject &root) {
if (enabled) {
JsonObject user = root["u"];
if (user.isNull())
user = root.createNestedObject("u");
JsonArray infoArr = user.createNestedArray(F("Number of relays")); //name
infoArr.add(String(getActiveRelayCount()));
}
}
/**
* 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
*/
void addToJsonState(JsonObject &root) {
}
/**
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
void readFromJsonState(JsonObject &root) {
}
/**
* provide the changeable values
*/
void addToConfig(JsonObject &root) {
JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled;
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
String parName = FPSTR(_relay_str); parName += "-"; parName += i; parName += "-";
top[parName+"pin"] = _relay[i].pin;
top[parName+FPSTR(_activeHigh)] = _relay[i].mode;
top[parName+FPSTR(_delay_str)] = _relay[i].delay;
top[parName+FPSTR(_external)] = _relay[i].external;
}
DEBUG_PRINTLN(F("MultiRelay config saved."));
}
/**
* restore the changeable values
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
*
* The function should return true if configuration was successfully loaded or false if there was no configuration.
*/
bool readFromConfig(JsonObject &root) {
int8_t oldPin[MULTI_RELAY_MAX_RELAYS];
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
enabled = top[FPSTR(_enabled)] | enabled;
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
String parName = FPSTR(_relay_str); parName += "-"; parName += i; parName += "-";
oldPin[i] = _relay[i].pin;
_relay[i].pin = top[parName+"pin"] | _relay[i].pin;
_relay[i].mode = top[parName+FPSTR(_activeHigh)] | _relay[i].mode;
_relay[i].external = top[parName+FPSTR(_external)] | _relay[i].external;
_relay[i].delay = top[parName+FPSTR(_delay_str)] | _relay[i].delay;
_relay[i].delay = min(600,max(0,abs((int)_relay[i].delay))); // bounds checking max 10min
}
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
// reading config prior to setup()
DEBUG_PRINTLN(F(" config loaded."));
} else {
// deallocate all pins 1st
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++)
if (oldPin[i]>=0) {
pinManager.deallocatePin(oldPin[i]);
}
// allocate new pins
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].pin>=0 && pinManager.allocatePin(_relay[i].pin,true)) {
if (!_relay[i].external) switchRelay(i, _relay[i].state = (bool)bri);
} else {
_relay[i].pin = -1;
}
_relay[i].active = false;
}
DEBUG_PRINTLN(F(" config (re)loaded."));
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return true;
}
/**
* 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_MULTI_RELAY;
}
};
// strings to reduce flash memory usage (used more than twice)
const char MultiRelay::_name[] PROGMEM = "MultiRelay";
const char MultiRelay::_enabled[] PROGMEM = "enabled";
const char MultiRelay::_relay_str[] PROGMEM = "relay";
const char MultiRelay::_delay_str[] PROGMEM = "delay-s";
const char MultiRelay::_activeHigh[] PROGMEM = "active-high";
const char MultiRelay::_external[] PROGMEM = "external";

View File

@@ -0,0 +1,37 @@
# QuinLED-Dig-Quad Preassembled Unofficial Build
This usermod targets the [Preassembled QuinLED-Dig-Quad](https://quinled.info/pre-assembled-quinled-dig-quad/). Tested on board revision v1r6b,
and includes the following features:
* **Multi-channel Support** - enabling use of LED1, LED2, LED3, LED4 pins to work using segments
* **Temperature Sensor Support** - pulls readings from the built-in temperature sensor and adds the reading to the *Info* page in the UI
## Background
As a starting point, you should check out this awesome video from Quindor: [How to compile WLED yourself](https://quinled.info/2020/12/22/livestream-wled-compile/). The usermod you are reading now just provides some shortcuts for parts of what were covered in that video.
## Build Firmware with Multi-channel and Temp Support
1. Copy the `platformio_override.ini` file to the project's root directory
1. If using VS Code with the PlatformIO plugin like in the video, you will now see this new project task listed in the PLATFORMIO panel at the bottom as `env:QL-DigQuad-Pre-v0.1` (you probably need to hit the refresh button)
<img src="images/pio-screenshot.png" width="400px"/>
1. Edit this file from the root directory as needed:
<img src="images/params.png" width="400px"/>
* `PIXEL_COUNTS` may need to be adjusted for your set-up. E.g. I have lots of LEDs in Channel 1, but that's probably unusual for most
* `DATA_PINS` may need to be changed to "16,3,1,26" instead of "16,1,3,26" apparently depending on the board revision or some such
1. Build the mod (e.g. click `Build` from the project task circled above) and update your firmware using the `QL-DigQuad-Pre-v0.1` file, e.g. using _Manual OTA_ from the Config menu. Based on the video and my own experience, you might need to build twice 🤷‍♂️.
## Observing Temperature
Hopefully you can now see the Temperature listed in the Info page. If not, use Chrome Developer Tools to find the current temperature
1. Open the Developer Tools Console
2. Enter `lastinfo.u.Temperature` to view the Temperature array
<img src="images/json-temp.png" width="300px"/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 KiB

View File

@@ -0,0 +1,16 @@
; QuinLED-Dig-Quad Preassembled Unofficial
[env:QL-DigQuad-Pre-v0.1]
extends = env:esp32dev
build_flags = ${common.build_flags_esp32}
-D ESP32_MULTISTRIP
-D NUM_STRIPS=4
-D PIXEL_COUNTS="600, 300, 300, 300"
-D DATA_PINS="16,1,3,26"
-D RLYPIN=19
-D BTNPIN=17
-D USERMOD_DALLASTEMPERATURE
-D USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL=10000
lib_deps = ${env.lib_deps}
milesburton/DallasTemperature@^3.9.0
OneWire@~2.3.5

View File

@@ -0,0 +1,87 @@
# Sensors To Home Assistant (or mqtt)
This usermod will publish values of the BMP280, CCS811 and Si7021 sensors to Home Assistant via MQTT.
Its using home assistant automatic device discovery feature.
The use of Home Assistant is not mandatory; it will publish the sensor values via MQTT just fine without it.
Its resusing the mqtt connection set in the WLED web user interface.
## Maintainer
twitter.com/mpronk89
## Features
- Reads BMP280, CCS811 and Si7021 senors
- Publishes via MQTT, configured via webui of wled
- Announces device in Home Assistant for easy setup
- Efficient energy usage
- Updates every 60 seconds
## Example mqtt topics:
`$mqttDeviceTopic` is set in webui of WLED!
```
temperature: $mqttDeviceTopic/temperature
pressure: $mqttDeviceTopic/pressure
humidity: $mqttDeviceTopic/humidity
tvoc: $mqttDeviceTopic/tvoc
eCO2: $mqttDeviceTopic/eco2
IAQ: $mqttDeviceTopic/iaq
```
# Installation
## Hardware
### Requirements
1. BMP280/CCS811/Si7021 sensor. E.g. https://aliexpress.com/item/32979998543.html
2. A microcontroller which can talk i2c, e.g. esp32
### installation
Attach the sensor to the i2c interface.
Default PINs esp32:
```
SCL_PIN = 22;
SDA_PIN = 21;
```
Default PINs ESP8266:
```
SCL_PIN = 5;
SDA_PIN = 4;
```
## Enable in WLED
1. Copy `usermod_v2_SensorsToMqtt.h` into the `wled00` directory.
2. Add to `build_flags` in platformio.ini:
```
-D USERMOD_SENSORSTOMQTT
```
3. And add to `lib_deps` in platformio.ini:
```
adafruit/Adafruit BMP280 Library @ 2.1.0
adafruit/Adafruit CCS811 Library @ 1.0.4
adafruit/Adafruit Si7021 Library @ 1.4.0
```
The #ifdefs in `usermods_list.cpp` should do the rest :)
# Credits
- Aircoookie for making WLED
- Other usermod creators for example code
- Bouke_Regnerus for https://community.home-assistant.io/t/example-indoor-air-quality-text-sensor-using-ccs811-sensor/125854
- You, for reading this

View File

@@ -0,0 +1,284 @@
#pragma once
#include "wled.h"
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP280.h>
#include <Adafruit_CCS811.h>
#include <Adafruit_Si7021.h>
Adafruit_BMP280 bmp;
Adafruit_Si7021 si7021;
Adafruit_CCS811 ccs811;
#ifdef ARDUINO_ARCH_ESP32 //ESP32 boards
uint8_t SCL_PIN = 22;
uint8_t SDA_PIN = 21;
#else //ESP8266 boards
uint8_t SCL_PIN = 5;
uint8_t SDA_PIN = 4;
#endif
class UserMod_SensorsToMQTT : public Usermod
{
private:
bool initialized = false;
bool mqttInitialized = false;
float SensorPressure = 0;
float SensorTemperature = 0;
float SensorHumidity = 0;
char *SensorIaq = "Unknown";
String mqttTemperatureTopic = "";
String mqttHumidityTopic = "";
String mqttPressureTopic = "";
String mqttTvocTopic = "";
String mqttEco2Topic = "";
String mqttIaqTopic = "";
unsigned int SensorTvoc = 0;
unsigned int SensorEco2 = 0;
unsigned long nextMeasure = 0;
void _initialize()
{
initialized = bmp.begin(BMP280_ADDRESS_ALT);
bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */
Adafruit_BMP280::SAMPLING_X16, /* Temp. oversampling */
Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */
Adafruit_BMP280::FILTER_X16, /* Filtering. */
Adafruit_BMP280::STANDBY_MS_2000); /* Refresh values every 20 seconds */
initialized &= si7021.begin();
initialized &= ccs811.begin();
ccs811.setDriveMode(CCS811_DRIVE_MODE_10SEC); /* Refresh values every 10s */
Serial.print(initialized);
}
void _mqttInitialize()
{
mqttTemperatureTopic = String(mqttDeviceTopic) + "/temperature";
mqttPressureTopic = String(mqttDeviceTopic) + "/pressure";
mqttHumidityTopic = String(mqttDeviceTopic) + "/humidity";
mqttTvocTopic = String(mqttDeviceTopic) + "/tvoc";
mqttEco2Topic = String(mqttDeviceTopic) + "/eco2";
mqttIaqTopic = String(mqttDeviceTopic) + "/iaq";
String t = String("homeassistant/sensor/") + mqttClientID + "/temperature/config";
_createMqttSensor("temperature", mqttTemperatureTopic, "temperature", "°C");
_createMqttSensor("pressure", mqttPressureTopic, "pressure", "hPa");
_createMqttSensor("humidity", mqttHumidityTopic, "humidity", "%");
_createMqttSensor("tvoc", mqttTvocTopic, "", "ppb");
_createMqttSensor("eco2", mqttEco2Topic, "", "ppm");
_createMqttSensor("iaq", mqttIaqTopic, "", "");
}
void _createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement)
{
String t = String("homeassistant/sensor/") + mqttClientID + "/" + name + "/config";
StaticJsonDocument<300> doc;
doc["name"] = name;
doc["state_topic"] = topic;
doc["unique_id"] = String(mqttClientID) + name;
if (unitOfMeasurement != "")
doc["unit_of_measurement"] = unitOfMeasurement;
if (deviceClass != "")
doc["device_class"] = deviceClass;
doc["expire_after"] = 1800;
JsonObject device = doc.createNestedObject("device"); // attach the sensor to the same device
device["identifiers"] = String("wled-sensor-") + mqttClientID;
device["manufacturer"] = "Aircoookie";
device["model"] = "WLED";
device["sw_version"] = VERSION;
device["name"] = mqttClientID;
String temp;
serializeJson(doc, temp);
Serial.println(t);
Serial.println(temp);
mqtt->publish(t.c_str(), 0, true, temp.c_str());
}
void _updateSensorData()
{
SensorTemperature = bmp.readTemperature();
SensorHumidity = si7021.readHumidity();
SensorPressure = (bmp.readPressure() / 100.0F);
ccs811.setEnvironmentalData(SensorHumidity, SensorTemperature);
ccs811.readData();
SensorTvoc = ccs811.getTVOC();
SensorEco2 = ccs811.geteCO2();
SensorIaq = _getIaqIndex(SensorHumidity, SensorTvoc, SensorEco2);
Serial.printf("%f c, %f humidity, %f hPA, %u tvoc, %u Eco2, %s iaq\n",
SensorTemperature, SensorHumidity, SensorPressure,
SensorTvoc, SensorEco2, SensorIaq);
}
/**
* Credits: Bouke_Regnerus @ https://community.home-assistant.io/t/example-indoor-air-quality-text-sensor-using-ccs811-sensor/125854
*/
char *_getIaqIndex(float humidity, int tvoc, int eco2)
{
int iaq_index = 0;
/*
* Transform indoor humidity values to IAQ points according to Indoor Air Quality UK:
* http://www.iaquk.org.uk/
*/
if (humidity < 10 or humidity > 90)
{
iaq_index += 1;
}
else if (humidity < 20 or humidity > 80)
{
iaq_index += 2;
}
else if (humidity < 30 or humidity > 70)
{
iaq_index += 3;
}
else if (humidity < 40 or humidity > 60)
{
iaq_index += 4;
}
else if (humidity >= 40 and humidity <= 60)
{
iaq_index += 5;
}
/*
* Transform eCO2 values to IAQ points according to Indoor Air Quality UK:
* http://www.iaquk.org.uk/
*/
if (eco2 <= 600)
{
iaq_index += 5;
}
else if (eco2 <= 800)
{
iaq_index += 4;
}
else if (eco2 <= 1500)
{
iaq_index += 3;
}
else if (eco2 <= 1800)
{
iaq_index += 2;
}
else if (eco2 > 1800)
{
iaq_index += 1;
}
/*
* Transform TVOC values to IAQ points according to German environmental guidelines:
* https://www.repcomsrl.com/wp-content/uploads/2017/06/Environmental_Sensing_VOC_Product_Brochure_EN.pdf
*/
if (tvoc <= 65)
{
iaq_index += 5;
}
else if (tvoc <= 220)
{
iaq_index += 4;
}
else if (tvoc <= 660)
{
iaq_index += 3;
}
else if (tvoc <= 2200)
{
iaq_index += 2;
}
else if (tvoc > 2200)
{
iaq_index += 1;
}
if (iaq_index <= 6)
{
return "Unhealty";
}
else if (iaq_index <= 9)
{
return "Poor";
}
else if (iaq_index <= 12)
{
return "Moderate";
}
else if (iaq_index <= 14)
{
return "Good";
}
else if (iaq_index > 14)
{
return "Excellent";
}
}
public:
void setup()
{
Serial.println("Starting!");
Wire.begin(SDA_PIN, SCL_PIN);
Serial.println("Initializing sensors.. ");
_initialize();
}
// gets called every time WiFi is (re-)connected.
void connected()
{
nextMeasure = millis() + 5000; // Schedule next measure in 5 seconds
}
void loop()
{
unsigned long tempTimer = millis();
if (tempTimer > nextMeasure)
{
nextMeasure = tempTimer + 60000; // Schedule next measure in 60 seconds
if (!initialized)
{
Serial.println("Error! Sensors not initialized in loop()!");
_initialize();
return; // lets try again next loop
}
if (mqtt != nullptr && mqtt->connected())
{
if (!mqttInitialized)
{
_mqttInitialize();
mqttInitialized = true;
}
// Update sensor data
_updateSensorData();
// Create string populated with user defined device topic from the UI,
// and the read temperature, humidity and pressure.
// Then publish to MQTT server.
mqtt->publish(mqttTemperatureTopic.c_str(), 0, true, String(SensorTemperature).c_str());
mqtt->publish(mqttPressureTopic.c_str(), 0, true, String(SensorPressure).c_str());
mqtt->publish(mqttHumidityTopic.c_str(), 0, true, String(SensorHumidity).c_str());
mqtt->publish(mqttTvocTopic.c_str(), 0, true, String(SensorTvoc).c_str());
mqtt->publish(mqttEco2Topic.c_str(), 0, true, String(SensorEco2).c_str());
mqtt->publish(mqttIaqTopic.c_str(), 0, true, String(SensorIaq).c_str());
}
else
{
Serial.println("Missing MQTT connection. Not publishing data");
mqttInitialized = false;
}
}
}
};

View File

@@ -82,18 +82,6 @@ class StairwayWipeUsermod : public Usermod {
//if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
}
void addToConfig(JsonObject& root)
{
JsonObject top = root.createNestedObject("exampleUsermod");
top["great"] = userVar0; //save this var persistently whenever settings are saved
}
void readFromConfig(JsonObject& root)
{
JsonObject top = root["top"];
userVar0 = top["great"] | 42; //The value right of the pipe "|" is the default value in case your setting was not present in cfg.json (e.g. first boot)
}
uint16_t getId()
{
return USERMOD_ID_EXAMPLE;

View File

@@ -0,0 +1,47 @@
# Auto Save
v2 Usermod to automatically save settings
to preset number AUTOSAVE_PRESET_NUM after a change to any of
* brightness
* effect speed
* effect intensity
* mode (effect)
* palette
but it will wait for AUTOSAVE_SETTLE_MS milliseconds, a "settle"
period in case there are other changes (any change will
extend the "settle" window).
It will additionally load preset AUTOSAVE_PRESET_NUM at startup.
during the first `loop()`. Reasoning below.
AutoSaveUsermod is standalone, but if FourLineDisplayUsermod is installed, it will notify the user of the saved changes.
Note: I don't love that WLED doesn't respect the brightness of the preset being auto loaded, so the AutoSaveUsermod will set the AUTOSAVE_PRESET_NUM preset in the first loop, so brightness IS honored. This means WLED will effectively ignore Default brightness and Apply N preset at boot when the AutoSaveUsermod is installed.
## Installation
Copy and update the example `platformio_override.ini.sample`
from the Rotary Encoder UI usermode folder to the root directory of your particular build.
This file should be placed in the same directory as `platformio.ini`.
### Define Your Options
* `USERMOD_AUTO_SAVE` - define this to have this the Auto Save usermod included wled00\usermods_list.cpp
* `USERMOD_FOUR_LINE_DISPLAY` - define this to have this the Four Line Display mod included wled00\usermods_list.cpp - also tells this usermod that the display is available (see the Four Line Display usermod `readme.md` for more details)
You can configure auto-save parameters using Usermods settings page.
### PlatformIO requirements
No special requirements.
Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`.
## Change Log
2021-02
* First public release
2021-04
* Adaptation for runtime configuration.

View File

@@ -0,0 +1,239 @@
#pragma once
#include "wled.h"
// v2 Usermod to automatically save settings
// to configurable preset after a change to any of
//
// * brightness
// * effect speed
// * effect intensity
// * mode (effect)
// * palette
//
// but it will wait for configurable number of seconds, a "settle"
// period in case there are other changes (any change will
// extend the "settle" window).
//
// It can be configured to load auto saved preset at startup,
// during the first `loop()`.
//
// AutoSaveUsermod is standalone, but if FourLineDisplayUsermod
// is installed, it will notify the user of the saved changes.
// format: "~ MM-DD HH:MM:SS ~"
#define PRESET_NAME_BUFFER_SIZE 25
class AutoSaveUsermod : public Usermod {
private:
bool firstLoop = true;
bool initDone = false;
bool enabled = true;
// configurable parameters
uint16_t autoSaveAfterSec = 15; // 15s by default
uint8_t autoSavePreset = 250; // last possible preset
bool applyAutoSaveOnBoot = false; // do we load auto-saved preset on boot?
// If we've detected the need to auto save, this will be non zero.
uint16_t autoSaveAfter = 0;
uint8_t knownBrightness = 0;
uint8_t knownEffectSpeed = 0;
uint8_t knownEffectIntensity = 0;
uint8_t knownMode = 0;
uint8_t knownPalette = 0;
#ifdef USERMOD_FOUR_LINE_DISPLAY
FourLineDisplayUsermod* display;
#endif
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _autoSaveEnabled[];
static const char _autoSaveAfterSec[];
static const char _autoSavePreset[];
static const char _autoSaveApplyOnBoot[];
void inline saveSettings() {
char presetNameBuffer[PRESET_NAME_BUFFER_SIZE];
updateLocalTime();
sprintf_P(presetNameBuffer,
PSTR("~ %02d-%02d %02d:%02d:%02d ~"),
month(localTime), day(localTime),
hour(localTime), minute(localTime), second(localTime));
savePreset(autoSavePreset, true, presetNameBuffer);
}
void inline displayOverlay() {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display != nullptr) {
display->wakeDisplay();
display->overlay("Settings", "Auto Saved", 1500);
}
#endif
}
public:
// gets called once at boot. Do all initialization that doesn't depend on
// network here
void setup() {
#ifdef USERMOD_FOUR_LINE_DISPLAY
// This Usermod has enhanced funcionality if
// FourLineDisplayUsermod is available.
display = (FourLineDisplayUsermod*) usermods.lookup(USERMOD_ID_FOUR_LINE_DISP);
#endif
initDone = true;
}
// gets called every time WiFi is (re-)connected. Initialize own network
// interfaces here
void connected() {}
/*
* Da loop.
*/
void loop() {
if (!autoSaveAfterSec || !enabled || strip.isUpdating()) return; // setting 0 as autosave seconds disables autosave
unsigned long now = millis();
uint8_t currentMode = strip.getMode();
uint8_t currentPalette = strip.getSegment(0).palette;
if (firstLoop) {
firstLoop = false;
if (applyAutoSaveOnBoot) applyPreset(autoSavePreset);
knownBrightness = bri;
knownEffectSpeed = effectSpeed;
knownEffectIntensity = effectIntensity;
knownMode = currentMode;
knownPalette = currentPalette;
return;
}
unsigned long wouldAutoSaveAfter = now + autoSaveAfterSec*1000;
if (knownBrightness != bri) {
knownBrightness = bri;
autoSaveAfter = wouldAutoSaveAfter;
} else if (knownEffectSpeed != effectSpeed) {
knownEffectSpeed = effectSpeed;
autoSaveAfter = wouldAutoSaveAfter;
} else if (knownEffectIntensity != effectIntensity) {
knownEffectIntensity = effectIntensity;
autoSaveAfter = wouldAutoSaveAfter;
} else if (knownMode != currentMode) {
knownMode = currentMode;
autoSaveAfter = wouldAutoSaveAfter;
} else if (knownPalette != currentPalette) {
knownPalette = currentPalette;
autoSaveAfter = wouldAutoSaveAfter;
}
if (autoSaveAfter && now > autoSaveAfter) {
autoSaveAfter = 0;
// Time to auto save. You may have some flickry?
saveSettings();
displayOverlay();
}
}
/*
* 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 data = user.createNestedArray(F("Autosave"));
//data.add(F("Loaded."));
//}
/*
* 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
*/
//void addToJsonState(JsonObject& root) {
//}
/*
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
//void readFromJsonState(JsonObject& root) {
// if (!initDone) return; // prevent crash on boot applyPreset()
//}
/*
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
* If you want to force saving the current state, use serializeConfig() in your loop().
*
* CAUTION: serializeConfig() will initiate a filesystem write operation.
* It might cause the LEDs to stutter and will cause flash wear if called too often.
* Use it sparingly and always in the loop, never in network callbacks!
*
* addToConfig() will also not yet add your setting to one of the settings pages automatically.
* To make that work you still have to add the setting to the HTML, xml.cpp and set.cpp manually.
*
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
*/
void addToConfig(JsonObject& root) {
// we add JSON object: {"Autosave": {"autoSaveAfterSec": 10, "autoSavePreset": 99}}
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
top[FPSTR(_autoSaveEnabled)] = enabled;
top[FPSTR(_autoSaveAfterSec)] = autoSaveAfterSec; // usermodparam
top[FPSTR(_autoSavePreset)] = autoSavePreset; // usermodparam
top[FPSTR(_autoSaveApplyOnBoot)] = applyAutoSaveOnBoot;
DEBUG_PRINTLN(F("Autosave config saved."));
}
/*
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
* This is called by WLED when settings are loaded (currently this only happens once immediately after boot)
*
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
*
* 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: {"Autosave": {"enabled": true, "autoSaveAfterSec": 10, "autoSavePreset": 250, ...}}
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
enabled = top[FPSTR(_autoSaveEnabled)] | enabled;
autoSaveAfterSec = top[FPSTR(_autoSaveAfterSec)] | autoSaveAfterSec;
autoSaveAfterSec = (uint16_t) min(3600,max(10,(int)autoSaveAfterSec)); // bounds checking
autoSavePreset = top[FPSTR(_autoSavePreset)] | autoSavePreset;
autoSavePreset = (uint8_t) min(250,max(100,(int)autoSavePreset)); // bounds checking
applyAutoSaveOnBoot = top[FPSTR(_autoSaveApplyOnBoot)] | applyAutoSaveOnBoot;
DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(" config (re)loaded."));
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return true;
}
/*
* 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_AUTO_SAVE;
}
};
// strings to reduce flash memory usage (used more than twice)
const char AutoSaveUsermod::_name[] PROGMEM = "Autosave";
const char AutoSaveUsermod::_autoSaveEnabled[] PROGMEM = "enabled";
const char AutoSaveUsermod::_autoSaveAfterSec[] PROGMEM = "autoSaveAfterSec";
const char AutoSaveUsermod::_autoSavePreset[] PROGMEM = "autoSavePreset";
const char AutoSaveUsermod::_autoSaveApplyOnBoot[] PROGMEM = "autoSaveApplyOnBoot";

View File

@@ -0,0 +1,39 @@
# I2C 4 Line Display Usermod
First, thanks to the authors of the ssd11306_i2c_oled_u8g2 mod.
This usermod provides a four line display using either
128x32 or 128x64 OLED displays.
It's can operate independently, but starts to provide
a relatively complete on-device UI when paired with the
Rotary Encoder UI usermod. I strongly encourage you to use
them together.
[See the pair of usermods in action](https://www.youtube.com/watch?v=tITQY80rIOA)
## Installation
Copy and update the example `platformio_override.ini.sample`
from the Rotary Encoder UI usermode folder to the root directory of your particular build.
This file should be placed in the same directory as `platformio.ini`.
### Define Your Options
* `USERMOD_FOUR_LINE_DISPLAY` - define this to have this the Four Line Display mod included wled00\usermods_list.cpp - also tells Rotary Encoder usermod, if installed, that the display is available
* `FLD_PIN_SCL` - The display SCL pin, defaults to 5
* `FLD_PIN_SDA` - The display SDA pin, defaults to 4
All of the parameters can be configured using Usermods settings page, inluding GPIO pins.
### PlatformIO requirements
This usermod requires the `U8g2` and `Wire` libraries. See the
`platformio_override.ini.sample` found in the Rotary Encoder
UI usermod folder for how to include these using `platformio_override.ini`.
## Change Log
2021-02
* First public release
2021-04
* Adaptation for runtime configuration.

View File

@@ -0,0 +1,690 @@
#pragma once
#include "wled.h"
#include <U8x8lib.h> // from https://github.com/olikraus/u8g2/
//
// Insired by the v1 usermod: ssd1306_i2c_oled_u8g2
//
// 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.
//
// 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
//
//The SCL and SDA pins are defined here.
#ifdef ARDUINO_ARCH_ESP32
#ifndef FLD_PIN_SCL
#define FLD_PIN_SCL 22
#endif
#ifndef FLD_PIN_SDA
#define FLD_PIN_SDA 21
#endif
#else
#ifndef FLD_PIN_SCL
#define FLD_PIN_SCL 5
#endif
#ifndef FLD_PIN_SDA
#define FLD_PIN_SDA 4
#endif
#endif
// When to time out to the clock or blank the screen
// if SLEEP_MODE_ENABLED.
#define SCREEN_TIMEOUT_MS 60*1000 // 1 min
#define TIME_INDENT 0
#define DATE_INDENT 2
// Minimum time between redrawing screen in ms
#define USER_LOOP_REFRESH_RATE_MS 1000
// Extra char (+1) for null
#define LINE_BUFFER_SIZE 16+1
typedef enum {
FLD_LINE_BRIGHTNESS = 0,
FLD_LINE_EFFECT_SPEED,
FLD_LINE_EFFECT_INTENSITY,
FLD_LINE_MODE,
FLD_LINE_PALETTE,
FLD_LINE_TIME
} Line4Type;
typedef enum {
NONE = 0,
SSD1306, // U8X8_SSD1306_128X32_UNIVISION_HW_I2C
SH1106, // U8X8_SH1106_128X64_WINSTAR_HW_I2C
SSD1306_64, // U8X8_SSD1306_128X64_NONAME_HW_I2C
SSD1305, // U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C
SSD1305_64 // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C
} DisplayType;
class FourLineDisplayUsermod : public Usermod {
private:
bool initDone = false;
unsigned long lastTime = 0;
// HW interface & configuration
U8X8 *u8x8 = nullptr; // pointer to U8X8 display object
int8_t sclPin=FLD_PIN_SCL, sdaPin=FLD_PIN_SDA; // I2C pins for interfacing, get initialised in readFromConfig()
DisplayType type = SSD1306; // display type
bool flip = false; // flip display 180°
uint8_t contrast = 10; // screen contrast
uint8_t lineHeight = 1; // 1 row or 2 rows
uint32_t refreshRate = USER_LOOP_REFRESH_RATE_MS; // in ms
uint32_t screenTimeout = SCREEN_TIMEOUT_MS; // in ms
bool sleepMode = true; // allow screen sleep?
bool clockMode = false; // display clock
// Next variables hold the previous known values to determine if redraw is
// required.
String knownSsid = "";
IPAddress knownIp;
uint8_t knownBrightness = 0;
uint8_t knownEffectSpeed = 0;
uint8_t knownEffectIntensity = 0;
uint8_t knownMode = 0;
uint8_t knownPalette = 0;
uint8_t knownMinute = 99;
uint8_t knownHour = 99;
bool displayTurnedOff = false;
unsigned long lastUpdate = 0;
unsigned long lastRedraw = 0;
unsigned long overlayUntil = 0;
Line4Type lineType = FLD_LINE_BRIGHTNESS;
// Set to 2 or 3 to mark lines 2 or 3. Other values ignored.
byte markLineNum = 0;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _contrast[];
static const char _refreshRate[];
static const char _screenTimeOut[];
static const char _flip[];
static const char _sleepMode[];
static const char _clockMode[];
// If display does not work or looks corrupted check the
// constructor reference:
// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp
// or check the gallery:
// https://github.com/olikraus/u8g2/wiki/gallery
public:
// gets called once at boot. Do all initialization that doesn't depend on
// network here
void setup() {
if (type == NONE) return;
if (!pinManager.allocatePin(sclPin)) { sclPin = -1; type = NONE; return;}
if (!pinManager.allocatePin(sdaPin)) { pinManager.deallocatePin(sclPin); sclPin = sdaPin = -1; type = NONE; return; }
switch (type) {
case SSD1306:
#ifdef ESP8266
if (!(sclPin==5 && sdaPin==4))
u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_SW_I2C(sclPin, sdaPin); // SCL, SDA, reset
else
#endif
u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA
lineHeight = 1;
break;
case SH1106:
#ifdef ESP8266
if (!(sclPin==5 && sdaPin==4))
u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_SW_I2C(sclPin, sdaPin); // SCL, SDA, reset
else
#endif
u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA
lineHeight = 2;
break;
case SSD1306_64:
#ifdef ESP8266
if (!(sclPin==5 && sdaPin==4))
u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_SW_I2C(sclPin, sdaPin); // SCL, SDA, reset
else
#endif
u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA
lineHeight = 2;
break;
case SSD1305:
#ifdef ESP8266
if (!(sclPin==5 && sdaPin==4))
u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C(sclPin, sdaPin); // SCL, SDA, reset
else
#endif
u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA
lineHeight = 1;
break;
case SSD1305_64:
#ifdef ESP8266
if (!(sclPin==5 && sdaPin==4))
u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_SW_I2C(sclPin, sdaPin); // SCL, SDA, reset
else
#endif
u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA
lineHeight = 2;
break;
default:
u8x8 = nullptr;
type = NONE;
return;
}
(static_cast<U8X8*>(u8x8))->begin();
setFlipMode(flip);
setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
setPowerSave(0);
drawString(0, 0, "Loading...");
initDone = true;
}
// gets called every time WiFi is (re-)connected. Initialize own network
// interfaces here
void connected() {}
/**
* Da loop.
*/
void loop() {
if (millis() - lastUpdate < (clockMode?1000:refreshRate) || strip.isUpdating()) return;
lastUpdate = millis();
redraw(false);
}
/**
* Wrappers for screen drawing
*/
void setFlipMode(uint8_t mode) {
if (type==NONE) return;
(static_cast<U8X8*>(u8x8))->setFlipMode(mode);
}
void setContrast(uint8_t contrast) {
if (type==NONE) return;
(static_cast<U8X8*>(u8x8))->setContrast(contrast);
}
void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false) {
if (type==NONE) return;
(static_cast<U8X8*>(u8x8))->setFont(u8x8_font_chroma48medium8_r);
if (!ignoreLH && lineHeight==2) (static_cast<U8X8*>(u8x8))->draw1x2String(col, row, string);
else (static_cast<U8X8*>(u8x8))->drawString(col, row, string);
}
void draw2x2String(uint8_t col, uint8_t row, const char *string) {
if (type==NONE) return;
(static_cast<U8X8*>(u8x8))->setFont(u8x8_font_chroma48medium8_r);
(static_cast<U8X8*>(u8x8))->draw2x2String(col, row, string);
}
void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH=false) {
if (type==NONE) return;
(static_cast<U8X8*>(u8x8))->setFont(font);
if (!ignoreLH && lineHeight==2) (static_cast<U8X8*>(u8x8))->draw1x2Glyph(col, row, glyph);
else (static_cast<U8X8*>(u8x8))->drawGlyph(col, row, glyph);
}
uint8_t getCols() {
if (type==NONE) return 0;
return (static_cast<U8X8*>(u8x8))->getCols();
}
void clear() {
if (type==NONE) return;
(static_cast<U8X8*>(u8x8))->clear();
}
void setPowerSave(uint8_t save) {
if (type==NONE) return;
(static_cast<U8X8*>(u8x8))->setPowerSave(save);
}
/**
* Redraw the screen (but only if things have changed
* or if forceRedraw).
*/
void redraw(bool forceRedraw) {
static bool showName = false;
unsigned long now = millis();
if (type==NONE) return;
if (overlayUntil > 0) {
if (now >= overlayUntil) {
// Time to display the overlay has elapsed.
overlayUntil = 0;
forceRedraw = true;
} else {
// We are still displaying the overlay
// Don't redraw.
return;
}
}
// Check if values which are shown on display changed from the last time.
if (forceRedraw ||
(((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) ||
(knownIp != (apActive ? IPAddress(4, 3, 2, 1) : Network.localIP())) ||
(knownBrightness != bri) ||
(knownEffectSpeed != effectSpeed) ||
(knownEffectIntensity != effectIntensity) ||
(knownMode != strip.getMode()) ||
(knownPalette != strip.getSegment(0).palette)) {
knownHour = 99; // force time update
clear();
} else if (sleepMode && !displayTurnedOff && ((now - lastRedraw)/1000)%5 == 0) {
// change line every 5s
showName = !showName;
switch (lineType) {
case FLD_LINE_BRIGHTNESS:
lineType = FLD_LINE_EFFECT_SPEED;
break;
case FLD_LINE_MODE:
lineType = FLD_LINE_BRIGHTNESS;
break;
case FLD_LINE_PALETTE:
lineType = clockMode ? FLD_LINE_MODE : FLD_LINE_BRIGHTNESS;
break;
case FLD_LINE_EFFECT_SPEED:
lineType = FLD_LINE_EFFECT_INTENSITY;
break;
case FLD_LINE_EFFECT_INTENSITY:
lineType = FLD_LINE_PALETTE;
break;
default:
lineType = FLD_LINE_MODE;
break;
}
knownHour = 99; // force time update
} else {
// Nothing to change.
// Turn off display after 3 minutes with no change.
if(sleepMode && !displayTurnedOff && (millis() - lastRedraw > screenTimeout)) {
// We will still check if there is a change in redraw()
// and turn it back on if it changed.
clear(); // force screen clear
sleepOrClock(true);
} else if (displayTurnedOff && clockMode) {
showTime();
}
return;
}
// do not update lastRedraw marker if just switching row contenet
if (((now - lastRedraw)/1000)%5 != 0) lastRedraw = now;
// Turn the display back on
if (displayTurnedOff) sleepOrClock(false);
// Update last known values.
knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID();
knownIp = apActive ? IPAddress(4, 3, 2, 1) : Network.localIP();
knownBrightness = bri;
knownMode = strip.getMode();
knownPalette = strip.getSegment(0).palette;
knownEffectSpeed = effectSpeed;
knownEffectIntensity = effectIntensity;
// Do the actual drawing
// First row with Wifi name
drawGlyph(0, 0, 80, u8x8_font_open_iconic_embedded_1x1); // home icon
String ssidString = knownSsid.substring(0, getCols() > 1 ? getCols() - 2 : 0);
drawString(1, 0, ssidString.c_str());
// Print `~` char to indicate that SSID is longer, than our display
if (knownSsid.length() > getCols()) {
drawString(getCols() - 1, 0, "~");
}
// Second row with IP or Psssword
drawGlyph(0, lineHeight, 68, u8x8_font_open_iconic_embedded_1x1); // wifi icon
// Print password in AP mode and if led is OFF.
if (apActive && bri == 0) {
drawString(1, lineHeight, apPass);
} else {
// alternate IP address and server name
String secondLine = knownIp.toString();
if (showName && strcmp(serverDescription, "WLED") != 0) {
secondLine = serverDescription;
}
for (uint8_t i=secondLine.length(); i<getCols()-1; i++) secondLine += ' ';
drawString(1, lineHeight, secondLine.c_str());
}
// draw third and fourth row
drawLine(2, clockMode ? lineType : FLD_LINE_MODE);
drawLine(3, clockMode ? FLD_LINE_TIME : lineType);
drawGlyph(0, 2*lineHeight, 66 + (bri > 0 ? 3 : 0), u8x8_font_open_iconic_weather_2x2); // sun/moon icon
//if (markLineNum>1) drawGlyph(2, markLineNum*lineHeight, 66, u8x8_font_open_iconic_arrow_1x1); // arrow icon
}
void drawLine(uint8_t line, Line4Type lineType) {
char lineBuffer[LINE_BUFFER_SIZE];
switch(lineType) {
case FLD_LINE_BRIGHTNESS:
sprintf_P(lineBuffer, PSTR("Brightness %3d"), bri);
drawString(2, line*lineHeight, lineBuffer);
break;
case FLD_LINE_EFFECT_SPEED:
sprintf_P(lineBuffer, PSTR("FX Speed %3d"), effectSpeed);
drawString(2, line*lineHeight, lineBuffer);
break;
case FLD_LINE_EFFECT_INTENSITY:
sprintf_P(lineBuffer, PSTR("FX Intens. %3d"), effectIntensity);
drawString(2, line*lineHeight, lineBuffer);
break;
case FLD_LINE_MODE:
showCurrentEffectOrPalette(knownMode, JSON_mode_names, line);
break;
case FLD_LINE_PALETTE:
showCurrentEffectOrPalette(knownPalette, JSON_palette_names, line);
break;
case FLD_LINE_TIME:
showTime(false);
break;
default:
// unknown type, do nothing
break;
}
}
/**
* Display the current effect or palette (desiredEntry)
* on the appropriate line (row).
*/
void showCurrentEffectOrPalette(int knownMode, const char *qstring, uint8_t row) {
char lineBuffer[LINE_BUFFER_SIZE];
uint8_t qComma = 0;
bool insideQuotes = false;
uint8_t printedChars = 0;
char singleJsonSymbol;
// Find the mode name in JSON
for (size_t i = 0; i < strlen_P(qstring); i++) {
singleJsonSymbol = pgm_read_byte_near(qstring + i);
if (singleJsonSymbol == '\0') break;
switch (singleJsonSymbol) {
case '"':
insideQuotes = !insideQuotes;
break;
case '[':
case ']':
break;
case ',':
qComma++;
default:
if (!insideQuotes || (qComma != knownMode)) break;
lineBuffer[printedChars++] = singleJsonSymbol;
}
if ((qComma > knownMode) || (printedChars >= getCols()-2) || printedChars >= sizeof(lineBuffer)-2) break;
}
for (;printedChars < getCols()-2 && printedChars < sizeof(lineBuffer)-2; printedChars++) lineBuffer[printedChars]=' ';
lineBuffer[printedChars] = 0;
drawString(2, row*lineHeight, lineBuffer);
}
/**
* If there screen is off or in clock is displayed,
* this will return true. This allows us to throw away
* the first input from the rotary encoder but
* to wake up the screen.
*/
bool wakeDisplay() {
knownHour = 99;
if (displayTurnedOff) {
// Turn the display back on
sleepOrClock(false);
redraw(true);
return true;
}
return false;
}
/**
* Allows you to show up to two lines as overlay for a
* period of time.
* Clears the screen and prints on the middle two lines.
*/
void overlay(const char* line1, const char *line2, long showHowLong) {
if (displayTurnedOff) {
// Turn the display back on
sleepOrClock(false);
}
// Print the overlay
clear();
if (line1) drawString(0, 1*lineHeight, line1);
if (line2) drawString(0, 2*lineHeight, line2);
overlayUntil = millis() + showHowLong;
}
/**
* Line 3 or 4 (last two lines) can be marked with an
* arrow in the first column. Pass 2 or 3 to this to
* specify which line to mark with an arrow.
* Any other values are ignored.
*/
void setMarkLine(byte newMarkLineNum) {
if (newMarkLineNum == 2 || newMarkLineNum == 3) {
markLineNum = newMarkLineNum;
}
else {
markLineNum = 0;
}
}
/**
* Enable sleep (turn the display off) or clock mode.
*/
void sleepOrClock(bool enabled) {
if (enabled) {
if (clockMode) showTime();
else setPowerSave(1);
displayTurnedOff = true;
} else {
setPowerSave(0);
displayTurnedOff = false;
}
}
/**
* Display the current date and time in large characters
* on the middle rows. Based 24 or 12 hour depending on
* the useAMPM configuration.
*/
void showTime(bool fullScreen = true) {
char lineBuffer[LINE_BUFFER_SIZE];
updateLocalTime();
byte minuteCurrent = minute(localTime);
byte hourCurrent = hour(localTime);
byte secondCurrent = second(localTime);
if (knownMinute == minuteCurrent && knownHour == hourCurrent) {
// Time hasn't changed.
if (!fullScreen) return;
} else {
//if (fullScreen) clear();
}
knownMinute = minuteCurrent;
knownHour = hourCurrent;
byte currentMonth = month(localTime);
sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(currentMonth), day(localTime));
if (fullScreen)
draw2x2String(DATE_INDENT, lineHeight==1 ? 0 : lineHeight, lineBuffer); // adjust for 8 line displays
else
drawString(2, lineHeight*3, lineBuffer);
byte showHour = hourCurrent;
boolean isAM = false;
if (useAMPM) {
if (showHour == 0) {
showHour = 12;
isAM = true;
}
else if (showHour > 12) {
showHour -= 12;
isAM = false;
}
else {
isAM = true;
}
}
sprintf_P(lineBuffer, (secondCurrent%2 || !fullScreen) ? PSTR("%2d:%02d") : PSTR("%2d %02d"), (useAMPM ? showHour : hourCurrent), minuteCurrent);
// For time, we always use LINE_HEIGHT of 2 since
// we are printing it big.
if (fullScreen) {
draw2x2String(TIME_INDENT+2, lineHeight*2, lineBuffer);
sprintf_P(lineBuffer, PSTR("%02d"), secondCurrent);
if (useAMPM) drawString(12+(fullScreen?0:2), lineHeight*2, (isAM ? "AM" : "PM"), true);
else drawString(12, lineHeight*2+1, lineBuffer, true); // even with double sized rows print seconds in 1 line
} else {
drawString(9+(useAMPM?0:2), lineHeight*3, lineBuffer);
if (useAMPM) drawString(12+(fullScreen?0:2), lineHeight*3, (isAM ? "AM" : "PM"), true);
}
}
/*
* 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 data = user.createNestedArray(F("4LineDisplay"));
//data.add(F("Loaded."));
//}
/*
* 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
*/
//void addToJsonState(JsonObject& root) {
//}
/*
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
//void readFromJsonState(JsonObject& root) {
// if (!initDone) return; // prevent crash on boot applyPreset()
//}
/*
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
* If you want to force saving the current state, use serializeConfig() in your loop().
*
* CAUTION: serializeConfig() will initiate a filesystem write operation.
* It might cause the LEDs to stutter and will cause flash wear if called too often.
* Use it sparingly and always in the loop, never in network callbacks!
*
* addToConfig() will also not yet add your setting to one of the settings pages automatically.
* To make that work you still have to add the setting to the HTML, xml.cpp and set.cpp manually.
*
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
*/
void addToConfig(JsonObject& root) {
JsonObject top = root.createNestedObject(FPSTR(_name));
JsonArray i2c_pin = top.createNestedArray("pin");
i2c_pin.add(sclPin);
i2c_pin.add(sdaPin);
top["type"] = type;
top[FPSTR(_flip)] = (bool) flip;
top[FPSTR(_contrast)] = contrast;
top[FPSTR(_refreshRate)] = refreshRate/1000;
top[FPSTR(_screenTimeOut)] = screenTimeout/1000;
top[FPSTR(_sleepMode)] = (bool) sleepMode;
top[FPSTR(_clockMode)] = (bool) clockMode;
DEBUG_PRINTLN(F("4 Line Display config saved."));
}
/*
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
* This is called by WLED when settings are loaded (currently this only happens once immediately after boot)
*
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
*/
bool readFromConfig(JsonObject& root) {
bool needsRedraw = false;
DisplayType newType = type;
int8_t newScl = sclPin;
int8_t newSda = sdaPin;
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
newScl = top["pin"][0] | newScl;
newSda = top["pin"][1] | newSda;
newType = top["type"] | newType;
flip = top[FPSTR(_flip)] | flip;
contrast = top[FPSTR(_contrast)] | contrast;
refreshRate = (top[FPSTR(_refreshRate)] | refreshRate/1000) * 1000;
screenTimeout = (top[FPSTR(_screenTimeOut)] | screenTimeout/1000) * 1000;
sleepMode = top[FPSTR(_sleepMode)] | sleepMode;
clockMode = top[FPSTR(_clockMode)] | clockMode;
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
// first run: reading from cfg.json
sclPin = newScl;
sdaPin = newSda;
type = newType;
DEBUG_PRINTLN(F(" config loaded."));
} else {
// changing parameters from settings page
if (sclPin!=newScl || sdaPin!=newSda || type!=newType) {
if (type != NONE) delete (static_cast<U8X8*>(u8x8));
pinManager.deallocatePin(sclPin);
pinManager.deallocatePin(sdaPin);
sclPin = newScl;
sdaPin = newSda;
if (newScl<0 || newSda<0) {
type = NONE;
return true;
} else type = newType;
setup();
needsRedraw |= true;
}
setContrast(contrast);
setFlipMode(flip);
if (needsRedraw && !wakeDisplay()) redraw(true);
DEBUG_PRINTLN(F(" config (re)loaded."));
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return true;
}
/*
* 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_FOUR_LINE_DISP;
}
};
// strings to reduce flash memory usage (used more than twice)
const char FourLineDisplayUsermod::_name[] PROGMEM = "4LineDisplay";
const char FourLineDisplayUsermod::_contrast[] PROGMEM = "contrast";
const char FourLineDisplayUsermod::_refreshRate[] PROGMEM = "refreshRateSec";
const char FourLineDisplayUsermod::_screenTimeOut[] PROGMEM = "screenTimeOutSec";
const char FourLineDisplayUsermod::_flip[] PROGMEM = "flip";
const char FourLineDisplayUsermod::_sleepMode[] PROGMEM = "sleepMode";
const char FourLineDisplayUsermod::_clockMode[] PROGMEM = "clockMode";

View File

@@ -0,0 +1,33 @@
# Mode Sort
v2 usermod that provides data about modes and
palettes to other usermods. Notably it provides:
* A direct method for a mode or palette name
* Ability to retrieve mode and palette names in
alphabetical order
```char **getModesQStrings()```
Provides an array of char* (pointers) to the names of the
palettes within JSON_mode_names, in the same order as
JSON_mode_names. These strings end in double quote (")
(or \0 if there is a problem).
```byte *getModesAlphaIndexes()```
An array of byte designating the indexes of names of the
modes in alphabetical order. "Solid" will always remain
at the front of the list.
```char **getPalettesQStrings()```
Provides an array of char* (pointers) to the names of the
palettes within JSON_palette_names, in the same order as
JSON_palette_names. These strings end in double quote (")
(or \0 if there is a problem).
```byte *getPalettesAlphaIndexes()```
An array of byte designating the indexes of names of the
palettes in alphabetical order. "Default" and those
starting with "(" will always remain at the front of the list.

View File

@@ -0,0 +1,248 @@
#pragma once
#include "wled.h"
//
// v2 usermod that provides data about modes and
// palettes to other usermods. Notably it provides:
// * A direct method for a mode or palette name
// * Ability to retrieve mode and palette names in
// alphabetical order
//
// char **getModesQStrings()
// Provides an array of char* (pointers) to the names of the
// palettes within JSON_mode_names, in the same order as
// JSON_mode_names. These strings end in double quote (")
// (or \0 if there is a problem).
//
// byte *getModesAlphaIndexes()
// An array of byte designating the indexes of names of the
// modes in alphabetical order. "Solid" will always remain
// at the front of the list.
//
// char **getPalettesQStrings()
// Provides an array of char* (pointers) to the names of the
// palettes within JSON_palette_names, in the same order as
// JSON_palette_names. These strings end in double quote (")
// (or \0 if there is a problem).
//
// byte *getPalettesAlphaIndexes()
// An array of byte designating the indexes of names of the
// palettes in alphabetical order. "Default" and those
// starting with "(" will always remain at the front of the list.
//
// Number of modes at the start of the list to not sort
#define MODE_SORT_SKIP_COUNT 1
// Which list is being sorted
char **listBeingSorted = nullptr;
/**
* Modes and palettes are stored as strings that
* end in a quote character. Compare two of them.
* We are comparing directly within either
* JSON_mode_names or JSON_palette_names.
*/
int re_qstringCmp(const void *ap, const void *bp) {
char *a = listBeingSorted[*((byte *)ap)];
char *b = listBeingSorted[*((byte *)bp)];
int i = 0;
do {
char aVal = pgm_read_byte_near(a + i);
if (aVal >= 97 && aVal <= 122) {
// Lowercase
aVal -= 32;
}
char bVal = pgm_read_byte_near(b + i);
if (bVal >= 97 && bVal <= 122) {
// Lowercase
bVal -= 32;
}
// Relly we shouldn't ever get to '\0'
if (aVal == '"' || bVal == '"' || aVal == '\0' || bVal == '\0') {
// We're done. one is a substring of the other
// or something happenend and the quote didn't stop us.
if (aVal == bVal) {
// Same value, probably shouldn't happen
// with this dataset
return 0;
}
else if (aVal == '"' || aVal == '\0') {
return -1;
}
else {
return 1;
}
}
if (aVal == bVal) {
// Same characters. Move to the next.
i++;
continue;
}
// We're done
if (aVal < bVal) {
return -1;
}
else {
return 1;
}
} while (true);
// We shouldn't get here.
return 0;
}
class ModeSortUsermod : public Usermod {
private:
// Pointers the start of the mode names within JSON_mode_names
char **modes_qstrings = nullptr;
// Array of mode indexes in alphabetical order.
byte *modes_alpha_indexes = nullptr;
// Pointers the start of the palette names within JSON_palette_names
char **palettes_qstrings = nullptr;
// Array of palette indexes in alphabetical order.
byte *palettes_alpha_indexes = nullptr;
public:
/**
* setup() is called once at boot. WiFi is not yet connected at this point.
* You can use it to initialize variables, sensors or similar.
*/
void setup() {
// Sort the modes and palettes on startup
// as they are guarantted to change.
sortModesAndPalettes();
}
char **getModesQStrings() {
return modes_qstrings;
}
byte *getModesAlphaIndexes() {
return modes_alpha_indexes;
}
char **getPalettesQStrings() {
return palettes_qstrings;
}
byte *getPalettesAlphaIndexes() {
return palettes_alpha_indexes;
}
/**
* This Usermod doesn't have anything for loop.
*/
void loop() {}
/**
* Sort the modes and palettes to the index arrays
* modes_alpha_indexes and palettes_alpha_indexes.
*/
void sortModesAndPalettes() {
modes_qstrings = re_findModeStrings(JSON_mode_names, strip.getModeCount());
modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT);
palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount());
palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount());
int skipPaletteCount = 1;
while (true) {
// How many palette names start with '*' and should not be sorted?
// (Also skipping the first one, 'Default').
if (pgm_read_byte_near(palettes_qstrings[skipPaletteCount]) == '*') {
skipPaletteCount++;
}
else {
break;
}
}
re_sortModes(palettes_qstrings, palettes_alpha_indexes, strip.getPaletteCount(), skipPaletteCount);
}
byte *re_initIndexArray(int numModes) {
byte *indexes = (byte *)malloc(sizeof(byte) * numModes);
for (byte i = 0; i < numModes; i++) {
indexes[i] = i;
}
return indexes;
}
/**
* Return an array of mode or palette names from the JSON string.
* They don't end in '\0', they end in '"'.
*/
char **re_findModeStrings(const char json[], int numModes) {
char **modeStrings = (char **)malloc(sizeof(char *) * numModes);
uint8_t modeIndex = 0;
bool insideQuotes = false;
// advance past the mark for markLineNum that may exist.
char singleJsonSymbol;
// Find the mode name in JSON
bool complete = false;
for (size_t i = 0; i < strlen_P(json); i++) {
singleJsonSymbol = pgm_read_byte_near(json + i);
switch (singleJsonSymbol) {
case '"':
insideQuotes = !insideQuotes;
if (insideQuotes) {
// We have a new mode or palette
modeStrings[modeIndex] = (char *)(json + i + 1);
}
break;
case '[':
break;
case ']':
complete = true;
break;
case ',':
modeIndex++;
default:
if (!insideQuotes) {
break;
}
}
if (complete) {
break;
}
}
return modeStrings;
}
/**
* Sort either the modes or the palettes using quicksort.
*/
void re_sortModes(char **modeNames, byte *indexes, int count, int numSkip) {
listBeingSorted = modeNames;
qsort(indexes + numSkip, count - numSkip, sizeof(byte), re_qstringCmp);
listBeingSorted = nullptr;
}
/*
* 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
*/
void addToJsonState(JsonObject &root) {}
/*
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
void readFromJsonState(JsonObject &root) {}
/*
* 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_MODE_SORT;
}
};

View File

@@ -0,0 +1,48 @@
[platformio]
default_envs = d1_mini
; default_envs = esp32dev
[env:esp32dev]
board = esp32dev
platform = espressif32@3.2
build_unflags = ${common.build_unflags}
build_flags =
${common.build_flags_esp32}
-D USERMOD_MODE_SORT
-D USERMOD_FOUR_LINE_DISPLAY -D FLD_PIN_SCL=22 -D FLD_PIN_SDA=21
-D USERMOD_ROTARY_ENCODER_UI -D ENCODER_DT_PIN=18 -D ENCODER_CLK_PIN=5 -D ENCODER_SW_PIN=19
-D USERMOD_AUTO_SAVE -D AUTOSAVE_PRESET_NUM=1
-D LEDPIN=16 -D BTNPIN=13
upload_speed = 460800
lib_ignore =
ESPAsyncTCP
ESPAsyncUDP
[env:d1_mini]
board = d1_mini
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
upload_speed = 460800
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags =
${common.build_flags_esp8266}
-D USERMOD_MODE_SORT
-D USERMOD_FOUR_LINE_DISPLAY -D FLD_PIN_SCL=5 -D FLD_PIN_SDA=4
-D USERMOD_ROTARY_ENCODER_UI -D ENCODER_DT_PIN=12 -D ENCODER_CLK_PIN=14 -D ENCODER_SW_PIN=13
-D USERMOD_AUTO_SAVE -D AUTOSAVE_PRESET_NUM=1
-D LEDPIN=3 -D BTNPIN=0
monitor_filters = esp8266_exception_decoder
[env]
lib_deps =
fastled/FastLED @ 3.3.2
NeoPixelBus @ 2.6.0
ESPAsyncTCP @ 1.2.0
ESPAsyncUDP
AsyncTCP @ 1.0.3
IRremoteESP8266 @ 2.7.3
https://github.com/lorol/LITTLEFS.git
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.0
U8g2@~2.27.2
Wire

View File

@@ -0,0 +1,33 @@
# Rotary Encoder UI Usermod
First, thanks to the authors of other Rotary Encoder usermods.
This usermod starts to provide a relatively complete on-device
UI when paired with the Four Line Display usermod. I strongly
encourage you to try them together.
[See the pair of usermods in action](https://www.youtube.com/watch?v=tITQY80rIOA)
## Installation
Copy and update the example `platformio_override.ini.sample` to the root directory of your particular build.
This file should be placed in the same directory as `platformio.ini`.
### Define Your Options
* `USERMOD_ROTARY_ENCODER_UI` - define this to have this user mod included wled00\usermods_list.cpp
* `USERMOD_FOUR_LINE_DISPLAY` - define this to have this the Four Line Display mod included wled00\usermods_list.cpp - also tells this usermod that the display is available (see the Four Line Display usermod `readme.md` for more details)
* `ENCODER_DT_PIN` - The encoders DT pin, defaults to 12
* `ENCODER_CLK_PIN` - The encoders CLK pin, defaults to 14
* `ENCODER_SW_PIN` - The encoders SW pin, defaults to 13
### PlatformIO requirements
No special requirements.
Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`.
## Change Log
2021-02
* First public release

View File

@@ -0,0 +1,401 @@
#pragma once
#include "wled.h"
//
// Inspired by the v1 usermods
// * rotary_encoder_change_brightness
// * rotary_encoder_change_effect
//
// v2 usermod that provides a rotary encoder-based UI.
//
// This usermod allows you to control:
//
// * Brightness
// * Selected Effect
// * Effect Speed
// * Effect Intensity
// * Palette
//
// Change between modes by pressing a button.
//
// Dependencies
// * This usermod REQURES the ModeSortUsermod
// * This Usermod works best coupled with
// FourLineDisplayUsermod.
//
#ifndef ENCODER_DT_PIN
#define ENCODER_DT_PIN 12
#endif
#ifndef ENCODER_CLK_PIN
#define ENCODER_CLK_PIN 14
#endif
#ifndef ENCODER_SW_PIN
#define ENCODER_SW_PIN 13
#endif
#ifndef USERMOD_FOUR_LINE_DISPLAY
// These constants won't be defined if we aren't using FourLineDisplay.
#define FLD_LINE_3_BRIGHTNESS 0
#define FLD_LINE_3_EFFECT_SPEED 0
#define FLD_LINE_3_EFFECT_INTENSITY 0
#define FLD_LINE_3_PALETTE 0
#endif
// The last UI state
#define LAST_UI_STATE 4
class RotaryEncoderUIUsermod : public Usermod {
private:
int fadeAmount = 10; // Amount to change every step (brightness)
unsigned long currentTime;
unsigned long loopTime;
const int pinA = ENCODER_DT_PIN; // DT from encoder
const int pinB = ENCODER_CLK_PIN; // CLK from encoder
const int pinC = ENCODER_SW_PIN; // SW from encoder
unsigned char select_state = 0; // 0: brightness, 1: effect, 2: effect speed
unsigned char button_state = HIGH;
unsigned char prev_button_state = HIGH;
#ifdef USERMOD_FOUR_LINE_DISPLAY
FourLineDisplayUsermod *display;
#else
void* display = nullptr;
#endif
byte *modes_alpha_indexes = nullptr;
byte *palettes_alpha_indexes = nullptr;
unsigned char Enc_A;
unsigned char Enc_B;
unsigned char Enc_A_prev = 0;
bool currentEffectAndPaleeteInitialized = false;
uint8_t effectCurrentIndex = 0;
uint8_t effectPaletteIndex = 0;
public:
/*
* setup() is called once at boot. WiFi is not yet connected at this point.
* You can use it to initialize variables, sensors or similar.
*/
void setup()
{
pinMode(pinA, INPUT_PULLUP);
pinMode(pinB, INPUT_PULLUP);
pinMode(pinC, INPUT_PULLUP);
currentTime = millis();
loopTime = currentTime;
ModeSortUsermod *modeSortUsermod = (ModeSortUsermod*) usermods.lookup(USERMOD_ID_MODE_SORT);
modes_alpha_indexes = modeSortUsermod->getModesAlphaIndexes();
palettes_alpha_indexes = modeSortUsermod->getPalettesAlphaIndexes();
#ifdef USERMOD_FOUR_LINE_DISPLAY
// This Usermod uses FourLineDisplayUsermod for the best experience.
// But it's optional. But you want it.
display = (FourLineDisplayUsermod*) usermods.lookup(USERMOD_ID_FOUR_LINE_DISP);
if (display != nullptr) {
display->setLineThreeType(FLD_LINE_3_BRIGHTNESS);
display->setMarkLine(3);
}
#endif
}
/*
* connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces
*/
void connected()
{
//Serial.println("Connected to WiFi!");
}
/*
* loop() is called continuously. Here you can check for events, read sensors, etc.
*
* Tips:
* 1. You can use "if (WLED_CONNECTED)" to check for a successful network connection.
* Additionally, "if (WLED_MQTT_CONNECTED)" is available to check for a connection to an MQTT broker.
*
* 2. Try to avoid using the delay() function. NEVER use delays longer than 10 milliseconds.
* Instead, use a timer check as shown here.
*/
void loop()
{
currentTime = millis(); // get the current elapsed time
// Initialize effectCurrentIndex and effectPaletteIndex to
// current state. We do it here as (at least) effectCurrent
// is not yet initialized when setup is called.
if (!currentEffectAndPaleeteInitialized) {
findCurrentEffectAndPalette();
}
if (currentTime >= (loopTime + 2)) // 2ms since last check of encoder = 500Hz
{
button_state = digitalRead(pinC);
if (prev_button_state != button_state)
{
if (button_state == LOW)
{
prev_button_state = button_state;
char newState = select_state + 1;
if (newState > LAST_UI_STATE) newState = 0;
bool changedState = true;
if (display != nullptr) {
switch(newState) {
case 0:
changedState = changeState("Brightness", FLD_LINE_3_BRIGHTNESS, 3);
break;
case 1:
changedState = changeState("Select FX", FLD_LINE_3_EFFECT_SPEED, 2);
break;
case 2:
changedState = changeState("FX Speed", FLD_LINE_3_EFFECT_SPEED, 3);
break;
case 3:
changedState = changeState("FX Intensity", FLD_LINE_3_EFFECT_INTENSITY, 3);
break;
case 4:
changedState = changeState("Palette", FLD_LINE_3_PALETTE, 3);
break;
}
}
if (changedState) {
select_state = newState;
}
}
else
{
prev_button_state = button_state;
}
}
int Enc_A = digitalRead(pinA); // Read encoder pins
int Enc_B = digitalRead(pinB);
if ((!Enc_A) && (Enc_A_prev))
{ // A has gone from high to low
if (Enc_B == HIGH)
{ // B is high so clockwise
switch(select_state) {
case 0:
changeBrightness(true);
break;
case 1:
changeEffect(true);
break;
case 2:
changeEffectSpeed(true);
break;
case 3:
changeEffectIntensity(true);
break;
case 4:
changePalette(true);
break;
}
}
else if (Enc_B == LOW)
{ // B is low so counter-clockwise
switch(select_state) {
case 0:
changeBrightness(false);
break;
case 1:
changeEffect(false);
break;
case 2:
changeEffectSpeed(false);
break;
case 3:
changeEffectIntensity(false);
break;
case 4:
changePalette(false);
break;
}
}
}
Enc_A_prev = Enc_A; // Store value of A for next time
loopTime = currentTime; // Updates loopTime
}
}
void findCurrentEffectAndPalette() {
currentEffectAndPaleeteInitialized = true;
for (uint8_t i = 0; i < strip.getModeCount(); i++) {
byte value = modes_alpha_indexes[i];
if (modes_alpha_indexes[i] == effectCurrent) {
effectCurrentIndex = i;
break;
}
}
for (uint8_t i = 0; i < strip.getPaletteCount(); i++) {
byte value = palettes_alpha_indexes[i];
if (palettes_alpha_indexes[i] == strip.getSegment(0).palette) {
effectPaletteIndex = i;
break;
}
}
}
boolean changeState(const char *stateName, byte lineThreeMode, byte markedLine) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display != nullptr) {
if (display->wakeDisplay()) {
// Throw away wake up input
return false;
}
display->overlay("Mode change", stateName, 1500);
display->setLineThreeType(lineThreeMode);
display->setMarkLine(markedLine);
}
#endif
return true;
}
void lampUdated() {
bool fxChanged = strip.setEffectConfig(effectCurrent, effectSpeed, effectIntensity, effectPalette);
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification)
// 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa
colorUpdated(NOTIFIER_CALL_MODE_DIRECT_CHANGE);
updateInterfaces(NOTIFIER_CALL_MODE_DIRECT_CHANGE);
}
void changeBrightness(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
// Throw away wake up input
return;
}
#endif
if (increase) {
bri = (bri + fadeAmount <= 255) ? (bri + fadeAmount) : 255;
}
else {
bri = (bri - fadeAmount >= 0) ? (bri - fadeAmount) : 0;
}
lampUdated();
}
void changeEffect(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
// Throw away wake up input
return;
}
#endif
if (increase) {
effectCurrentIndex = (effectCurrentIndex + 1 >= strip.getModeCount()) ? 0 : (effectCurrentIndex + 1);
}
else {
effectCurrentIndex = (effectCurrentIndex - 1 < 0) ? (strip.getModeCount() - 1) : (effectCurrentIndex - 1);
}
effectCurrent = modes_alpha_indexes[effectCurrentIndex];
lampUdated();
}
void changeEffectSpeed(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
// Throw away wake up input
return;
}
#endif
if (increase) {
effectSpeed = (effectSpeed + fadeAmount <= 255) ? (effectSpeed + fadeAmount) : 255;
}
else {
effectSpeed = (effectSpeed - fadeAmount >= 0) ? (effectSpeed - fadeAmount) : 0;
}
lampUdated();
}
void changeEffectIntensity(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
// Throw away wake up input
return;
}
#endif
if (increase) {
effectIntensity = (effectIntensity + fadeAmount <= 255) ? (effectIntensity + fadeAmount) : 255;
}
else {
effectIntensity = (effectIntensity - fadeAmount >= 0) ? (effectIntensity - fadeAmount) : 0;
}
lampUdated();
}
void changePalette(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
// Throw away wake up input
return;
}
#endif
if (increase) {
effectPaletteIndex = (effectPaletteIndex + 1 >= strip.getPaletteCount()) ? 0 : (effectPaletteIndex + 1);
}
else {
effectPaletteIndex = (effectPaletteIndex - 1 < 0) ? (strip.getPaletteCount() - 1) : (effectPaletteIndex - 1);
}
effectPalette = palettes_alpha_indexes[effectPaletteIndex];
lampUdated();
}
/*
* 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)
{
int reading = 20;
//this code adds "u":{"Light":[20," lux"]} to the info object
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
}
*/
/*
* 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
*/
void addToJsonState(JsonObject &root)
{
//root["user0"] = userVar0;
}
/*
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
void readFromJsonState(JsonObject &root)
{
userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value
//if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
}
/*
* 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_ROTARY_ENC_UI;
}
};

Binary file not shown.

View File

@@ -29,13 +29,12 @@
#define IBN 5100
#define PALETTE_SOLID_WRAP (paletteBlend == 1 || paletteBlend == 3)
/*
* No blinking. Just plain old static light.
*/
uint16_t WS2812FX::mode_static(void) {
fill(SEGCOLOR(0));
return (SEGMENT.getOption(SEG_OPTION_TRANSITIONAL)) ? FRAMETIME : 500; //update faster if in transition
return (SEGMENT.getOption(SEG_OPTION_TRANSITIONAL)) ? FRAMETIME : 350; //update faster if in transition
}
@@ -43,30 +42,24 @@ uint16_t WS2812FX::mode_static(void) {
* Blink/strobe function
* Alternate between color1 and color2
* if(strobe == true) then create a strobe effect
* NOTE: Maybe re-rework without timer
*/
uint16_t WS2812FX::blink(uint32_t color1, uint32_t color2, bool strobe, bool do_palette) {
uint16_t stateTime = SEGENV.aux1;
uint32_t cycleTime = (255 - SEGMENT.speed)*20;
uint32_t onTime = 0;
uint32_t offTime = cycleTime;
if (!strobe) {
onTime = (cycleTime * SEGMENT.intensity) >> 8;
offTime = cycleTime - onTime;
uint32_t onTime = FRAMETIME;
if (!strobe) onTime += ((cycleTime * SEGMENT.intensity) >> 8);
cycleTime += FRAMETIME*2;
uint32_t it = now / cycleTime;
uint32_t rem = now % cycleTime;
bool on = false;
if (it != SEGENV.step //new iteration, force on state for one frame, even if set time is too brief
|| rem <= onTime) {
on = true;
}
stateTime = ((SEGENV.aux0 & 1) == 0) ? onTime : offTime;
stateTime += 20;
if (now - SEGENV.step > stateTime)
{
SEGENV.aux0++;
SEGENV.aux1 = stateTime;
SEGENV.step = now;
}
SEGENV.step = it; //save previous iteration
uint32_t color = ((SEGENV.aux0 & 1) == 0) ? color1 : color2;
uint32_t color = on ? color1 : color2;
if (color == color1 && do_palette)
{
for(uint16_t i = 0; i < SEGLEN; i++) {
@@ -233,9 +226,9 @@ uint16_t WS2812FX::mode_random_color(void) {
/*
* Lights every LED in a random color. Changes all LED at the same time
// * to new random colors.
* to new random colors.
*/
uint16_t WS2812FX::mode_dynamic(void) {
uint16_t WS2812FX::dynamic(boolean smooth=false) {
if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed
if(SEGENV.call == 0) {
@@ -252,12 +245,31 @@ uint16_t WS2812FX::mode_dynamic(void) {
SEGENV.step = it;
}
for (uint16_t i = 0; i < SEGLEN; i++) {
setPixelColor(i, color_wheel(SEGENV.data[i]));
}
if (smooth) {
for (uint16_t i = 0; i < SEGLEN; i++) {
blendPixelColor(i, color_wheel(SEGENV.data[i]),16);
}
} else {
for (uint16_t i = 0; i < SEGLEN; i++) {
setPixelColor(i, color_wheel(SEGENV.data[i]));
}
}
return FRAMETIME;
}
/*
* Original effect "Dynamic"
*/
uint16_t WS2812FX::mode_dynamic(void) {
return dynamic(false);
}
/*
* effect "Dynamic" with smoth color-fading
*/
uint16_t WS2812FX::mode_dynamic_smooth(void) {
return dynamic(true);
}
/*
* Does the "standby-breathing" of well known i-Devices.
@@ -426,28 +438,45 @@ uint16_t WS2812FX::mode_theater_chase_rainbow(void) {
/*
* Running lights effect with smooth sine transition base.
*/
uint16_t WS2812FX::running_base(bool saw) {
uint16_t WS2812FX::running_base(bool saw, bool dual=false) {
uint8_t x_scale = SEGMENT.intensity >> 2;
uint32_t counter = (now * SEGMENT.speed) >> 9;
for(uint16_t i = 0; i < SEGLEN; i++) {
uint8_t s = 0;
uint8_t a = i*x_scale - counter;
uint16_t a = i*x_scale - counter;
if (saw) {
a &= 0xFF;
if (a < 16)
{
a = 192 + a*8;
} else {
a = map(a,16,255,64,192);
}
a = 255 - a;
}
s = sin8(a);
setPixelColor(i, color_blend(color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), s));
uint8_t s = dual ? sin_gap(a) : sin8(a);
uint32_t ca = color_blend(SEGCOLOR(1), color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), s);
if (dual) {
uint16_t b = (SEGLEN-1-i)*x_scale - counter;
uint8_t t = sin_gap(b);
uint32_t cb = color_blend(SEGCOLOR(1), color_from_palette(i, true, PALETTE_SOLID_WRAP, 2), t);
ca = color_blend(ca, cb, 127);
}
setPixelColor(i, ca);
}
return FRAMETIME;
}
/*
* Running lights in opposite directions.
* Idea: Make the gap width controllable with a third slider in the future
*/
uint16_t WS2812FX::mode_running_dual(void) {
return running_base(false, true);
}
/*
* Running lights effect with smooth sine transition.
*/
@@ -526,7 +555,7 @@ uint16_t WS2812FX::dissolve(uint32_t color) {
}
}
if (SEGENV.call > (255 - SEGMENT.speed) + 15)
if (SEGENV.call > (255 - SEGMENT.speed) + 15U)
{
SEGENV.aux0 = !SEGENV.aux0;
SEGENV.call = 0;
@@ -574,7 +603,7 @@ uint16_t WS2812FX::mode_sparkle(void) {
/*
* Lights all LEDs in the color. Flashes single white pixels randomly.
* Lights all LEDs in the color. Flashes single col 1 pixels randomly. (List name: Sparkle Dark)
* Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
*/
uint16_t WS2812FX::mode_flash_sparkle(void) {
@@ -582,12 +611,14 @@ uint16_t WS2812FX::mode_flash_sparkle(void) {
setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
}
if(random8(5) == 0) {
SEGENV.aux0 = random16(SEGLEN); // aux0 stores the random led index
setPixelColor(SEGENV.aux0, SEGCOLOR(1));
return 20;
}
return 20 + (uint16_t)(255-SEGMENT.speed);
if (now - SEGENV.aux0 > SEGENV.step) {
if(random8((255-SEGMENT.intensity) >> 4) == 0) {
setPixelColor(random16(SEGLEN), SEGCOLOR(1)); //flash
}
SEGENV.step = now;
SEGENV.aux0 = 255-SEGMENT.speed;
}
return FRAMETIME;
}
@@ -600,13 +631,16 @@ uint16_t WS2812FX::mode_hyper_sparkle(void) {
setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
}
if(random8(5) < 2) {
for(uint16_t i = 0; i < MAX(1, SEGLEN/3); i++) {
setPixelColor(random16(SEGLEN), SEGCOLOR(1));
if (now - SEGENV.aux0 > SEGENV.step) {
if(random8((255-SEGMENT.intensity) >> 4) == 0) {
for(uint16_t i = 0; i < MAX(1, SEGLEN/3); i++) {
setPixelColor(random16(SEGLEN), SEGCOLOR(1));
}
}
return 20;
SEGENV.step = now;
SEGENV.aux0 = 255-SEGMENT.speed;
}
return 20 + (uint16_t)(255-SEGMENT.speed);
return FRAMETIME;
}
@@ -617,22 +651,25 @@ uint16_t WS2812FX::mode_multi_strobe(void) {
for(uint16_t i = 0; i < SEGLEN; i++) {
setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 1));
}
//blink(SEGCOLOR(0), SEGCOLOR(1), true, true);
uint16_t delay = 50 + 20*(uint16_t)(255-SEGMENT.speed);
uint16_t count = 2 * ((SEGMENT.speed / 10) + 1);
if(SEGENV.step < count) {
if((SEGENV.step & 1) == 0) {
for(uint16_t i = 0; i < SEGLEN; i++) {
setPixelColor(i, SEGCOLOR(0));
}
delay = 20;
SEGENV.aux0 = 50 + 20*(uint16_t)(255-SEGMENT.speed);
uint16_t count = 2 * ((SEGMENT.intensity / 10) + 1);
if(SEGENV.aux1 < count) {
if((SEGENV.aux1 & 1) == 0) {
fill(SEGCOLOR(0));
SEGENV.aux0 = 15;
} else {
delay = 50;
SEGENV.aux0 = 50;
}
}
SEGENV.step = (SEGENV.step + 1) % (count + 1);
return delay;
if (now - SEGENV.aux0 > SEGENV.step) {
SEGENV.aux1++;
if (SEGENV.aux1 > count) SEGENV.aux1 = 0;
SEGENV.step = now;
}
return FRAMETIME;
}
/*
@@ -974,22 +1011,6 @@ uint16_t WS2812FX::mode_running_color(void) {
return running(SEGCOLOR(0), SEGCOLOR(1));
}
/*
* Alternating red/blue pixels running.
*/
uint16_t WS2812FX::mode_running_red_blue(void) {
return running(RED, BLUE);
}
/*
* Alternating red/green pixels running.
*/
uint16_t WS2812FX::mode_merry_christmas(void) {
return running(RED, GREEN);
}
/*
* Alternating red/white pixels running.
*/
@@ -1023,7 +1044,7 @@ uint16_t WS2812FX::mode_running_random(void) {
}
SEGENV.step++;
if (SEGENV.step > ((255-SEGMENT.intensity) >> 4))
if (SEGENV.step > (uint8_t)((255-SEGMENT.intensity) >> 4))
{
SEGENV.step = 0;
}
@@ -1246,7 +1267,7 @@ uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2, bool all)
for (uint16_t i = idexB; i < idexR; i++) setPixelColor(i, color2);
}
} else { //regular dot-only mode
uint8_t size = 1 + SEGMENT.intensity >> 3;
uint8_t size = 1 + (SEGMENT.intensity >> 3);
if (size > SEGLEN/2) size = 1+ SEGLEN/2;
for (uint8_t i=0; i <= size; i++) {
setPixelColor(idexR+i, color1);
@@ -1324,14 +1345,6 @@ uint16_t WS2812FX::tricolor_chase(uint32_t color1, uint32_t color2) {
}
/*
* Alternating white/red/black pixels running. PLACEHOLDER
*/
uint16_t WS2812FX::mode_circus_combustus(void) {
return tricolor_chase(RED, WHITE);
}
/*
* Tricolor chase mode
*/
@@ -1448,8 +1461,8 @@ uint16_t WS2812FX::mode_tricolor_fade(void)
}
byte stp = prog; // % 256
uint32_t color = 0;
for(uint16_t i = 0; i < SEGLEN; i++) {
uint32_t color;
if (stage == 2) {
color = color_blend(color_from_palette(i, true, PALETTE_SOLID_WRAP, 2), color2, stp);
} else if (stage == 1) {
@@ -1557,9 +1570,9 @@ uint16_t WS2812FX::mode_oscillate(void)
if (SEGENV.call == 0)
{
oscillators[0] = {SEGLEN/4, SEGLEN/8, 1, 1};
oscillators[1] = {SEGLEN/4*3, SEGLEN/8, 1, 2};
oscillators[2] = {SEGLEN/4*2, SEGLEN/8, -1, 1};
oscillators[0] = {(int16_t)(SEGLEN/4), (int8_t)(SEGLEN/8), 1, 1};
oscillators[1] = {(int16_t)(SEGLEN/4*3), (int8_t)(SEGLEN/8), 1, 2};
oscillators[2] = {(int16_t)(SEGLEN/4*2), (int8_t)(SEGLEN/8), -1, 1};
}
uint32_t cycleTime = 20 + (2 * (uint32_t)(255 - SEGMENT.speed));
@@ -1600,40 +1613,42 @@ uint16_t WS2812FX::mode_oscillate(void)
uint16_t WS2812FX::mode_lightning(void)
{
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)
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);
if (SEGENV.step == 0)
if (SEGENV.aux1 == 0) //init, leader flash
{
SEGENV.aux0 = random8(3, 3 + SEGMENT.intensity/20); //number of flashes
bri = 52;
SEGENV.aux1 = 1;
SEGENV.aux1 = random8(4, 4 + SEGMENT.intensity/20); //number of flashes
SEGENV.aux1 *= 2;
bri = 52; //leader has lower brightness
SEGENV.aux0 = 200; //200ms delay after leader
}
fill(SEGCOLOR(1));
if (SEGENV.aux1) {
if (SEGENV.aux1 > 3 && !(SEGENV.aux1 & 0x01)) { //flash on even number >2
for (int i = ledstart; i < ledstart + ledlen; i++)
{
if (SEGMENT.palette == 0)
{
setPixelColor(i,bri,bri,bri,bri);
} else {
setPixelColor(i,color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, bri));
}
setPixelColor(i,color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, bri));
}
SEGENV.aux1--;
SEGENV.step = millis();
//return random8(4, 10); // each flash only lasts one frame/every 24ms... originally 4-10 milliseconds
} else {
if (millis() - SEGENV.step > SEGENV.aux0) {
SEGENV.aux1--;
if (SEGENV.aux1 < 2) SEGENV.aux1 = 0;
SEGENV.aux0 = (50 + random8(100)); //delay between flashes
if (SEGENV.aux1 == 2) {
SEGENV.aux0 = (random8(255 - SEGMENT.speed) * 100); // delay between strikes
}
SEGENV.step = millis();
}
SEGENV.aux1 = 0;
SEGENV.step++;
return random8(4, 10); // each flash only lasts 4-10 milliseconds
}
SEGENV.aux1 = 1;
if (SEGENV.step == 1) return (200); // longer delay until next flash after the leader
if (SEGENV.step <= SEGENV.aux0) return (50 + random8(100)); // shorter delay between strokes
SEGENV.step = 0;
return (random8(255 - SEGMENT.speed) * 100); // delay between strikes
return FRAMETIME;
}
@@ -1760,19 +1775,22 @@ uint16_t WS2812FX::mode_fire_2012()
if (it != SEGENV.step)
{
uint8_t ignition = max(7,SEGLEN/10); // ignition area: 10% of segment length or minimum 7 pixels
// Step 1. Cool down every cell a little
for (uint16_t i = 0; i < SEGLEN; i++) {
SEGENV.data[i] = qsub8(heat[i], random8(0, (((20 + SEGMENT.speed /3) * 10) / SEGLEN) + 2));
uint8_t temp = qsub8(heat[i], random8(0, (((20 + SEGMENT.speed /3) * 10) / SEGLEN) + 2));
heat[i] = (temp==0 && i<ignition) ? 2 : temp; // prevent ignition area from becoming black
}
// Step 2. Heat from each cell drifts 'up' and diffuses a little
for (uint16_t k= SEGLEN -1; k > 1; k--) {
heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
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(7);
uint8_t y = random8(ignition);
if (y < SEGLEN) heat[y] = qadd8(heat[y], random8(160,255));
}
SEGENV.step = it;
@@ -1903,7 +1921,6 @@ uint16_t WS2812FX::mode_noise16_2()
for (uint16_t i = 0; i < SEGLEN; i++) {
uint16_t shift_x = SEGENV.step >> 6; // x as a function of time
uint16_t shift_y = SEGENV.step/42;
uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field
@@ -1967,7 +1984,7 @@ uint16_t WS2812FX::mode_colortwinkle()
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
CRGB fastled_col, prev;
fract8 fadeUpAmount = 8 + (SEGMENT.speed/4), fadeDownAmount = 5 + (SEGMENT.speed/7);
fract8 fadeUpAmount = _brightness>28 ? 8 + (SEGMENT.speed>>2) : 68-_brightness, fadeDownAmount = _brightness>28 ? 8 + (SEGMENT.speed>>3) : 68-_brightness;
for (uint16_t i = 0; i < SEGLEN; i++) {
fastled_col = col_to_crgb(getPixelColor(i));
prev = fastled_col;
@@ -3062,11 +3079,11 @@ uint16_t WS2812FX::mode_drip(void)
numDrops = 1 + (SEGMENT.intensity >> 6);
float gravity = -0.001 - (SEGMENT.speed/50000.0);
float gravity = -0.0005 - (SEGMENT.speed/50000.0);
gravity *= SEGLEN;
int sourcedrop = 12;
for (int j=0;j<numDrops;j++) {
for (uint8_t j=0;j<numDrops;j++) {
if (drops[j].colIndex == 0) { //init
drops[j].pos = SEGLEN-1; // start at end
drops[j].vel = 0; // speed
@@ -3077,7 +3094,7 @@ uint16_t WS2812FX::mode_drip(void)
setPixelColor(SEGLEN-1,color_blend(BLACK,SEGCOLOR(0), sourcedrop));// water source
if (drops[j].colIndex==1) {
if (drops[j].col>255) drops[j].col=255;
setPixelColor(int(drops[j].pos),color_blend(BLACK,SEGCOLOR(0),drops[j].col));
setPixelColor(uint16_t(drops[j].pos),color_blend(BLACK,SEGCOLOR(0),drops[j].col));
drops[j].col += map(SEGMENT.speed, 0, 255, 1, 6); // swelling
@@ -3092,8 +3109,9 @@ uint16_t WS2812FX::mode_drip(void)
if (drops[j].pos < 0) drops[j].pos = 0;
drops[j].vel += gravity;
for (int i=1;i<7-drops[j].colIndex;i++) { // some minor math so we don't expand bouncing droplets
setPixelColor(int(drops[j].pos)+i,color_blend(BLACK,SEGCOLOR(0),drops[j].col/i)); //spread pixel with fade while falling
for (uint16_t i=1;i<7-drops[j].colIndex;i++) { // some minor math so we don't expand bouncing droplets
uint16_t pos = constrain(uint16_t(drops[j].pos) +i, 0, SEGLEN-1); //this is BAD, returns a pos >= SEGLEN occasionally
setPixelColor(pos,color_blend(BLACK,SEGCOLOR(0),drops[j].col/i)); //spread pixel with fade while falling
}
if (drops[j].colIndex > 2) { // during bounce, some water is on the floor
@@ -3120,6 +3138,59 @@ uint16_t WS2812FX::mode_drip(void)
}
/*
* Tetris or Stacking (falling bricks) Effect
* by Blaz Kristan (https://github.com/blazoncek, https://blaz.at/home)
*/
typedef struct Tetris {
float pos;
float speed;
uint32_t col;
} tetris;
uint16_t WS2812FX::mode_tetrix(void) {
uint16_t dataSize = sizeof(tetris);
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
Tetris* drop = reinterpret_cast<Tetris*>(SEGENV.data);
// initialize dropping on first call or segment full
if (SEGENV.call == 0 || SEGENV.aux1 >= SEGLEN) {
SEGENV.aux1 = 0; // reset brick stack size
SEGENV.step = 0;
fill(SEGCOLOR(1));
return 250; // short wait
}
if (SEGENV.step == 0) { //init
drop->speed = 0.0238 * (SEGMENT.speed ? (SEGMENT.speed>>3)+1 : random8(6,40)); // set speed
drop->pos = SEGLEN-1; // start at end of segment
drop->col = color_from_palette(random8(0,15)<<4,false,false,0); // limit color choices so there is enough HUE gap
SEGENV.step = 1; // drop state (0 init, 1 forming, 2 falling)
SEGENV.aux0 = (SEGMENT.intensity ? (SEGMENT.intensity>>5)+1 : random8(1,5)) * (1+(SEGLEN>>6)); // size of brick
}
if (SEGENV.step == 1) { // forming
if (random8()>>6) { // random drop
SEGENV.step = 2; // fall
}
}
if (SEGENV.step > 1) { // falling
if (drop->pos > SEGENV.aux1) { // fall until top of stack
drop->pos -= drop->speed; // may add gravity as: speed += gravity
if (int(drop->pos) < SEGENV.aux1) drop->pos = SEGENV.aux1;
for (uint16_t i=int(drop->pos); i<SEGLEN; i++) setPixelColor(i,i<int(drop->pos)+SEGENV.aux0 ? drop->col : SEGCOLOR(1));
} else { // we hit bottom
SEGENV.step = 0; // go back to init
SEGENV.aux1 += SEGENV.aux0; // increase the stack size
if (SEGENV.aux1 >= SEGLEN) return 1000; // wait for a second
}
}
return FRAMETIME;
}
/*
/ Plasma Effect
/ adapted from https://github.com/atuline/FastLED-Demos/blob/master/plasma/plasma.ino
@@ -3129,8 +3200,8 @@ uint16_t WS2812FX::mode_plasma(void) {
uint8_t thatPhase = beatsin8(7,-64,64);
for (int i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set color & brightness based on a wave as follows:
uint8_t colorIndex = cubicwave8((i*(1+ 3*(SEGMENT.speed >> 5)))+(thisPhase) & 0xFF)/2 // factor=23 // Create a wave and add a phase change and add another wave with its own phase change.
+ cos8((i*(1+ 2*(SEGMENT.speed >> 5)))+(thatPhase) & 0xFF)/2; // factor=15 // Hey, you can even change the frequencies if you wish.
uint8_t colorIndex = cubicwave8(((i*(1+ 3*(SEGMENT.speed >> 5)))+(thisPhase)) & 0xFF)/2 // factor=23 // Create a wave and add a phase change and add another wave with its own phase change.
+ cos8(((i*(1+ 2*(SEGMENT.speed >> 5)))+(thatPhase)) & 0xFF)/2; // factor=15 // Hey, you can even change the frequencies if you wish.
uint8_t thisBright = qsub8(colorIndex, beatsin8(6,0, (255 - SEGMENT.intensity)|0x01 ));
CRGB color = ColorFromPalette(currentPalette, colorIndex, thisBright, LINEARBLEND);
setPixelColor(i, color.red, color.green, color.blue);
@@ -3559,15 +3630,15 @@ uint16_t WS2812FX::mode_chunchun(void)
{
fill(SEGCOLOR(1));
uint16_t counter = now*(6 + (SEGMENT.speed >> 4));
uint16_t numBirds = SEGLEN >> 2;
uint16_t span = SEGMENT.intensity << 8;
uint16_t numBirds = 2 + (SEGLEN >> 3); // 2 + 1/8 of a segment
uint16_t span = (SEGMENT.intensity << 8) / numBirds;
for (uint16_t i = 0; i < numBirds; i++)
{
counter -= span/numBirds;
int megumin = sin16(counter) + 0x8000;
counter -= span;
uint16_t megumin = sin16(counter) + 0x8000;
uint32_t bird = (megumin * SEGLEN) >> 16;
uint32_t c = color_from_palette((i * 255)/ numBirds, false, true, 0);
uint32_t c = color_from_palette((i * 255)/ numBirds, false, false, 0); // no palette wrapping
setPixelColor(bird, c);
}
return FRAMETIME;
@@ -3751,5 +3822,282 @@ uint16_t WS2812FX::mode_blends(void) {
shift += 3;
}
return FRAMETIME;
}
typedef struct TvSim {
uint32_t totalTime = 0;
uint32_t fadeTime = 0;
uint32_t startTime = 0;
uint32_t elapsed = 0;
uint32_t pixelNum = 0;
uint16_t sliderValues = 0;
uint32_t sceeneStart = 0;
uint32_t sceeneDuration = 0;
uint16_t sceeneColorHue = 0;
uint8_t sceeneColorSat = 0;
uint8_t sceeneColorBri = 0;
uint8_t actualColorR = 0;
uint8_t actualColorG = 0;
uint8_t actualColorB = 0;
uint16_t pr = 0; // Prev R, G, B
uint16_t pg = 0;
uint16_t pb = 0;
} tvSim;
/*
TV Simulator
Modified and adapted to WLED by Def3nder, based on "Fake TV Light for Engineers" by Phillip Burgess https://learn.adafruit.com/fake-tv-light-for-engineers/arduino-sketch
*/
uint16_t WS2812FX::mode_tv_simulator(void) {
uint16_t nr, ng, nb, r, g, b, i, hue;
uint8_t sat, bri, j;
if (!SEGENV.allocateData(sizeof(tvSim))) return mode_static(); //allocation failed
TvSim* tvSimulator = reinterpret_cast<TvSim*>(SEGENV.data);
uint8_t colorSpeed = map(SEGMENT.speed, 0, UINT8_MAX, 1, 20);
uint8_t colorIntensity = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 30);
i = SEGMENT.speed << 8 | SEGMENT.intensity;
if (i != tvSimulator->sliderValues) {
tvSimulator->sliderValues = i;
SEGENV.aux1 = 0;
}
// create a new sceene
if (((millis() - tvSimulator->sceeneStart) >= tvSimulator->sceeneDuration) || SEGENV.aux1 == 0) {
tvSimulator->sceeneStart = millis(); // remember the start of the new sceene
tvSimulator->sceeneDuration = random16(60* 250* colorSpeed, 60* 750 * colorSpeed); // duration of a "movie sceene" which has similar colors (5 to 15 minutes with max speed slider)
tvSimulator->sceeneColorHue = random16( 0, 768); // random start color-tone for the sceene
tvSimulator->sceeneColorSat = random8 ( 100, 130 + colorIntensity); // random start color-saturation for the sceene
tvSimulator->sceeneColorBri = random8 ( 200, 240); // random start color-brightness for the sceene
SEGENV.aux1 = 1;
SEGENV.aux0 = 0;
}
// slightly change the color-tone in this sceene
if ( SEGENV.aux0 == 0) {
// hue change in both directions
j = random8(4 * colorIntensity);
hue = (random8() < 128) ? ((j < tvSimulator->sceeneColorHue) ? tvSimulator->sceeneColorHue - j : 767 - tvSimulator->sceeneColorHue - j) : // negative
((j + tvSimulator->sceeneColorHue) < 767 ? tvSimulator->sceeneColorHue + j : tvSimulator->sceeneColorHue + j - 767) ; // positive
// saturation
j = random8(2 * colorIntensity);
sat = (tvSimulator->sceeneColorSat - j) < 0 ? 0 : tvSimulator->sceeneColorSat - j;
// brightness
j = random8(100);
bri = (tvSimulator->sceeneColorBri - j) < 0 ? 0 : tvSimulator->sceeneColorBri - j;
// calculate R,G,B from HSV
// Source: https://blog.adafruit.com/2012/03/14/constant-brightness-hsb-to-rgb-algorithm/
{ // just to create a local scope for the variables
uint8_t temp[5], n = (hue >> 8) % 3;
uint8_t x = ((((hue & 255) * sat) >> 8) * bri) >> 8;
uint8_t s = ( (256 - sat) * bri) >> 8;
temp[0] = temp[3] = s;
temp[1] = temp[4] = x + s;
temp[2] = bri - x;
tvSimulator->actualColorR = temp[n + 2];
tvSimulator->actualColorG = temp[n + 1];
tvSimulator->actualColorB = temp[n ];
}
}
// Apply gamma correction, further expand to 16/16/16
nr = (uint8_t)gamma8(tvSimulator->actualColorR) * 257; // New R/G/B
ng = (uint8_t)gamma8(tvSimulator->actualColorG) * 257;
nb = (uint8_t)gamma8(tvSimulator->actualColorB) * 257;
if (SEGENV.aux0 == 0) { // initialize next iteration
SEGENV.aux0 = 1;
// randomize total duration and fade duration for the actual color
tvSimulator->totalTime = random16(250, 2500); // Semi-random pixel-to-pixel time
tvSimulator->fadeTime = random16(0, tvSimulator->totalTime); // Pixel-to-pixel transition time
if (random8(10) < 3) tvSimulator->fadeTime = 0; // Force scene cut 30% of time
tvSimulator->startTime = millis();
} // end of initialization
// how much time is elapsed ?
tvSimulator->elapsed = millis() - tvSimulator->startTime;
// fade from prev volor to next color
if (tvSimulator->elapsed < tvSimulator->fadeTime) {
r = map(tvSimulator->elapsed, 0, tvSimulator->fadeTime, tvSimulator->pr, nr);
g = map(tvSimulator->elapsed, 0, tvSimulator->fadeTime, tvSimulator->pg, ng);
b = map(tvSimulator->elapsed, 0, tvSimulator->fadeTime, tvSimulator->pb, nb);
} else { // Avoid divide-by-zero in map()
r = nr;
g = ng;
b = nb;
}
// set strip color
for (i = 0; i < SEGLEN; i++) {
setPixelColor(i, r >> 8, g >> 8, b >> 8); // Quantize to 8-bit
}
// if total duration has passed, remember last color and restart the loop
if ( tvSimulator->elapsed >= tvSimulator->totalTime) {
tvSimulator->pr = nr; // Prev RGB = new RGB
tvSimulator->pg = ng;
tvSimulator->pb = nb;
SEGENV.aux0 = 0;
}
return FRAMETIME;
}
/*
Aurora effect
*/
//CONFIG
#define BACKLIGHT 5
#define W_MAX_COUNT 20 //Number of simultaneous waves
#define W_MAX_SPEED 6 //Higher number, higher speed
#define W_WIDTH_FACTOR 6 //Higher number, smaller waves
class AuroraWave {
private:
uint16_t ttl;
CRGB basecolor;
float basealpha;
uint16_t age;
uint16_t width;
float center;
bool goingleft;
float speed_factor;
bool alive = true;
public:
void init(uint32_t segment_length, CRGB color) {
ttl = random(500, 1501);
basecolor = color;
basealpha = random(60, 101) / (float)100;
age = 0;
width = random(segment_length / 20, segment_length / W_WIDTH_FACTOR); //half of width to make math easier
if (!width) width = 1;
center = random(101) / (float)100 * segment_length;
goingleft = random(0, 2) == 0;
speed_factor = (random(10, 31) / (float)100 * W_MAX_SPEED / 255);
alive = true;
}
CRGB getColorForLED(int ledIndex) {
if(ledIndex < center - width || ledIndex > center + width) return 0; //Position out of range of this wave
CRGB rgb;
//Offset of this led from center of wave
//The further away from the center, the dimmer the LED
float offset = ledIndex - center;
if (offset < 0) offset = -offset;
float offsetFactor = offset / width;
//The age of the wave determines it brightness.
//At half its maximum age it will be the brightest.
float ageFactor = 0.1;
if((float)age / ttl < 0.5) {
ageFactor = (float)age / (ttl / 2);
} else {
ageFactor = (float)(ttl - age) / ((float)ttl * 0.5);
}
//Calculate color based on above factors and basealpha value
float factor = (1 - offsetFactor) * ageFactor * basealpha;
rgb.r = basecolor.r * factor;
rgb.g = basecolor.g * factor;
rgb.b = basecolor.b * factor;
return rgb;
};
//Change position and age of wave
//Determine if its sill "alive"
void update(uint32_t segment_length, uint32_t speed) {
if(goingleft) {
center -= speed_factor * speed;
} else {
center += speed_factor * speed;
}
age++;
if(age > ttl) {
alive = false;
} else {
if(goingleft) {
if(center + width < 0) {
alive = false;
}
} else {
if(center - width > segment_length) {
alive = false;
}
}
}
};
bool stillAlive() {
return alive;
};
};
uint16_t WS2812FX::mode_aurora(void) {
//aux1 = Wavecount
//aux2 = Intensity in last loop
AuroraWave* waves;
if(SEGENV.aux0 != SEGMENT.intensity || SEGENV.call == 0) {
//Intensity slider changed or first call
SEGENV.aux1 = ((float)SEGMENT.intensity / 255) * W_MAX_COUNT;
SEGENV.aux0 = SEGMENT.intensity;
if(!SEGENV.allocateData(sizeof(AuroraWave) * SEGENV.aux1)) {
return mode_static(); //allocation failed
}
waves = reinterpret_cast<AuroraWave*>(SEGENV.data);
for(int i = 0; i < SEGENV.aux1; i++) {
waves[i].init(SEGLEN, col_to_crgb(color_from_palette(random8(), false, false, random(0, 3))));
}
} else {
waves = reinterpret_cast<AuroraWave*>(SEGENV.data);
}
for(int i = 0; i < SEGENV.aux1; i++) {
//Update values of wave
waves[i].update(SEGLEN, SEGMENT.speed);
if(!(waves[i].stillAlive())) {
//If a wave dies, reinitialize it starts over.
waves[i].init(SEGLEN, col_to_crgb(color_from_palette(random8(), false, false, random(0, 3))));
}
}
//Loop through LEDs to determine color
for(int i = 0; i < SEGLEN; i++) {
CRGB mixedRgb = CRGB(BACKLIGHT, BACKLIGHT, BACKLIGHT);
//For each LED we must check each wave if it is "active" at this position.
//If there are multiple waves active on a LED we multiply their values.
for(int j = 0; j < SEGENV.aux1; j++) {
CRGB rgb = waves[j].getColorForLED(i);
if(rgb != CRGB(0)) {
mixedRgb += rgb;
}
}
setPixelColor(i, mixedRgb[0], mixedRgb[1], mixedRgb[2], BACKLIGHT);
}
return FRAMETIME;
}

View File

@@ -24,15 +24,11 @@
Modified for WLED
*/
#include "wled.h"
#ifndef WS2812FX_h
#define WS2812FX_h
#ifdef ESP32_MULTISTRIP
#include "../usermods/esp32_multistrip/NpbWrapper.h"
#else
#include "NpbWrapper.h"
#endif
#include "const.h"
#define FASTLED_INTERNAL //remove annoying pragma messages
@@ -59,16 +55,17 @@
/* each segment uses 52 bytes of SRAM memory, so if you're application fails because of
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
#ifdef ESP8266
#define MAX_NUM_SEGMENTS 12
#define MAX_NUM_SEGMENTS 12
/* How many color transitions can run at once */
#define MAX_NUM_TRANSITIONS 8
/* How much data bytes all segments combined may allocate */
#define MAX_SEGMENT_DATA 2048
#else
#define MAX_NUM_SEGMENTS 16
#ifndef MAX_NUM_SEGMENTS
#define MAX_NUM_SEGMENTS 16
#endif
/* How much data bytes all segments combined may allocate */
#ifdef ESP8266
#define MAX_SEGMENT_DATA 2048
#else
#define MAX_SEGMENT_DATA 8192
#define MAX_NUM_TRANSITIONS 16
#define MAX_SEGMENT_DATA 8192
#endif
#define LED_SKIP_AMOUNT 1
@@ -76,11 +73,11 @@
#define NUM_COLORS 3 /* number of colors per segment */
#define SEGMENT _segments[_segment_index]
#define SEGCOLOR(x) gamma32(_segments[_segment_index].colors[x])
#define SEGCOLOR(x) _colors_t[x]
#define SEGENV _segment_runtimes[_segment_index]
#define SEGLEN _virtualSegmentLength
#define SEGACT SEGMENT.stop
#define SPEED_FORMULA_L 5 + (50*(255 - SEGMENT.speed))/SEGLEN
#define SPEED_FORMULA_L 5U + (50U*(255U - SEGMENT.speed))/SEGLEN
#define RESET_RUNTIME memset(_segment_runtimes, 0, sizeof(_segment_runtimes))
// some common colors
@@ -116,7 +113,7 @@
#define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE )
#define IS_SELECTED ((SEGMENT.options & SELECTED ) == SELECTED )
#define MODE_COUNT 116
#define MODE_COUNT 118
#define FX_MODE_STATIC 0
#define FX_MODE_BLINK 1
@@ -156,13 +153,13 @@
#define FX_MODE_TRAFFIC_LIGHT 35
#define FX_MODE_COLOR_SWEEP_RANDOM 36
#define FX_MODE_RUNNING_COLOR 37
#define FX_MODE_RUNNING_RED_BLUE 38
#define FX_MODE_AURORA 38
#define FX_MODE_RUNNING_RANDOM 39
#define FX_MODE_LARSON_SCANNER 40
#define FX_MODE_COMET 41
#define FX_MODE_FIREWORKS 42
#define FX_MODE_RAIN 43
#define FX_MODE_MERRY_CHRISTMAS 44
#define FX_MODE_TETRIX 44
#define FX_MODE_FIRE_FLICKER 45
#define FX_MODE_GRADIENT 46
#define FX_MODE_LOADING 47
@@ -170,7 +167,7 @@
#define FX_MODE_POLICE_ALL 49
#define FX_MODE_TWO_DOTS 50
#define FX_MODE_TWO_AREAS 51
#define FX_MODE_CIRCUS_COMBUSTUS 52
#define FX_MODE_RUNNING_DUAL 52
#define FX_MODE_HALLOWEEN 53
#define FX_MODE_TRICOLOR_CHASE 54
#define FX_MODE_TRICOLOR_WIPE 55
@@ -234,18 +231,24 @@
#define FX_MODE_WASHING_MACHINE 113
#define FX_MODE_CANDY_CANE 114
#define FX_MODE_BLENDS 115
#define FX_MODE_TV_SIMULATOR 116
#define FX_MODE_DYNAMIC_SMOOTH 117
class WS2812FX {
typedef uint16_t (WS2812FX::*mode_ptr)(void);
// pre show callback
typedef void (*show_callback) (void);
static WS2812FX* instance;
// segment parameters
public:
typedef struct Segment { // 24 bytes
typedef struct Segment { // 25 (28 in memory?) bytes
uint16_t start;
uint16_t stop; //segment invalid if stop == 0
uint16_t offset;
uint8_t speed;
uint8_t intensity;
uint8_t palette;
@@ -254,14 +257,40 @@ class WS2812FX {
uint8_t grouping, spacing;
uint8_t opacity;
uint32_t colors[NUM_COLORS];
void setOption(uint8_t n, bool val)
bool setColor(uint8_t slot, uint32_t c, uint8_t segn) { //returns true if changed
if (slot >= NUM_COLORS || segn >= MAX_NUM_SEGMENTS) return false;
if (c == colors[slot]) return false;
ColorTransition::startTransition(opacity, colors[slot], instance->_transitionDur, segn, slot);
colors[slot] = c; return true;
}
void setOpacity(uint8_t o, uint8_t segn) {
if (segn >= MAX_NUM_SEGMENTS) return;
if (opacity == o) return;
ColorTransition::startTransition(opacity, colors[0], instance->_transitionDur, segn, 0);
opacity = o;
}
/*uint8_t actualOpacity() { //respects On/Off state
if (!getOption(SEG_OPTION_ON)) return 0;
return opacity;
}*/
void setOption(uint8_t n, bool val, uint8_t segn = 255)
{
//bool prevOn = false;
//if (n == SEG_OPTION_ON) prevOn = getOption(SEG_OPTION_ON);
if (val) {
options |= 0x01 << n;
} else
{
options &= ~(0x01 << n);
}
//transitions on segment on/off don't work correctly at this point
/*if (n == SEG_OPTION_ON && segn < MAX_NUM_SEGMENTS && getOption(SEG_OPTION_ON) != prevOn) {
if (getOption(SEG_OPTION_ON)) {
ColorTransition::startTransition(0, colors[0], instance->_transitionDur, segn, 0);
} else {
ColorTransition::startTransition(opacity, colors[0], instance->_transitionDur, segn, 0);
}
}*/
}
bool getOption(uint8_t n)
{
@@ -304,10 +333,10 @@ class WS2812FX {
bool allocateData(uint16_t len){
if (data && _dataLen == len) return true; //already allocated
deallocateData();
if (WS2812FX::_usedSegmentData + len > MAX_SEGMENT_DATA) return false; //not enough memory
if (WS2812FX::instance->_usedSegmentData + len > MAX_SEGMENT_DATA) return false; //not enough memory
data = new (std::nothrow) byte[len];
if (!data) return false; //allocation failed
WS2812FX::_usedSegmentData += len;
WS2812FX::instance->_usedSegmentData += len;
_dataLen = len;
memset(data, 0, len);
return true;
@@ -315,7 +344,7 @@ class WS2812FX {
void deallocateData(){
delete[] data;
data = nullptr;
WS2812FX::_usedSegmentData -= _dataLen;
WS2812FX::instance->_usedSegmentData -= _dataLen;
_dataLen = 0;
}
@@ -345,7 +374,86 @@ class WS2812FX {
bool _requiresReset = false;
} segment_runtime;
typedef struct ColorTransition { // 12 bytes
uint32_t colorOld = 0;
uint32_t transitionStart;
uint16_t transitionDur;
uint8_t segment = 0xFF; //lower 6 bits: the segment this transition is for (255 indicates transition not in use/available) upper 2 bits: color channel
uint8_t briOld = 0;
static void startTransition(uint8_t oldBri, uint32_t oldCol, uint16_t dur, uint8_t segn, uint8_t slot) {
if (segn >= MAX_NUM_SEGMENTS || slot >= NUM_COLORS || dur == 0) return;
if (instance->_brightness == 0) return; //do not need transitions if master bri is off
uint8_t tIndex = 0xFF; //none found
uint16_t tProgression = 0;
uint8_t s = segn + (slot << 6); //merge slot and segment into one byte
for (uint8_t i = 0; i < MAX_NUM_TRANSITIONS; i++) {
uint8_t tSeg = instance->transitions[i].segment;
//see if this segment + color already has a running transition
if (tSeg == s) {
tIndex = i; break;
}
if (tSeg == 0xFF) { //free transition
tIndex = i; tProgression = 0xFFFF;
}
}
if (tIndex == 0xFF) { //no slot found yet
for (uint8_t i = 0; i < MAX_NUM_TRANSITIONS; i++) {
//find most progressed transition to overwrite
uint16_t prog = instance->transitions[i].progress();
if (prog > tProgression) {
tIndex = i; tProgression = prog;
}
}
}
ColorTransition& t = instance->transitions[tIndex];
if (t.segment == s) //this is an active transition on the same segment+color
{
t.briOld = t.currentBri();
t.colorOld = t.currentColor(oldCol);
} else {
t.briOld = oldBri;
t.colorOld = oldCol;
uint8_t prevSeg = t.segment & 0x3F;
if (prevSeg < MAX_NUM_SEGMENTS) instance->_segments[prevSeg].setOption(SEG_OPTION_TRANSITIONAL, false);
}
t.transitionDur = dur;
t.transitionStart = millis();
t.segment = s;
instance->_segments[segn].setOption(SEG_OPTION_TRANSITIONAL, true);
//refresh immediately, required for Solid mode
if (instance->_segment_runtimes[segn].next_time > t.transitionStart + 22) instance->_segment_runtimes[segn].next_time = t.transitionStart;
}
uint16_t progress(bool allowEnd = false) { //transition progression between 0-65535
uint32_t timeNow = millis();
if (timeNow - transitionStart > transitionDur) {
if (allowEnd) {
uint8_t segn = segment & 0x3F;
if (segn < MAX_NUM_SEGMENTS) instance->_segments[segn].setOption(SEG_OPTION_TRANSITIONAL, false);
segment = 0xFF;
}
return 0xFFFF;
}
uint32_t elapsed = timeNow - transitionStart;
uint32_t prog = elapsed * 0xFFFF / transitionDur;
return (prog > 0xFFFF) ? 0xFFFF : prog;
}
uint32_t currentColor(uint32_t colorNew) {
return instance->color_blend(colorOld, colorNew, progress(true), true);
}
uint8_t currentBri() {
uint8_t segn = segment & 0x3F;
if (segn >= MAX_NUM_SEGMENTS) return 0;
uint8_t briNew = instance->_segments[segn].opacity;
uint32_t prog = progress() + 1;
return ((briNew * prog) + (briOld * (0x10000 - prog))) >> 16;
}
} color_transition;
WS2812FX() {
WS2812FX::instance = this;
//assign each member of the _mode[] array to its respective function reference
_mode[FX_MODE_STATIC] = &WS2812FX::mode_static;
_mode[FX_MODE_BLINK] = &WS2812FX::mode_blink;
@@ -383,13 +491,13 @@ class WS2812FX {
_mode[FX_MODE_TRAFFIC_LIGHT] = &WS2812FX::mode_traffic_light;
_mode[FX_MODE_COLOR_SWEEP_RANDOM] = &WS2812FX::mode_color_sweep_random;
_mode[FX_MODE_RUNNING_COLOR] = &WS2812FX::mode_running_color;
_mode[FX_MODE_RUNNING_RED_BLUE] = &WS2812FX::mode_running_red_blue;
_mode[FX_MODE_AURORA] = &WS2812FX::mode_aurora;
_mode[FX_MODE_RUNNING_RANDOM] = &WS2812FX::mode_running_random;
_mode[FX_MODE_LARSON_SCANNER] = &WS2812FX::mode_larson_scanner;
_mode[FX_MODE_COMET] = &WS2812FX::mode_comet;
_mode[FX_MODE_FIREWORKS] = &WS2812FX::mode_fireworks;
_mode[FX_MODE_RAIN] = &WS2812FX::mode_rain;
_mode[FX_MODE_MERRY_CHRISTMAS] = &WS2812FX::mode_merry_christmas;
_mode[FX_MODE_TETRIX] = &WS2812FX::mode_tetrix;
_mode[FX_MODE_FIRE_FLICKER] = &WS2812FX::mode_fire_flicker;
_mode[FX_MODE_GRADIENT] = &WS2812FX::mode_gradient;
_mode[FX_MODE_LOADING] = &WS2812FX::mode_loading;
@@ -397,7 +505,7 @@ class WS2812FX {
_mode[FX_MODE_POLICE_ALL] = &WS2812FX::mode_police_all;
_mode[FX_MODE_TWO_DOTS] = &WS2812FX::mode_two_dots;
_mode[FX_MODE_TWO_AREAS] = &WS2812FX::mode_two_areas;
_mode[FX_MODE_CIRCUS_COMBUSTUS] = &WS2812FX::mode_circus_combustus;
_mode[FX_MODE_RUNNING_DUAL] = &WS2812FX::mode_running_dual;
_mode[FX_MODE_HALLOWEEN] = &WS2812FX::mode_halloween;
_mode[FX_MODE_TRICOLOR_CHASE] = &WS2812FX::mode_tricolor_chase;
_mode[FX_MODE_TRICOLOR_WIPE] = &WS2812FX::mode_tricolor_wipe;
@@ -463,6 +571,8 @@ class WS2812FX {
_mode[FX_MODE_WASHING_MACHINE] = &WS2812FX::mode_washing_machine;
_mode[FX_MODE_CANDY_CANE] = &WS2812FX::mode_candy_cane;
_mode[FX_MODE_BLENDS] = &WS2812FX::mode_blends;
_mode[FX_MODE_TV_SIMULATOR] = &WS2812FX::mode_tv_simulator;
_mode[FX_MODE_DYNAMIC_SMOOTH] = &WS2812FX::mode_dynamic_smooth;
_brightness = DEFAULT_BRIGHTNESS;
currentPalette = CRGBPalette16(CRGB::Black);
@@ -470,12 +580,11 @@ class WS2812FX {
ablMilliampsMax = 850;
currentMilliamps = 0;
timebase = 0;
bus = new NeoPixelWrapper();
resetSegments();
}
void
init(bool supportWhite, uint16_t countPixels, bool skipFirst),
finalizeInit(uint16_t countPixels),
service(void),
blur(uint8_t),
fill(uint32_t),
@@ -486,6 +595,7 @@ class WS2812FX {
setBrightness(uint8_t b),
setRange(uint16_t i, uint16_t i2, uint32_t col),
setShowCallback(show_callback cb),
setTransition(uint16_t t),
setTransitionMode(bool t),
calcGammaTable(float),
trigger(void),
@@ -494,12 +604,12 @@ class WS2812FX {
setPixelColor(uint16_t n, uint32_t c),
setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
show(void),
setRgbwPwm(void),
setColorOrder(uint8_t co),
setPixelSegment(uint8_t n);
bool
reverseMode = false, //is the entire LED strip reversed?
isRgbw = false,
isOffRefreshRequred = false, //periodic refresh is required for the strip to remain off.
gammaCorrectBri = false,
gammaCorrectCol = true,
applyToAllSelected = true,
@@ -514,6 +624,8 @@ class WS2812FX {
paletteFade = 0,
paletteBlend = 0,
milliampsPerLed = 55,
// getStripType(uint8_t strip=0),
// setStripType(uint8_t type, uint8_t strip=0),
getBrightness(void),
getMode(void),
getSpeed(void),
@@ -525,22 +637,31 @@ class WS2812FX {
getColorOrder(void),
gamma8(uint8_t),
gamma8_cal(uint8_t, float),
sin_gap(uint16_t),
get_random_wheel_index(uint8_t);
int8_t
// setStripPin(uint8_t strip, int8_t pin),
// getStripPin(uint8_t strip=0),
// setStripPinClk(uint8_t strip, int8_t pin),
// getStripPinClk(uint8_t strip=0),
tristate_square8(uint8_t x, uint8_t pulsewidth, uint8_t attdec);
uint16_t
ablMilliampsMax,
currentMilliamps,
triwave16(uint16_t);
// setStripLen(uint8_t strip, uint16_t len),
// getStripLen(uint8_t strip=0),
triwave16(uint16_t),
getFps();
uint32_t
now,
timebase,
color_wheel(uint8_t),
color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255),
color_blend(uint32_t,uint32_t,uint8_t),
color_blend(uint32_t,uint32_t,uint16_t,bool b16=false),
currentColor(uint32_t colorNew, uint8_t tNr),
gamma32(uint32_t),
getLastShow(void),
getPixelColor(uint16_t),
@@ -595,13 +716,13 @@ class WS2812FX {
mode_colorful(void),
mode_traffic_light(void),
mode_running_color(void),
mode_running_red_blue(void),
mode_aurora(void),
mode_running_random(void),
mode_larson_scanner(void),
mode_comet(void),
mode_fireworks(void),
mode_rain(void),
mode_merry_christmas(void),
mode_tetrix(void),
mode_halloween(void),
mode_fire_flicker(void),
mode_gradient(void),
@@ -610,7 +731,7 @@ class WS2812FX {
mode_police_all(void),
mode_two_dots(void),
mode_two_areas(void),
mode_circus_combustus(void),
mode_running_dual(void),
mode_bicolor_chase(void),
mode_tricolor_chase(void),
mode_tricolor_wipe(void),
@@ -673,28 +794,28 @@ class WS2812FX {
mode_dancing_shadows(void),
mode_washing_machine(void),
mode_candy_cane(void),
mode_blends(void);
mode_blends(void),
mode_tv_simulator(void),
mode_dynamic_smooth(void);
private:
NeoPixelWrapper *bus;
uint32_t crgb_to_col(CRGB fastled);
CRGB col_to_crgb(uint32_t);
CRGBPalette16 currentPalette;
CRGBPalette16 targetPalette;
uint16_t _length, _lengthRaw, _virtualSegmentLength;
uint16_t _length, _virtualSegmentLength;
uint16_t _rand16seed;
uint8_t _brightness;
static uint16_t _usedSegmentData;
uint16_t _usedSegmentData = 0;
uint16_t _transitionDur = 750;
uint16_t _cumulativeFps = 2;
void load_gradient_palette(uint8_t);
void handle_palette(void);
bool
shouldStartBus = false,
_useRgbw = false,
_skipFirstMode,
_triggered;
mode_ptr _mode[MODE_COUNT]; // SRAM footprint: 4 bytes per element
@@ -706,9 +827,10 @@ class WS2812FX {
blink(uint32_t, uint32_t, bool strobe, bool),
candle(bool),
color_wipe(bool, bool),
dynamic(bool),
scan(bool),
theater_chase(uint32_t, uint32_t, bool),
running_base(bool),
running_base(bool,bool),
larson_scanner(bool),
sinelon_base(bool,bool),
dissolve(uint32_t),
@@ -725,27 +847,35 @@ class WS2812FX {
CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat);
CRGB pacifica_one_layer(uint16_t i, CRGBPalette16& p, uint16_t cistart, uint16_t wavescale, uint8_t bri, uint16_t ioff);
void blendPixelColor(uint16_t n, uint32_t color, uint8_t blend);
void
blendPixelColor(uint16_t n, uint32_t color, uint8_t blend),
startTransition(uint8_t oldBri, uint32_t oldCol, uint16_t dur, uint8_t segn, uint8_t slot),
deserializeMap(void);
uint16_t* customMappingTable = nullptr;
uint16_t customMappingSize = 0;
uint32_t _lastPaletteChange = 0;
uint32_t _lastShow = 0;
#ifdef WLED_USE_ANALOG_LEDS
uint32_t _analogLastShow = 0;
RgbwColor _analogLastColor = 0;
uint8_t _analogLastBri = 0;
#endif
uint32_t _colors_t[3];
uint8_t _bri_t;
uint8_t _segment_index = 0;
uint8_t _segment_index_palette_last = 99;
segment _segments[MAX_NUM_SEGMENTS] = { // SRAM footprint: 24 bytes per element
// start, stop, speed, intensity, palette, mode, options, grouping, spacing, opacity (unused), color[]
{ 0, 7, DEFAULT_SPEED, 128, 0, DEFAULT_MODE, NO_OPTIONS, 1, 0, 255, {DEFAULT_COLOR}}
// start, stop, offset, speed, intensity, palette, mode, options, grouping, spacing, opacity (unused), color[]
{0, 7, 0, DEFAULT_SPEED, 128, 0, DEFAULT_MODE, NO_OPTIONS, 1, 0, 255, {DEFAULT_COLOR}}
};
segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 28 bytes per element
friend class Segment_runtime;
uint16_t realPixelIndex(uint16_t i);
ColorTransition transitions[MAX_NUM_TRANSITIONS]; //12 bytes per element
friend class ColorTransition;
uint16_t
realPixelIndex(uint16_t i),
transitionProgress(uint8_t tNr);
};
//10 names per line
@@ -753,15 +883,15 @@ const char JSON_mode_names[] PROGMEM = R"=====([
"Solid","Blink","Breathe","Wipe","Wipe Random","Random Colors","Sweep","Dynamic","Colorloop","Rainbow",
"Scan","Scan Dual","Fade","Theater","Theater Rainbow","Running","Saw","Twinkle","Dissolve","Dissolve Rnd",
"Sparkle","Sparkle Dark","Sparkle+","Strobe","Strobe Rainbow","Strobe Mega","Blink Rainbow","Android","Chase","Chase Random",
"Chase Rainbow","Chase Flash","Chase Flash Rnd","Rainbow Runner","Colorful","Traffic Light","Sweep Random","Running 2","Red & Blue","Stream",
"Scanner","Lighthouse","Fireworks","Rain","Merry Christmas","Fire Flicker","Gradient","Loading","Police","Police All",
"Two Dots","Two Areas","Circus","Halloween","Tri Chase","Tri Wipe","Tri Fade","Lightning","ICU","Multi Comet",
"Chase Rainbow","Chase Flash","Chase Flash Rnd","Rainbow Runner","Colorful","Traffic Light","Sweep Random","Running 2","Aurora","Stream",
"Scanner","Lighthouse","Fireworks","Rain","Tetrix","Fire Flicker","Gradient","Loading","Police","Police All",
"Two Dots","Two Areas","Running Dual","Halloween","Tri Chase","Tri Wipe","Tri Fade","Lightning","ICU","Multi Comet",
"Scanner Dual","Stream 2","Oscillate","Pride 2015","Juggle","Palette","Fire 2012","Colorwaves","Bpm","Fill Noise",
"Noise 1","Noise 2","Noise 3","Noise 4","Colortwinkles","Lake","Meteor","Meteor Smooth","Railway","Ripple",
"Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Fireworks Starburst",
"Fireworks 1D","Bouncing Balls","Sinelon","Sinelon Dual","Sinelon Rainbow","Popcorn","Drip","Plasma","Percent","Ripple Rainbow",
"Heartbeat","Pacifica","Candle Multi", "Solid Glitter","Sunrise","Phased","Twinkleup","Noise Pal", "Sine","Phased Noise",
"Flow","Chunchun","Dancing Shadows","Washing Machine","Candy Cane","Blends"
"Flow","Chunchun","Dancing Shadows","Washing Machine","Candy Cane","Blends","TV Simulator","Dynamic Smooth"
])=====";
@@ -771,7 +901,7 @@ const char JSON_palette_names[] PROGMEM = R"=====([
"Pastel","Sunset 2","Beech","Vintage","Departure","Landscape","Beach","Sherbet","Hult","Hult 64",
"Drywet","Jul","Grintage","Rewhi","Tertiary","Fire","Icefire","Cyane","Light Pink","Autumn",
"Magenta","Magred","Yelmag","Yelblu","Orange & Teal","Tiamat","April Night","Orangery","C9","Sakura",
"Aurora","Atlantica","C9 2","C9 New","Temperature"
"Aurora","Atlantica","C9 2","C9 New","Temperature","Aurora 2"
])=====";
#endif

View File

@@ -27,48 +27,109 @@
#include "FX.h"
#include "palettes.h"
//enable custom per-LED mapping. This can allow for better effects on matrices or special displays
//#define WLED_CUSTOM_LED_MAPPING
/*
Custom per-LED mapping has moved!
#ifdef WLED_CUSTOM_LED_MAPPING
//this is just an example (30 LEDs). It will first set all even, then all uneven LEDs.
const uint16_t customMappingTable[] = {
Create a file "ledmap.json" using the edit page.
this is just an example (30 LEDs). It will first set all even, then all uneven LEDs.
{"map":[
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28,
1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29};
1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29]}
//another example. Switches direction every 5 LEDs.
/*const uint16_t customMappingTable[] = {
another example. Switches direction every 5 LEDs.
{"map":[
0, 1, 2, 3, 4, 9, 8, 7, 6, 5, 10, 11, 12, 13, 14,
19, 18, 17, 16, 15, 20, 21, 22, 23, 24, 29, 28, 27, 26, 25};*/
19, 18, 17, 16, 15, 20, 21, 22, 23, 24, 29, 28, 27, 26, 25]
*/
const uint16_t customMappingSize = sizeof(customMappingTable)/sizeof(uint16_t); //30 in example
//factory defaults LED setup
//#define PIXEL_COUNTS 30, 30, 30, 30
//#define DATA_PINS 16, 1, 3, 4
//#define DEFAULT_LED_TYPE TYPE_WS2812_RGB
#ifndef PIXEL_COUNTS
#define PIXEL_COUNTS DEFAULT_LED_COUNT
#endif
#ifndef PWM_INDEX
#define PWM_INDEX 0
#ifndef DATA_PINS
#define DATA_PINS LEDPIN
#endif
void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst)
#ifndef DEFAULT_LED_TYPE
#define DEFAULT_LED_TYPE TYPE_WS2812_RGB
#endif
#if MAX_NUM_SEGMENTS < WLED_MAX_BUSSES
#error "Max segments must be at least max number of busses!"
#endif
//do not call this method from system context (network callback)
void WS2812FX::finalizeInit(uint16_t countPixels)
{
if (supportWhite == _useRgbw && countPixels == _length && _skipFirstMode == skipFirst) return;
RESET_RUNTIME;
_useRgbw = supportWhite;
_length = countPixels;
_skipFirstMode = skipFirst;
uint8_t ty = 1;
if (supportWhite) ty = 2;
_lengthRaw = _length;
if (_skipFirstMode) {
_lengthRaw += LED_SKIP_AMOUNT;
//if busses failed to load, add default (FS issue...)
if (busses.getNumBusses() == 0) {
const uint8_t defDataPins[] = {DATA_PINS};
const uint16_t defCounts[] = {PIXEL_COUNTS};
const uint8_t defNumBusses = ((sizeof defDataPins) / (sizeof defDataPins[0]));
const uint8_t defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0]));
uint16_t prevLen = 0;
for (uint8_t i = 0; i < defNumBusses; i++) {
uint8_t defPin[] = {defDataPins[i]};
uint16_t start = prevLen;
uint16_t count = _length;
if (defNumBusses > 1 && defNumCounts) {
count = defCounts[(i < defNumCounts) ? i : defNumCounts -1];
}
prevLen += count;
BusConfig defCfg = BusConfig(DEFAULT_LED_TYPE, defPin, start, count, COL_ORDER_GRB);
busses.add(defCfg);
}
}
bus->Begin((NeoPixelType)ty, _lengthRaw);
_segments[0].start = 0;
_segments[0].stop = _length;
deserializeMap();
uint16_t segStarts[MAX_NUM_SEGMENTS] = {0};
uint16_t segStops [MAX_NUM_SEGMENTS] = {0};
setBrightness(_brightness);
//TODO make sure segments are only refreshed when bus config actually changed (new settings page)
//make one segment per bus
uint8_t s = 0;
for (uint8_t i = 0; i < busses.getNumBusses(); i++) {
Bus* b = busses.getBus(i);
segStarts[s] = b->getStart();
segStops[s] = segStarts[s] + b->getLength();
//check for overlap with previous segments
for (uint8_t j = 0; j < s; j++) {
if (segStops[j] > segStarts[s] && segStarts[j] < segStops[s]) {
//segments overlap, merge
segStarts[j] = min(segStarts[s],segStarts[j]);
segStops [j] = max(segStops [s],segStops [j]); segStops[s] = 0;
s--;
}
}
s++;
#ifdef ESP8266
if ((!IS_DIGITAL(b->getType()) || IS_2PIN(b->getType()))) continue;
uint8_t pins[5];
b->getPins(pins);
BusDigital* bd = static_cast<BusDigital*>(b);
if (pins[0] == 3) bd->reinit();
#endif
}
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) {
_segments[i].start = segStarts[i];
_segments[i].stop = segStops [i];
}
}
void WS2812FX::service() {
@@ -85,23 +146,31 @@ void WS2812FX::service() {
// segment's buffers are cleared
SEGENV.resetIfRequired();
if (SEGMENT.isActive())
if (!SEGMENT.isActive()) continue;
if(nowUp > SEGENV.next_time || _triggered || (doShow && SEGMENT.mode == 0)) //last is temporary
{
if(nowUp > SEGENV.next_time || _triggered || (doShow && SEGMENT.mode == 0)) //last is temporary
{
if (SEGMENT.grouping == 0) SEGMENT.grouping = 1; //sanity check
doShow = true;
uint16_t delay = FRAMETIME;
if (SEGMENT.grouping == 0) SEGMENT.grouping = 1; //sanity check
doShow = true;
uint16_t delay = FRAMETIME;
if (!SEGMENT.getOption(SEG_OPTION_FREEZE)) { //only run effect function if not frozen
_virtualSegmentLength = SEGMENT.virtualLength();
handle_palette();
delay = (this->*_mode[SEGMENT.mode])(); //effect function
if (SEGMENT.mode != FX_MODE_HALLOWEEN_EYES) SEGENV.call++;
if (!SEGMENT.getOption(SEG_OPTION_FREEZE)) { //only run effect function if not frozen
_virtualSegmentLength = SEGMENT.virtualLength();
_bri_t = SEGMENT.opacity; _colors_t[0] = SEGMENT.colors[0]; _colors_t[1] = SEGMENT.colors[1]; _colors_t[2] = SEGMENT.colors[2];
if (!IS_SEGMENT_ON) _bri_t = 0;
for (uint8_t t = 0; t < MAX_NUM_TRANSITIONS; t++) {
if ((transitions[t].segment & 0x3F) != i) continue;
uint8_t slot = transitions[t].segment >> 6;
if (slot == 0) _bri_t = transitions[t].currentBri();
_colors_t[slot] = transitions[t].currentColor(SEGMENT.colors[slot]);
}
SEGENV.next_time = nowUp + delay;
for (uint8_t c = 0; c < 3; c++) _colors_t[c] = gamma32(_colors_t[c]);
handle_palette();
delay = (this->*_mode[SEGMENT.mode])(); //effect function
if (SEGMENT.mode != FX_MODE_HALLOWEEN_EYES) SEGENV.call++;
}
SEGENV.next_time = nowUp + delay;
}
}
_virtualSegmentLength = 0;
@@ -120,8 +189,6 @@ void WS2812FX::setPixelColor(uint16_t n, uint32_t c) {
setPixelColor(n, r, g, b, w);
}
#define REV(i) (_length - 1 - (i))
//used to map from segment index to physical pixel, taking into account grouping, offsets, reverse and mirroring
uint16_t WS2812FX::realPixelIndex(uint16_t i) {
int16_t iGroup = i * SEGMENT.groupLength();
@@ -137,8 +204,6 @@ uint16_t WS2812FX::realPixelIndex(uint16_t i) {
}
realIndex += SEGMENT.start;
/* Reverse the whole string */
if (reverseMode) realIndex = REV(realIndex);
return realIndex;
}
@@ -146,7 +211,7 @@ uint16_t WS2812FX::realPixelIndex(uint16_t i) {
void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
{
//auto calculate white channel value if enabled
if (_useRgbw) {
if (isRgbw) {
if (rgbwMode == RGBW_MODE_AUTO_BRIGHTER || (w == 0 && (rgbwMode == RGBW_MODE_DUAL || rgbwMode == RGBW_MODE_LEGACY)))
{
//white value is set to lowest RGB channel
@@ -159,58 +224,46 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
}
}
RgbwColor col;
col.R = r; col.G = g; col.B = b; col.W = w;
uint16_t skip = _skipFirstMode ? LED_SKIP_AMOUNT : 0;
if (SEGLEN) {//from segment
//color_blend(getpixel, col, SEGMENT.opacity); (pseudocode for future blending of segments)
if (IS_SEGMENT_ON)
{
if (SEGMENT.opacity < 255) {
col.R = scale8(col.R, SEGMENT.opacity);
col.G = scale8(col.G, SEGMENT.opacity);
col.B = scale8(col.B, SEGMENT.opacity);
col.W = scale8(col.W, SEGMENT.opacity);
}
} else {
col = BLACK;
//color_blend(getpixel, col, _bri_t); (pseudocode for future blending of segments)
if (_bri_t < 255) {
r = scale8(r, _bri_t);
g = scale8(g, _bri_t);
b = scale8(b, _bri_t);
w = scale8(w, _bri_t);
}
uint32_t col = ((w << 24) | (r << 16) | (g << 8) | (b));
/* Set all the pixels in the group, ensuring _skipFirstMode is honored */
bool reversed = reverseMode ^ IS_REVERSE;
bool reversed = IS_REVERSE;
uint16_t realIndex = realPixelIndex(i);
uint16_t len = SEGMENT.length();
for (uint16_t j = 0; j < SEGMENT.grouping; j++) {
int16_t indexSet = realIndex + (reversed ? -j : j);
int16_t indexSetRev = indexSet;
if (reverseMode) indexSetRev = REV(indexSet);
#ifdef WLED_CUSTOM_LED_MAPPING
if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet];
#endif
if (indexSetRev >= SEGMENT.start && indexSetRev < SEGMENT.stop) {
bus->SetPixelColor(indexSet + skip, col);
int indexSet = realIndex + (reversed ? -j : j);
if (indexSet >= SEGMENT.start && indexSet < SEGMENT.stop) {
if (IS_MIRROR) { //set the corresponding mirrored pixel
if (reverseMode) {
bus->SetPixelColor(REV(SEGMENT.start) - indexSet + skip + REV(SEGMENT.stop) + 1, col);
} else {
bus->SetPixelColor(SEGMENT.stop - indexSet + skip + SEGMENT.start - 1, col);
}
uint16_t indexMir = SEGMENT.stop - indexSet + SEGMENT.start - 1;
/* offset/phase */
indexMir += SEGMENT.offset;
if (indexMir >= SEGMENT.stop) indexMir -= len;
if (indexMir < customMappingSize) indexMir = customMappingTable[indexMir];
busses.setPixelColor(indexMir, col);
}
/* offset/phase */
indexSet += SEGMENT.offset;
if (indexSet >= SEGMENT.stop) indexSet -= len;
if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet];
busses.setPixelColor(indexSet, col);
}
}
} else { //live data, etc.
if (reverseMode) i = REV(i);
#ifdef WLED_CUSTOM_LED_MAPPING
if (i < customMappingSize) i = customMappingTable[i];
#endif
bus->SetPixelColor(i + skip, col);
}
if (skip && i == 0) {
for (uint16_t j = 0; j < skip; j++) {
bus->SetPixelColor(j, RgbwColor(0, 0, 0, 0));
}
uint32_t col = ((w << 24) | (r << 16) | (g << 8) | (b));
busses.setPixelColor(i, col);
}
}
@@ -260,21 +313,22 @@ void WS2812FX::show(void) {
for (uint16_t i = 0; i < _length; i++) //sum up the usage of each LED
{
RgbwColor c = bus->GetPixelColorRaw(i);
uint32_t c = busses.getPixelColor(i);
byte r = c >> 16, g = c >> 8, b = c, w = c >> 24;
if(useWackyWS2815PowerModel)
{
// ignore white component on WS2815 power calculation
powerSum += (MAX(MAX(c.R,c.G),c.B)) * 3;
powerSum += (MAX(MAX(r,g),b)) * 3;
}
else
{
powerSum += (c.R + c.G + c.B + c.W);
powerSum += (r + g + b + w);
}
}
if (_useRgbw) //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
if (isRgbw) //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
{
powerSum *= 3;
powerSum = powerSum >> 2; //same as /= 4
@@ -289,25 +343,30 @@ void WS2812FX::show(void) {
uint16_t scaleI = scale * 255;
uint8_t scaleB = (scaleI > 255) ? 255 : scaleI;
uint8_t newBri = scale8(_brightness, scaleB);
bus->SetBrightness(newBri);
busses.setBrightness(newBri);
currentMilliamps = (powerSum0 * newBri) / puPerMilliamp;
} else
{
currentMilliamps = powerSum / puPerMilliamp;
bus->SetBrightness(_brightness);
busses.setBrightness(_brightness);
}
currentMilliamps += MA_FOR_ESP; //add power of ESP back to estimate
currentMilliamps += _length; //add standby power back to estimate
} else {
currentMilliamps = 0;
bus->SetBrightness(_brightness);
busses.setBrightness(_brightness);
}
// some buses send asynchronously and this method will return before
// all of the data has been sent.
// See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
bus->Show();
_lastShow = millis();
busses.show();
unsigned long now = millis();
unsigned long diff = now - _lastShow;
uint16_t fpsCurr = 200;
if (diff > 0) fpsCurr = 1000 / diff;
_cumulativeFps = (3 * _cumulativeFps + fpsCurr) >> 2;
_lastShow = now;
}
/**
@@ -315,7 +374,16 @@ void WS2812FX::show(void) {
* On some hardware (ESP32), strip updates are done asynchronously.
*/
bool WS2812FX::isUpdating() {
return !bus->CanShow();
return !busses.canAllShow();
}
/**
* Returns the refresh rate of the LED strip. Useful for finding out whether a given setup is fast enough.
* Only updates on show() or is set to 0 fps if last show is more than 2 secs ago, so accurary varies
*/
uint16_t WS2812FX::getFps() {
if (millis() - _lastShow > 2000) return 0;
return _cumulativeFps +1;
}
/**
@@ -347,11 +415,10 @@ uint8_t WS2812FX::getPaletteCount()
return 13 + GRADIENT_PALETTE_COUNT;
}
//TODO transitions
//TODO effect transitions
bool WS2812FX::setEffectConfig(uint8_t m, uint8_t s, uint8_t in, uint8_t p) {
uint8_t mainSeg = getMainSegmentId();
Segment& seg = _segments[getMainSegmentId()];
uint8_t modePrev = seg.mode, speedPrev = seg.speed, intensityPrev = seg.intensity, palettePrev = seg.palette;
@@ -394,12 +461,16 @@ void WS2812FX::setColor(uint8_t slot, uint32_t c) {
if (applyToAllSelected) {
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
{
if (_segments[i].isSelected()) _segments[i].colors[slot] = c;
if (_segments[i].isSelected()) {
_segments[i].setColor(slot, c, i);
applied = true;
}
}
}
if (!applyToAllSelected || !applied) {
_segments[getMainSegmentId()].colors[slot] = c;
uint8_t mainseg = getMainSegmentId();
_segments[mainseg].setColor(slot, c, mainseg);
}
}
@@ -413,17 +484,6 @@ void WS2812FX::setBrightness(uint8_t b) {
{
_segments[i].setOption(SEG_OPTION_FREEZE, false);
}
#if LEDPIN == LED_BUILTIN
shouldStartBus = true;
#endif
} else {
#if LEDPIN == LED_BUILTIN
if (shouldStartBus) {
shouldStartBus = false;
const uint8_t ty = _useRgbw ? 2 : 1;
bus->Begin((NeoPixelType)ty, _lengthRaw);
}
#endif
}
if (SEGENV.next_time > millis() + 22 && millis() - _lastShow > MIN_SHOW_DELAY) show();//apply brightness change immediately if no refresh soon
}
@@ -474,16 +534,16 @@ uint32_t WS2812FX::getColor(void) {
uint32_t WS2812FX::getPixelColor(uint16_t i)
{
i = realPixelIndex(i);
#ifdef WLED_CUSTOM_LED_MAPPING
if (i < customMappingSize) i = customMappingTable[i];
#endif
if (_skipFirstMode) i += LED_SKIP_AMOUNT;
if (SEGLEN) {
/* offset/phase */
i += SEGMENT.offset;
if (i >= SEGMENT.stop) i -= SEGMENT.length();
}
if (i >= _lengthRaw) return 0;
if (i < customMappingSize) i = customMappingTable[i];
return bus->GetPixelColorRgbw(i);
return busses.getPixelColor(i);
}
WS2812FX::Segment& WS2812FX::getSegment(uint8_t id) {
@@ -503,12 +563,13 @@ uint32_t WS2812FX::getLastShow(void) {
return _lastShow;
}
//TODO these need to be on a per-strip basis
uint8_t WS2812FX::getColorOrder(void) {
return bus->GetColorOrder();
return COL_ORDER_GRB;
}
void WS2812FX::setColorOrder(uint8_t co) {
bus->SetColorOrder(co);
//bus->SetColorOrder(co);
}
void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing) {
@@ -602,6 +663,11 @@ void WS2812FX::setShowCallback(show_callback cb)
_callback = cb;
}
void WS2812FX::setTransition(uint16_t t)
{
_transitionDur = t;
}
void WS2812FX::setTransitionMode(bool t)
{
unsigned long waitMax = millis() + 20; //refresh after 20 ms if transition enabled
@@ -617,24 +683,26 @@ void WS2812FX::setTransitionMode(bool t)
/*
* color blend function
*/
uint32_t WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint8_t blend) {
uint32_t WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) {
if(blend == 0) return color1;
if(blend == 255) return color2;
uint16_t blendmax = b16 ? 0xFFFF : 0xFF;
if(blend == blendmax) return color2;
uint8_t shift = b16 ? 16 : 8;
uint32_t w1 = (color1 >> 24) & 0xff;
uint32_t r1 = (color1 >> 16) & 0xff;
uint32_t g1 = (color1 >> 8) & 0xff;
uint32_t b1 = color1 & 0xff;
uint32_t w1 = (color1 >> 24) & 0xFF;
uint32_t r1 = (color1 >> 16) & 0xFF;
uint32_t g1 = (color1 >> 8) & 0xFF;
uint32_t b1 = color1 & 0xFF;
uint32_t w2 = (color2 >> 24) & 0xff;
uint32_t r2 = (color2 >> 16) & 0xff;
uint32_t g2 = (color2 >> 8) & 0xff;
uint32_t b2 = color2 & 0xff;
uint32_t w2 = (color2 >> 24) & 0xFF;
uint32_t r2 = (color2 >> 16) & 0xFF;
uint32_t g2 = (color2 >> 8) & 0xFF;
uint32_t b2 = color2 & 0xFF;
uint32_t w3 = ((w2 * blend) + (w1 * (255 - blend))) >> 8;
uint32_t r3 = ((r2 * blend) + (r1 * (255 - blend))) >> 8;
uint32_t g3 = ((g2 * blend) + (g1 * (255 - blend))) >> 8;
uint32_t b3 = ((b2 * blend) + (b1 * (255 - blend))) >> 8;
uint32_t w3 = ((w2 * blend) + (w1 * (blendmax - blend))) >> shift;
uint32_t r3 = ((r2 * blend) + (r1 * (blendmax - blend))) >> shift;
uint32_t g3 = ((g2 * blend) + (g1 * (blendmax - blend))) >> shift;
uint32_t b3 = ((b2 * blend) + (b1 * (blendmax - blend))) >> shift;
return ((w3 << 24) | (r3 << 16) | (g3 << 8) | (b3));
}
@@ -724,6 +792,12 @@ uint16_t WS2812FX::triwave16(uint16_t in)
return 0xFFFF - (in - 0x8000)*2;
}
uint8_t WS2812FX::sin_gap(uint16_t in) {
if (in & 0x100) return 0;
//if (in > 255) return 0;
return sin8(in + 192); //correct phase shift of sine so that it starts and stops at 0
}
/*
* Generates a tristate square wave w/ attac & decay
* @param x input value 0-255
@@ -928,7 +1002,7 @@ uint32_t WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8
}
uint8_t paletteIndex = i;
if (mapping) paletteIndex = (i*255)/(SEGLEN -1);
if (mapping && SEGLEN > 1) paletteIndex = (i*255)/(SEGLEN -1);
if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
CRGB fastled_col;
fastled_col = ColorFromPalette( currentPalette, paletteIndex, pbri, (paletteBlend == 3)? NOBLEND:LINEARBLEND);
@@ -953,44 +1027,31 @@ bool WS2812FX::segmentsAreIdentical(Segment* a, Segment* b)
return true;
}
#ifdef WLED_USE_ANALOG_LEDS
void WS2812FX::setRgbwPwm(void) {
uint32_t nowUp = millis(); // Be aware, millis() rolls over every 49 days
if (nowUp - _analogLastShow < MIN_SHOW_DELAY) return;
_analogLastShow = nowUp;
//load custom mapping table from JSON file
void WS2812FX::deserializeMap(void) {
if (!WLED_FS.exists("/ledmap.json")) return;
DynamicJsonDocument doc(JSON_BUFFER_SIZE); // full sized buffer for larger maps
RgbwColor c;
uint32_t col = bus->GetPixelColorRgbw(PWM_INDEX);
c.R = col >> 16; c.G = col >> 8; c.B = col; c.W = col >> 24;
DEBUG_PRINTLN(F("Reading LED map from /ledmap.json..."));
byte b = getBrightness();
if (c == _analogLastColor && b == _analogLastBri) return;
// check color values for Warm / Cold white mix (for RGBW) // EsplanexaDevice.cpp
#ifdef WLED_USE_5CH_LEDS
if (c.R == 255 && c.G == 255 && c.B == 255 && c.W == 255) {
bus->SetRgbwPwm(0, 0, 0, 0, c.W * b / 255);
} else if (c.R == 127 && c.G == 127 && c.B == 127 && c.W == 255) {
bus->SetRgbwPwm(0, 0, 0, c.W * b / 512, c.W * b / 255);
} else if (c.R == 0 && c.G == 0 && c.B == 0 && c.W == 255) {
bus->SetRgbwPwm(0, 0, 0, c.W * b / 255, 0);
} else if (c.R == 130 && c.G == 90 && c.B == 0 && c.W == 255) {
bus->SetRgbwPwm(0, 0, 0, c.W * b / 255, c.W * b / 512);
} else if (c.R == 255 && c.G == 153 && c.B == 0 && c.W == 255) {
bus->SetRgbwPwm(0, 0, 0, c.W * b / 255, 0);
} else { // not only white colors
bus->SetRgbwPwm(c.R * b / 255, c.G * b / 255, c.B * b / 255, c.W * b / 255);
if (!readObjectFromFile("/ledmap.json", nullptr, &doc)) return; //if file does not exist just exit
if (customMappingTable != nullptr) {
delete[] customMappingTable;
customMappingTable = nullptr;
customMappingSize = 0;
}
JsonArray map = doc[F("map")];
if (!map.isNull() && map.size()) { // not an empty map
customMappingSize = map.size();
customMappingTable = new uint16_t[customMappingSize];
for (uint16_t i=0; i<customMappingSize; i++) {
customMappingTable[i] = (uint16_t) map[i];
}
#else
bus->SetRgbwPwm(c.R * b / 255, c.G * b / 255, c.B * b / 255, c.W * b / 255);
#endif
_analogLastColor = c;
_analogLastBri = b;
}
}
#else
void WS2812FX::setRgbwPwm() {}
#endif
//gamma 2.8 lookup table used for color correction
byte gammaT[] = {
@@ -1041,4 +1102,4 @@ uint32_t WS2812FX::gamma32(uint32_t color)
return ((w << 24) | (r << 16) | (g << 8) | (b));
}
uint16_t WS2812FX::_usedSegmentData = 0;
WS2812FX* WS2812FX::instance = nullptr;

34
wled00/NodeStruct.h Normal file
View File

@@ -0,0 +1,34 @@
#ifndef WLED_NODESTRUCT_H
#define WLED_NODESTRUCT_H
/*********************************************************************************************\
* NodeStruct from the ESP Easy project (https://github.com/letscontrolit/ESPEasy)
\*********************************************************************************************/
#include <map>
#include <IPAddress.h>
#define NODE_TYPE_ID_UNDEFINED 0
#define NODE_TYPE_ID_ESP8266 82
#define NODE_TYPE_ID_ESP32 32
/*********************************************************************************************\
* NodeStruct
\*********************************************************************************************/
struct NodeStruct
{
String nodeName;
IPAddress ip;
uint8_t unit;
uint8_t age;
uint8_t nodeType;
uint32_t build;
NodeStruct() : age(0), nodeType(0), build(0)
{
for (uint8_t i = 0; i < 4; ++i) { ip[i] = 0; }
}
};
typedef std::map<uint8_t, NodeStruct> NodesMap;
#endif // WLED_NODESTRUCT_H

View File

@@ -1,439 +0,0 @@
//this code is a modified version of https://github.com/Makuna/NeoPixelBus/issues/103
#ifndef NpbWrapper_h
#define NpbWrapper_h
//PIN CONFIGURATION
#ifndef LEDPIN
#define LEDPIN 2 //strip pin. Any for ESP32, gpio2 or 3 is recommended for ESP8266 (gpio2/3 are labeled D4/RX on NodeMCU and Wemos)
#endif
//#define USE_APA102 // Uncomment for using APA102 LEDs.
//#define USE_WS2801 // Uncomment for using WS2801 LEDs (make sure you have NeoPixelBus v2.5.6 or newer)
//#define USE_LPD8806 // Uncomment for using LPD8806
//#define USE_TM1814 // Uncomment for using TM1814 LEDs (make sure you have NeoPixelBus v2.5.7 or newer)
//#define USE_P9813 // Uncomment for using P9813 LEDs (make sure you have NeoPixelBus v2.5.8 or newer)
//#define WLED_USE_ANALOG_LEDS //Uncomment for using "dumb" PWM controlled LEDs (see pins below, default R: gpio5, G: 12, B: 15, W: 13)
//#define WLED_USE_H801 //H801 controller. Please uncomment #define WLED_USE_ANALOG_LEDS as well
//#define WLED_USE_5CH_LEDS //5 Channel H801 for cold and warm white
//#define WLED_USE_BWLT11
//#define WLED_USE_SHOJO_PCB
#ifndef BTNPIN
#define BTNPIN 0 //button pin. Needs to have pullup (gpio0 recommended)
#endif
#ifndef TOUCHPIN
//#define TOUCHPIN T0 //touch pin. Behaves the same as button. ESP32 only.
#endif
#ifndef IRPIN
#define IRPIN 4 //infrared pin (-1 to disable) MagicHome: 4, H801 Wifi: 0
#endif
#ifndef RLYPIN
#define RLYPIN 12 //pin for relay, will be set HIGH if LEDs are on (-1 to disable). Also usable for standby leds, triggers,...
#endif
#ifndef AUXPIN
#define AUXPIN -1 //debug auxiliary output pin (-1 to disable)
#endif
#ifndef RLYMDE
#define RLYMDE 1 //mode for relay, 0: LOW if LEDs are on 1: HIGH if LEDs are on
#endif
//enable color order override for a specific range of the strip
//This can be useful if you want to chain multiple strings with incompatible color order
//#define COLOR_ORDER_OVERRIDE
#define COO_MIN 0
#define COO_MAX 35 //not inclusive, this would set the override for LEDs 0-26
#define COO_ORDER COL_ORDER_GRB
//END CONFIGURATION
#if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) || defined(USE_P9813)
#ifndef CLKPIN
#define CLKPIN 0
#endif
#ifndef DATAPIN
#define DATAPIN 2
#endif
#if BTNPIN == CLKPIN || BTNPIN == DATAPIN
#undef BTNPIN // Deactivate button pin if it conflicts with one of the APA102 pins.
#endif
#endif
#ifdef WLED_USE_ANALOG_LEDS
//PWM pins - PINs 15,13,12,14 (W2 = 04)are used with H801 Wifi LED Controller
#ifdef WLED_USE_H801
#define RPIN 15 //R pin for analog LED strip
#define GPIN 13 //G pin for analog LED strip
#define BPIN 12 //B pin for analog LED strip
#define WPIN 14 //W pin for analog LED strip
#define W2PIN 04 //W2 pin for analog LED strip
#undef BTNPIN
#undef IRPIN
#define IRPIN 0 //infrared pin (-1 to disable) MagicHome: 4, H801 Wifi: 0
#elif defined(WLED_USE_BWLT11)
//PWM pins - to use with BW-LT11
#define RPIN 12 //R pin for analog LED strip
#define GPIN 4 //G pin for analog LED strip
#define BPIN 14 //B pin for analog LED strip
#define WPIN 5 //W pin for analog LED strip
#elif defined(WLED_USE_SHOJO_PCB)
//PWM pins - to use with Shojo PCB (https://www.bastelbunker.de/esp-rgbww-wifi-led-controller-vbs-edition/)
#define RPIN 14 //R pin for analog LED strip
#define GPIN 4 //G pin for analog LED strip
#define BPIN 5 //B pin for analog LED strip
#define WPIN 15 //W pin for analog LED strip
#define W2PIN 12 //W2 pin for analog LED strip
#elif defined(WLED_USE_PLJAKOBS_PCB)
// PWM pins - to use with esp_rgbww_controller from patrickjahns/pljakobs (https://github.com/pljakobs/esp_rgbww_controller)
#define RPIN 12 //R pin for analog LED strip
#define GPIN 13 //G pin for analog LED strip
#define BPIN 14 //B pin for analog LED strip
#define WPIN 4 //W pin for analog LED strip
#define W2PIN 5 //W2 pin for analog LED strip
#undef IRPIN
#else
//Enable override of Pins by using the platformio_override.ini file
//PWM pins - PINs 5,12,13,15 are used with Magic Home LED Controller
#ifndef RPIN
#define RPIN 5 //R pin for analog LED strip
#endif
#ifndef GPIN
#define GPIN 12 //G pin for analog LED strip
#endif
#ifndef BPIN
#define BPIN 15 //B pin for analog LED strip
#endif
#ifndef WPIN
#define WPIN 13 //W pin for analog LED strip
#endif
#endif
#undef RLYPIN
#define RLYPIN -1 //disable as pin 12 is used by analog LEDs
#endif
//automatically uses the right driver method for each platform
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_APA102
#define PIXELMETHOD DotStarMethod
#elif defined(USE_WS2801)
#define PIXELMETHOD NeoWs2801Method
#elif defined(USE_LPD8806)
#define PIXELMETHOD Lpd8806Method
#elif defined(USE_TM1814)
#define PIXELMETHOD NeoTm1814Method
#elif defined(USE_P9813)
#define PIXELMETHOD P9813Method
#else
#define PIXELMETHOD NeoEsp32Rmt0Ws2812xMethod
#endif
#else //esp8266
//autoselect the right method depending on strip pin
#ifdef USE_APA102
#define PIXELMETHOD DotStarMethod
#elif defined(USE_WS2801)
#define PIXELMETHOD NeoWs2801Method
#elif defined(USE_LPD8806)
#define PIXELMETHOD Lpd8806Method
#elif defined(USE_TM1814)
#define PIXELMETHOD NeoTm1814Method
#elif defined(USE_P9813)
#define PIXELMETHOD P9813Method
#elif LEDPIN == 2
#define PIXELMETHOD NeoEsp8266Uart1Ws2813Method //if you get an error here, try to change to NeoEsp8266UartWs2813Method or update Neopixelbus
#elif LEDPIN == 3
#define PIXELMETHOD NeoEsp8266Dma800KbpsMethod
#else
#define PIXELMETHOD NeoEsp8266BitBang800KbpsMethod
#pragma message "Software BitBang will be used because of your selected LED pin. This may cause flicker. Use GPIO 2 or 3 for best results."
#endif
#endif
//you can now change the color order in the web settings
#ifdef USE_APA102
#define PIXELFEATURE3 DotStarBgrFeature
#define PIXELFEATURE4 DotStarLbgrFeature
#elif defined(USE_LPD8806)
#define PIXELFEATURE3 Lpd8806GrbFeature
#define PIXELFEATURE4 Lpd8806GrbFeature
#elif defined(USE_WS2801)
#define PIXELFEATURE3 NeoRbgFeature
#define PIXELFEATURE4 NeoRbgFeature
#elif defined(USE_TM1814)
#define PIXELFEATURE3 NeoWrgbTm1814Feature
#define PIXELFEATURE4 NeoWrgbTm1814Feature
#elif defined(USE_P9813)
#define PIXELFEATURE3 P9813BgrFeature
#define PIXELFEATURE4 NeoGrbwFeature
#else
#define PIXELFEATURE3 NeoGrbFeature
#define PIXELFEATURE4 NeoGrbwFeature
#endif
#include <NeoPixelBrightnessBus.h>
#include "const.h"
enum NeoPixelType
{
NeoPixelType_None = 0,
NeoPixelType_Grb = 1,
NeoPixelType_Grbw = 2,
NeoPixelType_End = 3
};
class NeoPixelWrapper
{
public:
NeoPixelWrapper() :
// initialize each member to null
_pGrb(NULL),
_pGrbw(NULL),
_type(NeoPixelType_None)
{
}
~NeoPixelWrapper()
{
cleanup();
}
void Begin(NeoPixelType type, uint16_t countPixels)
{
cleanup();
_type = type;
switch (_type)
{
case NeoPixelType_Grb:
#if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) || defined(USE_P9813)
_pGrb = new NeoPixelBrightnessBus<PIXELFEATURE3,PIXELMETHOD>(countPixels, CLKPIN, DATAPIN);
#else
_pGrb = new NeoPixelBrightnessBus<PIXELFEATURE3,PIXELMETHOD>(countPixels, LEDPIN);
#endif
_pGrb->Begin();
break;
case NeoPixelType_Grbw:
#if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) || defined(USE_P9813)
_pGrbw = new NeoPixelBrightnessBus<PIXELFEATURE4,PIXELMETHOD>(countPixels, CLKPIN, DATAPIN);
#else
_pGrbw = new NeoPixelBrightnessBus<PIXELFEATURE4,PIXELMETHOD>(countPixels, LEDPIN);
#endif
_pGrbw->Begin();
break;
}
#ifdef WLED_USE_ANALOG_LEDS
#ifdef ARDUINO_ARCH_ESP32
ledcSetup(0, 5000, 8);
ledcAttachPin(RPIN, 0);
ledcSetup(1, 5000, 8);
ledcAttachPin(GPIN, 1);
ledcSetup(2, 5000, 8);
ledcAttachPin(BPIN, 2);
if(_type == NeoPixelType_Grbw)
{
ledcSetup(3, 5000, 8);
ledcAttachPin(WPIN, 3);
#ifdef WLED_USE_5CH_LEDS
ledcSetup(4, 5000, 8);
ledcAttachPin(W2PIN, 4);
#endif
}
#else // ESP8266
//init PWM pins
pinMode(RPIN, OUTPUT);
pinMode(GPIN, OUTPUT);
pinMode(BPIN, OUTPUT);
if(_type == NeoPixelType_Grbw)
{
pinMode(WPIN, OUTPUT);
#ifdef WLED_USE_5CH_LEDS
pinMode(W2PIN, OUTPUT);
#endif
}
analogWriteRange(255); //same range as one RGB channel
analogWriteFreq(880); //PWM frequency proven as good for LEDs
#endif
#endif
}
#ifdef WLED_USE_ANALOG_LEDS
void SetRgbwPwm(uint8_t r, uint8_t g, uint8_t b, uint8_t w, uint8_t w2=0)
{
#ifdef ARDUINO_ARCH_ESP32
ledcWrite(0, r);
ledcWrite(1, g);
ledcWrite(2, b);
switch (_type) {
case NeoPixelType_Grb: break;
#ifdef WLED_USE_5CH_LEDS
case NeoPixelType_Grbw: ledcWrite(3, w); ledcWrite(4, w2); break;
#else
case NeoPixelType_Grbw: ledcWrite(3, w); break;
#endif
}
#else // ESP8266
analogWrite(RPIN, r);
analogWrite(GPIN, g);
analogWrite(BPIN, b);
switch (_type) {
case NeoPixelType_Grb: break;
#ifdef WLED_USE_5CH_LEDS
case NeoPixelType_Grbw: analogWrite(WPIN, w); analogWrite(W2PIN, w2); break;
#else
case NeoPixelType_Grbw: analogWrite(WPIN, w); break;
#endif
}
#endif
}
#endif
void Show()
{
switch (_type)
{
case NeoPixelType_Grb: _pGrb->Show(); break;
case NeoPixelType_Grbw: _pGrbw->Show(); break;
}
}
/**
* This will return true if enough time has passed since the last time Show() was called.
* This also means that calling Show() will not cause any undue waiting. If the method for
* the defined bus is hardware that sends asynchronously, then call CanShow() will let
* you know if it has finished sending the data from the last Show().
*/
bool CanShow()
{
switch (_type)
{
case NeoPixelType_Grb: return _pGrb->CanShow();
case NeoPixelType_Grbw: return _pGrbw->CanShow();
default: return true;
}
}
void SetPixelColor(uint16_t indexPixel, RgbwColor c)
{
RgbwColor col;
uint8_t co = _colorOrder;
#ifdef COLOR_ORDER_OVERRIDE
if (indexPixel >= COO_MIN && indexPixel < COO_MAX) co = COO_ORDER;
#endif
//reorder channels to selected order
switch (co)
{
case 0: col.G = c.G; col.R = c.R; col.B = c.B; break; //0 = GRB, default
case 1: col.G = c.R; col.R = c.G; col.B = c.B; break; //1 = RGB, common for WS2811
case 2: col.G = c.B; col.R = c.R; col.B = c.G; break; //2 = BRG
case 3: col.G = c.R; col.R = c.B; col.B = c.G; break; //3 = RBG
case 4: col.G = c.B; col.R = c.G; col.B = c.R; break; //4 = BGR
default: col.G = c.G; col.R = c.B; col.B = c.R; break; //5 = GBR
}
col.W = c.W;
switch (_type) {
case NeoPixelType_Grb: {
_pGrb->SetPixelColor(indexPixel, RgbColor(col.R,col.G,col.B));
}
break;
case NeoPixelType_Grbw: {
#if defined(USE_LPD8806) || defined(USE_WS2801)
_pGrbw->SetPixelColor(indexPixel, RgbColor(col.R,col.G,col.B));
#else
_pGrbw->SetPixelColor(indexPixel, col);
#endif
}
break;
}
}
void SetBrightness(byte b)
{
switch (_type) {
case NeoPixelType_Grb: _pGrb->SetBrightness(b); break;
case NeoPixelType_Grbw:_pGrbw->SetBrightness(b); break;
}
}
void SetColorOrder(byte colorOrder) {
_colorOrder = colorOrder;
}
uint8_t GetColorOrder() {
return _colorOrder;
}
RgbwColor GetPixelColorRaw(uint16_t indexPixel) const
{
switch (_type) {
case NeoPixelType_Grb: return _pGrb->GetPixelColor(indexPixel); break;
case NeoPixelType_Grbw: return _pGrbw->GetPixelColor(indexPixel); break;
}
return 0;
}
// NOTE: Due to feature differences, some support RGBW but the method name
// here needs to be unique, thus GetPixeColorRgbw
uint32_t GetPixelColorRgbw(uint16_t indexPixel) const
{
RgbwColor col(0,0,0,0);
switch (_type) {
case NeoPixelType_Grb: col = _pGrb->GetPixelColor(indexPixel); break;
case NeoPixelType_Grbw: col = _pGrbw->GetPixelColor(indexPixel); break;
}
uint8_t co = _colorOrder;
#ifdef COLOR_ORDER_OVERRIDE
if (indexPixel >= COO_MIN && indexPixel < COO_MAX) co = COO_ORDER;
#endif
switch (co)
{
// W G R B
case 0: return ((col.W << 24) | (col.G << 8) | (col.R << 16) | (col.B)); //0 = GRB, default
case 1: return ((col.W << 24) | (col.R << 8) | (col.G << 16) | (col.B)); //1 = RGB, common for WS2811
case 2: return ((col.W << 24) | (col.B << 8) | (col.R << 16) | (col.G)); //2 = BRG
case 3: return ((col.W << 24) | (col.B << 8) | (col.G << 16) | (col.R)); //3 = RBG
case 4: return ((col.W << 24) | (col.R << 8) | (col.B << 16) | (col.G)); //4 = BGR
case 5: return ((col.W << 24) | (col.G << 8) | (col.B << 16) | (col.R)); //5 = GBR
}
return 0;
}
uint8_t* GetPixels(void)
{
switch (_type) {
case NeoPixelType_Grb: return _pGrb->Pixels(); break;
case NeoPixelType_Grbw: return _pGrbw->Pixels(); break;
}
return 0;
}
private:
NeoPixelType _type;
// have a member for every possible type
NeoPixelBrightnessBus<PIXELFEATURE3,PIXELMETHOD>* _pGrb;
NeoPixelBrightnessBus<PIXELFEATURE4,PIXELMETHOD>* _pGrbw;
byte _colorOrder = 0;
void cleanup()
{
switch (_type) {
case NeoPixelType_Grb: delete _pGrb ; _pGrb = NULL; break;
case NeoPixelType_Grbw: delete _pGrbw; _pGrbw = NULL; break;
}
}
};
#endif

View File

@@ -1,124 +0,0 @@
/*
Editor: https://www.visualmicro.com/
This file is for intellisense purpose only.
Visual micro (and the arduino ide) ignore this code during compilation. This code is automatically maintained by visualmicro, manual changes to this file will be overwritten
The contents of the _vm sub folder can be deleted prior to publishing a project
All non-arduino files created by visual micro and all visual studio project or solution files can be freely deleted and are not required to compile a sketch (do not delete your own code!).
Note: debugger breakpoints are stored in '.sln' or '.asln' files, knowledge of last uploaded breakpoints is stored in the upload.vmps.xml file. Both files are required to continue a previous debug session without needing to compile and upload again
Hardware: ESP32 Dev Module, Platform=esp32, Package=esp32
*/
#if defined(_VMICRO_INTELLISENSE)
#ifndef _VSARDUINO_H_
#define _VSARDUINO_H_
#define __ESP32_esp32__
#define __ESP32_ESP32__
#define ESP_PLATFORM
#define HAVE_CONFIG_H
#define GCC_NOT_5_2_0 0
#define WITH_POSIX
#define F_CPU 240000000L
#define ARDUINO 108011
#define ARDUINO_ESP32_DEV
#define ARDUINO_ARCH_ESP32
#define ESP32
#define CORE_DEBUG_LEVEL 0
#define __cplusplus 201103L
#define _Pragma(x)
#undef __cplusplus
#define __cplusplus 201103L
#define __STDC__
#define __ARM__
#define __arm__
#define __inline__
#define __asm__(...)
#define __extension__
#define __ATTR_PURE__
#define __ATTR_CONST__
#define __volatile__
#define __ASM
#define __INLINE
#define __attribute__(noinline)
//#define _STD_BEGIN
//#define EMIT
#define WARNING
#define _Lockit
#define __CLR_OR_THIS_CALL
#define C4005
#define _NEW
typedef bool _Bool;
typedef int _read;
typedef int _seek;
typedef int _write;
typedef int _close;
typedef int __cleanup;
//#define inline
#define __builtin_clz
#define __builtin_clzl
#define __builtin_clzll
#define __builtin_labs
#define __builtin_va_list
typedef int __gnuc_va_list;
#define __ATOMIC_ACQ_REL
#define __CHAR_BIT__
#define _EXFUN()
typedef unsigned char byte;
extern "C" void __cxa_pure_virtual() {;}
typedef long __INTPTR_TYPE__ ;
typedef long __UINTPTR_TYPE__ ;
typedef long __SIZE_TYPE__ ;
typedef long __PTRDIFF_TYPE__;
typedef long pthread_t;
typedef long pthread_key_t;
typedef long pthread_once_t;
typedef long pthread_mutex_t;
typedef long pthread_mutex_t;
typedef long pthread_cond_t;
#include "arduino.h"
#include <pins_arduino.h>
#define interrupts() sei()
#define noInterrupts() cli()
#define ESP_LOGI(tag, ...)
#include "wled00.ino"
#include "wled01_eeprom.ino"
#include "wled02_xml.ino"
#include "wled03_set.ino"
#include "wled04_file.ino"
#include "wled05_init.ino"
#include "wled06_usermod.ino"
#include "wled07_notify.ino"
#include "wled08_led.ino"
#include "wled09_button.ino"
#include "wled10_ntp.ino"
#include "wled11_ol.ino"
#include "wled12_alexa.ino"
#include "wled13_cronixie.ino"
#include "wled14_colors.ino"
#include "wled15_hue.ino"
#include "wled16_blynk.ino"
#include "wled17_mqtt.ino"
#include "wled18_server.ino"
#include "wled19_json.ino"
#include "wled20_ir.ino"
#endif
#endif

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -46,7 +46,10 @@ void onAlexaChange(EspalexaDevice* dev)
bri = briLast;
colorUpdated(NOTIFIER_CALL_MODE_ALEXA);
}
} else applyPreset(macroAlexaOn);
} else {
applyPreset(macroAlexaOn);
if (bri == 0) espalexaDevice->setValue(briLast); //stop Alexa from complaining if macroAlexaOn does not actually turn on
}
} else if (m == EspalexaDeviceProperty::off)
{
if (!macroAlexaOff)
@@ -57,7 +60,10 @@ void onAlexaChange(EspalexaDevice* dev)
bri = 0;
colorUpdated(NOTIFIER_CALL_MODE_ALEXA);
}
} else applyPreset(macroAlexaOff);
} else {
applyPreset(macroAlexaOff);
if (bri != 0) espalexaDevice->setValue(0); //stop Alexa from complaining if macroAlexaOff does not actually turn off
}
} else if (m == EspalexaDeviceProperty::bri)
{
bri = espalexaDevice->getValue();
@@ -67,7 +73,7 @@ void onAlexaChange(EspalexaDevice* dev)
if (espalexaDevice->getColorMode() == EspalexaColorMode::ct) //shade of white
{
uint16_t ct = espalexaDevice->getCt();
if (useRGBW)
if (strip.isRgbw)
{
switch (ct) { //these values empirically look good on RGBW
case 199: col[0]=255; col[1]=255; col[2]=255; col[3]=255; break;

View File

@@ -8,12 +8,12 @@
uint16_t blHue = 0;
byte blSat = 255;
void initBlynk(const char* auth)
void initBlynk(const char *auth, const char *host, uint16_t port)
{
#ifndef WLED_DISABLE_BLYNK
if (!WLED_CONNECTED) return;
blynkEnabled = (auth[0] != 0);
if (blynkEnabled) Blynk.config(auth);
if (blynkEnabled) Blynk.config(auth, host, port);
#endif
}

447
wled00/bus_manager.h Normal file
View File

@@ -0,0 +1,447 @@
#ifndef BusManager_h
#define BusManager_h
/*
* Class for addressing various light types
*/
#include "const.h"
#include "pin_manager.h"
#include "bus_wrapper.h"
#include <Arduino.h>
//temporary struct for passing bus configuration to bus
struct BusConfig {
uint8_t type = TYPE_WS2812_RGB;
uint16_t count = 1;
uint16_t start = 0;
uint8_t colorOrder = COL_ORDER_GRB;
bool reversed = false;
uint8_t skipAmount;
uint8_t pins[5] = {LEDPIN, 255, 255, 255, 255};
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip=0) {
type = busType; count = len; start = pstart;
colorOrder = pcolorOrder; reversed = rev; skipAmount = skip;
uint8_t nPins = 1;
if (type > 47) nPins = 2;
else if (type > 40 && type < 46) nPins = NUM_PWM_PINS(type);
for (uint8_t i = 0; i < nPins; i++) pins[i] = ppins[i];
}
};
//parent class of BusDigital and BusPwm
class Bus {
public:
Bus(uint8_t type, uint16_t start) {
_type = type;
_start = start;
};
virtual void show() {}
virtual bool canShow() { return true; }
virtual void setPixelColor(uint16_t pix, uint32_t c) {};
virtual void setBrightness(uint8_t b) {};
virtual uint32_t getPixelColor(uint16_t pix) { return 0; };
virtual void cleanup() {};
virtual ~Bus() { //throw the bus under the bus
}
virtual uint8_t getPins(uint8_t* pinArray) { return 0; }
inline uint16_t getStart() {
return _start;
}
inline void setStart(uint16_t start) {
_start = start;
}
virtual uint16_t getLength() {
return 1;
}
virtual void setColorOrder() {}
virtual uint8_t getColorOrder() {
return COL_ORDER_RGB;
}
virtual bool isRgbw() {
return false;
}
virtual uint8_t skippedLeds() {
return 0;
}
inline uint8_t getType() {
return _type;
}
inline bool isOk() {
return _valid;
}
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;
return false;
}
bool reversed = false;
protected:
uint8_t _type = TYPE_NONE;
uint8_t _bri = 255;
uint16_t _start = 0;
bool _valid = false;
};
class BusDigital : public Bus {
public:
BusDigital(BusConfig &bc, uint8_t nr) : Bus(bc.type, bc.start) {
if (!IS_DIGITAL(bc.type) || !bc.count) return;
_pins[0] = bc.pins[0];
if (!pinManager.allocatePin(_pins[0])) return;
if (IS_2PIN(bc.type)) {
_pins[1] = bc.pins[1];
if (!pinManager.allocatePin(_pins[1])) {
cleanup(); return;
}
}
reversed = bc.reversed;
_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);
_valid = (_busPtr != nullptr);
_colorOrder = bc.colorOrder;
//Serial.printf("Successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u)\n",nr, len, 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);
}
#endif
_bri = b;
PolyBus::setBrightness(_busPtr, _iType, b);
}
void setPixelColor(uint16_t pix, uint32_t c) {
if (reversed) pix = _len - pix -1;
else pix += _skip;
PolyBus::setPixelColor(_busPtr, _iType, pix, c, _colorOrder);
}
uint32_t getPixelColor(uint16_t pix) {
if (reversed) pix = _len - pix -1;
else pix += _skip;
return PolyBus::getPixelColor(_busPtr, _iType, pix, _colorOrder);
}
inline uint8_t getColorOrder() {
return _colorOrder;
}
inline 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) {
if (colorOrder > 5) return;
_colorOrder = colorOrder;
}
inline bool isRgbw() {
return (_type == TYPE_SK6812_RGBW || _type == TYPE_TM1814);
}
inline uint8_t skippedLeds() {
return _skip;
}
inline void reinit() {
PolyBus::begin(_busPtr, _iType, _pins);
}
void cleanup() {
//Serial.println("Digital Cleanup");
PolyBus::cleanup(_busPtr, _iType);
_iType = I_NONE;
_valid = false;
_busPtr = nullptr;
pinManager.deallocatePin(_pins[0]);
pinManager.deallocatePin(_pins[1]);
}
~BusDigital() {
cleanup();
}
private:
uint8_t _colorOrder = COL_ORDER_GRB;
uint8_t _pins[2] = {255, 255};
uint8_t _iType = I_NONE;
uint16_t _len = 0;
uint8_t _skip = 0;
void * _busPtr = nullptr;
};
class BusPwm : public Bus {
public:
BusPwm(BusConfig &bc) : Bus(bc.type, bc.start) {
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++) {
_pins[i] = bc.pins[i];
if (!pinManager.allocatePin(_pins[i])) {
deallocatePins(); return;
}
#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 setPixelColor(uint16_t pix, uint32_t c) {
if (pix != 0 || !_valid) return; //only react to first pixel
uint8_t r = c >> 16;
uint8_t g = c >> 8;
uint8_t b = c ;
uint8_t w = c >> 24;
switch (_type) {
case TYPE_ANALOG_1CH: //one channel (white), use highest RGBW value
_data[0] = max(r, max(g, max(b, w))); break;
case TYPE_ANALOG_2CH: //warm white + cold white, we'll need some nice handling here, for now just R+G channels
case TYPE_ANALOG_3CH: //standard dumb RGB
case TYPE_ANALOG_4CH: //RGBW
case TYPE_ANALOG_5CH: //we'll want the white handling from 2CH here + RGB
_data[0] = r; _data[1] = g; _data[2] = b; _data[3] = w; _data[4] = 0; break;
default: return;
}
}
//does no index check
uint32_t getPixelColor(uint16_t pix) {
return ((_data[3] << 24) | (_data[0] << 16) | (_data[1] << 8) | (_data[2]));
}
void show() {
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
}
}
inline void setBrightness(uint8_t b) {
_bri = b;
}
uint8_t getPins(uint8_t* pinArray) {
uint8_t numPins = NUM_PWM_PINS(_type);
for (uint8_t i = 0; i < numPins; i++) pinArray[i] = _pins[i];
return numPins;
}
bool isRgbw() {
return (_type > TYPE_ONOFF && _type <= TYPE_ANALOG_5CH && _type != TYPE_ANALOG_3CH);
}
inline void cleanup() {
deallocatePins();
}
~BusPwm() {
cleanup();
}
private:
uint8_t _pins[5] = {255, 255, 255, 255, 255};
uint8_t _data[5] = {255, 255, 255, 255, 255};
#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++) {
if (!pinManager.isPinOk(_pins[i])) continue;
#ifdef ESP8266
digitalWrite(_pins[i], LOW); //turn off PWM interrupt
#else
if (_ledcStart < 16) ledcDetachPin(_pins[i]);
#endif
pinManager.deallocatePin(_pins[i]);
}
#ifdef ARDUINO_ARCH_ESP32
pinManager.deallocateLedc(_ledcStart, numPins);
#endif
}
};
class BusManager {
public:
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;
if (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;
}
int add(BusConfig &bc) {
if (numBusses >= WLED_MAX_BUSSES) return -1;
if (IS_DIGITAL(bc.type)) {
busses[numBusses] = new BusDigital(bc, numBusses);
} else {
busses[numBusses] = new BusPwm(bc);
}
return numBusses++;
}
//do not call this method from system context (network callback)
void removeAll() {
//Serial.println("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();
}
}
void setPixelColor(uint16_t pix, uint32_t c) {
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);
break;
}
}
void setBrightness(uint8_t b) {
for (uint8_t i = 0; i < numBusses; i++) {
busses[i]->setBrightness(b);
}
}
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];
}
inline uint8_t getNumBusses() {
return numBusses;
}
uint16_t getTotalLength() {
uint16_t len = 0;
for (uint8_t i=0; i<numBusses; i++ ) len += busses[i]->getLength();
return len;
}
static inline bool isRgbw(uint8_t type) {
return Bus::isRgbw(type);
}
//Return true if the strip requires a refresh to stay off.
static bool isOffRefreshRequred(uint8_t type) {
return type == TYPE_TM1814;
}
private:
uint8_t numBusses = 0;
Bus* busses[WLED_MAX_BUSSES];
};
#endif

894
wled00/bus_wrapper.h Normal file
View File

@@ -0,0 +1,894 @@
#ifndef BusWrapper_h
#define BusWrapper_h
#include "NeoPixelBrightnessBus.h"
//Hardware SPI Pins
#define P_8266_HS_MOSI 13
#define P_8266_HS_CLK 14
#define P_32_HS_MOSI 13
#define P_32_HS_CLK 14
#define P_32_VS_MOSI 23
#define P_32_VS_CLK 18
//The dirty list of possible bus types. Quite a lot...
#define I_NONE 0
//ESP8266 RGB
#define I_8266_U0_NEO_3 1
#define I_8266_U1_NEO_3 2
#define I_8266_DM_NEO_3 3
#define I_8266_BB_NEO_3 4
//RGBW
#define I_8266_U0_NEO_4 5
#define I_8266_U1_NEO_4 6
#define I_8266_DM_NEO_4 7
#define I_8266_BB_NEO_4 8
//400Kbps
#define I_8266_U0_400_3 9
#define I_8266_U1_400_3 10
#define I_8266_DM_400_3 11
#define I_8266_BB_400_3 12
//TM1814 (RGBW)
#define I_8266_U0_TM1_4 13
#define I_8266_U1_TM1_4 14
#define I_8266_DM_TM1_4 15
#define I_8266_BB_TM1_4 16
/*** ESP32 Neopixel methods ***/
//RGB
#define I_32_R0_NEO_3 17
#define I_32_R1_NEO_3 18
#define I_32_R2_NEO_3 19
#define I_32_R3_NEO_3 20
#define I_32_R4_NEO_3 21
#define I_32_R5_NEO_3 22
#define I_32_R6_NEO_3 23
#define I_32_R7_NEO_3 24
#define I_32_I0_NEO_3 25
#define I_32_I1_NEO_3 26
//RGBW
#define I_32_R0_NEO_4 27
#define I_32_R1_NEO_4 28
#define I_32_R2_NEO_4 29
#define I_32_R3_NEO_4 30
#define I_32_R4_NEO_4 31
#define I_32_R5_NEO_4 32
#define I_32_R6_NEO_4 33
#define I_32_R7_NEO_4 34
#define I_32_I0_NEO_4 35
#define I_32_I1_NEO_4 36
//400Kbps
#define I_32_R0_400_3 37
#define I_32_R1_400_3 38
#define I_32_R2_400_3 39
#define I_32_R3_400_3 40
#define I_32_R4_400_3 41
#define I_32_R5_400_3 42
#define I_32_R6_400_3 43
#define I_32_R7_400_3 44
#define I_32_I0_400_3 45
#define I_32_I1_400_3 46
//TM1814 (RGBW)
#define I_32_R0_TM1_4 47
#define I_32_R1_TM1_4 48
#define I_32_R2_TM1_4 49
#define I_32_R3_TM1_4 50
#define I_32_R4_TM1_4 51
#define I_32_R5_TM1_4 52
#define I_32_R6_TM1_4 53
#define I_32_R7_TM1_4 54
#define I_32_I0_TM1_4 55
#define I_32_I1_TM1_4 56
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
//APA102
#define I_HS_DOT_3 57 //hardware SPI
#define I_SS_DOT_3 58 //soft SPI
//LPD8806
#define I_HS_LPD_3 59
#define I_SS_LPD_3 60
//WS2801
#define I_HS_WS1_3 61
#define I_SS_WS1_3 62
//P9813
#define I_HS_P98_3 63
#define I_SS_P98_3 64
/*** ESP8266 Neopixel methods ***/
#ifdef ESP8266
//RGB
#define B_8266_U0_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Uart0Ws2813Method> //3 chan, esp8266, gpio1
#define B_8266_U1_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Uart1Ws2813Method> //3 chan, esp8266, gpio2
#define B_8266_DM_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Dma800KbpsMethod> //3 chan, esp8266, gpio3
#define B_8266_BB_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266BitBang800KbpsMethod> //3 chan, esp8266, bb (any pin but 16)
//RGBW
#define B_8266_U0_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp8266Uart0Ws2813Method> //4 chan, esp8266, gpio1
#define B_8266_U1_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp8266Uart1Ws2813Method> //4 chan, esp8266, gpio2
#define B_8266_DM_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp8266Dma800KbpsMethod> //4 chan, esp8266, gpio3
#define B_8266_BB_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp8266BitBang800KbpsMethod> //4 chan, esp8266, bb (any pin)
//400Kbps
#define B_8266_U0_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Uart0400KbpsMethod> //3 chan, esp8266, gpio1
#define B_8266_U1_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Uart1400KbpsMethod> //3 chan, esp8266, gpio2
#define B_8266_DM_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Dma400KbpsMethod> //3 chan, esp8266, gpio3
#define B_8266_BB_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266BitBang400KbpsMethod> //3 chan, esp8266, bb (any pin)
//TM1814 (RGBW)
#define B_8266_U0_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266Uart0Tm1814Method>
#define B_8266_U1_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266Uart1Tm1814Method>
#define B_8266_DM_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266DmaTm1814Method>
#define B_8266_BB_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266BitBangTm1814Method>
#endif
/*** ESP32 Neopixel methods ***/
#ifdef ARDUINO_ARCH_ESP32
//RGB
#define B_32_R0_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32Rmt0Ws2812xMethod>
#define B_32_R1_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32Rmt1Ws2812xMethod>
#define B_32_R2_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32Rmt2Ws2812xMethod>
#define B_32_R3_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32Rmt3Ws2812xMethod>
#define B_32_R4_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32Rmt4Ws2812xMethod>
#define B_32_R5_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32Rmt5Ws2812xMethod>
#define B_32_R6_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32Rmt6Ws2812xMethod>
#define B_32_R7_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32Rmt7Ws2812xMethod>
#define B_32_I0_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s0800KbpsMethod>
#define B_32_I1_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s1800KbpsMethod>
//RGBW
#define B_32_R0_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32Rmt0Ws2812xMethod>
#define B_32_R1_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32Rmt1Ws2812xMethod>
#define B_32_R2_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32Rmt2Ws2812xMethod>
#define B_32_R3_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32Rmt3Ws2812xMethod>
#define B_32_R4_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32Rmt4Ws2812xMethod>
#define B_32_R5_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32Rmt5Ws2812xMethod>
#define B_32_R6_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32Rmt6Ws2812xMethod>
#define B_32_R7_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32Rmt7Ws2812xMethod>
#define B_32_I0_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32I2s0800KbpsMethod>
#define B_32_I1_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32I2s1800KbpsMethod>
//400Kbps
#define B_32_R0_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32Rmt0400KbpsMethod>
#define B_32_R1_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32Rmt1400KbpsMethod>
#define B_32_R2_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32Rmt2400KbpsMethod>
#define B_32_R3_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32Rmt3400KbpsMethod>
#define B_32_R4_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32Rmt4400KbpsMethod>
#define B_32_R5_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32Rmt5400KbpsMethod>
#define B_32_R6_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32Rmt6400KbpsMethod>
#define B_32_R7_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32Rmt7400KbpsMethod>
#define B_32_I0_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s0400KbpsMethod>
#define B_32_I1_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s1400KbpsMethod>
//TM1814 (RGBW)
#define B_32_R0_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32Rmt0Tm1814Method>
#define B_32_R1_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32Rmt1Tm1814Method>
#define B_32_R2_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32Rmt2Tm1814Method>
#define B_32_R3_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32Rmt3Tm1814Method>
#define B_32_R4_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32Rmt4Tm1814Method>
#define B_32_R5_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32Rmt5Tm1814Method>
#define B_32_R6_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32Rmt6Tm1814Method>
#define B_32_R7_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32Rmt7Tm1814Method>
#define B_32_I0_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32I2s0Tm1814Method>
#define B_32_I1_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32I2s1Tm1814Method>
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
#endif
//APA102
#define B_HS_DOT_3 NeoPixelBrightnessBus<DotStarBgrFeature, DotStarSpiMethod> //hardware SPI
#define B_SS_DOT_3 NeoPixelBrightnessBus<DotStarBgrFeature, DotStarMethod> //soft SPI
//LPD8806
#define B_HS_LPD_3 NeoPixelBrightnessBus<Lpd8806GrbFeature, Lpd8806SpiMethod>
#define B_SS_LPD_3 NeoPixelBrightnessBus<Lpd8806GrbFeature, Lpd8806Method>
//WS2801
#define B_HS_WS1_3 NeoPixelBrightnessBus<NeoRbgFeature, NeoWs2801SpiMethod>
#define B_SS_WS1_3 NeoPixelBrightnessBus<NeoRbgFeature, NeoWs2801Method>
//P9813
#define B_HS_P98_3 NeoPixelBrightnessBus<P9813BgrFeature, P9813SpiMethod>
#define B_SS_P98_3 NeoPixelBrightnessBus<P9813BgrFeature, P9813Method>
//handles pointer type conversion for all possible bus types
class PolyBus {
public:
// Begin & initialize the PixelSettings for TM1814 strips.
template <class T>
static void beginTM1814(void* busPtr) {
T tm1814_strip = static_cast<T>(busPtr);
tm1814_strip->Begin();
// Max current for each LED (22.5 mA).
tm1814_strip->SetPixelSettings(NeoTm1814Settings(/*R*/225, /*G*/225, /*B*/225, /*W*/225));
}
static void begin(void* busPtr, uint8_t busType, uint8_t* pins) {
switch (busType) {
case I_NONE: break;
#ifdef ESP8266
case I_8266_U0_NEO_3: (static_cast<B_8266_U0_NEO_3*>(busPtr))->Begin(); break;
case I_8266_U1_NEO_3: (static_cast<B_8266_U1_NEO_3*>(busPtr))->Begin(); break;
case I_8266_DM_NEO_3: (static_cast<B_8266_DM_NEO_3*>(busPtr))->Begin(); break;
case I_8266_BB_NEO_3: (static_cast<B_8266_BB_NEO_3*>(busPtr))->Begin(); break;
case I_8266_U0_NEO_4: (static_cast<B_8266_U0_NEO_4*>(busPtr))->Begin(); break;
case I_8266_U1_NEO_4: (static_cast<B_8266_U1_NEO_4*>(busPtr))->Begin(); break;
case I_8266_DM_NEO_4: (static_cast<B_8266_DM_NEO_4*>(busPtr))->Begin(); break;
case I_8266_BB_NEO_4: (static_cast<B_8266_BB_NEO_4*>(busPtr))->Begin(); break;
case I_8266_U0_400_3: (static_cast<B_8266_U0_400_3*>(busPtr))->Begin(); break;
case I_8266_U1_400_3: (static_cast<B_8266_U1_400_3*>(busPtr))->Begin(); break;
case I_8266_DM_400_3: (static_cast<B_8266_DM_400_3*>(busPtr))->Begin(); break;
case I_8266_BB_400_3: (static_cast<B_8266_BB_400_3*>(busPtr))->Begin(); break;
case I_8266_U0_TM1_4: beginTM1814<B_8266_U0_TM1_4*>(busPtr); break;
case I_8266_U1_TM1_4: beginTM1814<B_8266_U1_TM1_4*>(busPtr); break;
case I_8266_DM_TM1_4: beginTM1814<B_8266_DM_TM1_4*>(busPtr); break;
case I_8266_BB_TM1_4: beginTM1814<B_8266_BB_TM1_4*>(busPtr); break;
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->Begin(); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->Begin(); break;
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->Begin(); break;
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->Begin(); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_R0_NEO_3: (static_cast<B_32_R0_NEO_3*>(busPtr))->Begin(); break;
case I_32_R1_NEO_3: (static_cast<B_32_R1_NEO_3*>(busPtr))->Begin(); break;
case I_32_R2_NEO_3: (static_cast<B_32_R2_NEO_3*>(busPtr))->Begin(); break;
case I_32_R3_NEO_3: (static_cast<B_32_R3_NEO_3*>(busPtr))->Begin(); break;
case I_32_R4_NEO_3: (static_cast<B_32_R4_NEO_3*>(busPtr))->Begin(); break;
case I_32_R5_NEO_3: (static_cast<B_32_R5_NEO_3*>(busPtr))->Begin(); break;
case I_32_R6_NEO_3: (static_cast<B_32_R6_NEO_3*>(busPtr))->Begin(); break;
case I_32_R7_NEO_3: (static_cast<B_32_R7_NEO_3*>(busPtr))->Begin(); break;
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->Begin(); break;
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->Begin(); break;
case I_32_R0_NEO_4: (static_cast<B_32_R0_NEO_4*>(busPtr))->Begin(); break;
case I_32_R1_NEO_4: (static_cast<B_32_R1_NEO_4*>(busPtr))->Begin(); break;
case I_32_R2_NEO_4: (static_cast<B_32_R2_NEO_4*>(busPtr))->Begin(); break;
case I_32_R3_NEO_4: (static_cast<B_32_R3_NEO_4*>(busPtr))->Begin(); break;
case I_32_R4_NEO_4: (static_cast<B_32_R4_NEO_4*>(busPtr))->Begin(); break;
case I_32_R5_NEO_4: (static_cast<B_32_R5_NEO_4*>(busPtr))->Begin(); break;
case I_32_R6_NEO_4: (static_cast<B_32_R6_NEO_4*>(busPtr))->Begin(); break;
case I_32_R7_NEO_4: (static_cast<B_32_R7_NEO_4*>(busPtr))->Begin(); break;
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->Begin(); break;
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->Begin(); break;
case I_32_R0_400_3: (static_cast<B_32_R0_400_3*>(busPtr))->Begin(); break;
case I_32_R1_400_3: (static_cast<B_32_R1_400_3*>(busPtr))->Begin(); break;
case I_32_R2_400_3: (static_cast<B_32_R2_400_3*>(busPtr))->Begin(); break;
case I_32_R3_400_3: (static_cast<B_32_R3_400_3*>(busPtr))->Begin(); break;
case I_32_R4_400_3: (static_cast<B_32_R4_400_3*>(busPtr))->Begin(); break;
case I_32_R5_400_3: (static_cast<B_32_R5_400_3*>(busPtr))->Begin(); break;
case I_32_R6_400_3: (static_cast<B_32_R6_400_3*>(busPtr))->Begin(); break;
case I_32_R7_400_3: (static_cast<B_32_R7_400_3*>(busPtr))->Begin(); break;
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->Begin(); break;
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->Begin(); break;
case I_32_R0_TM1_4: beginTM1814<B_32_R0_TM1_4*>(busPtr); break;
case I_32_R1_TM1_4: beginTM1814<B_32_R1_TM1_4*>(busPtr); break;
case I_32_R2_TM1_4: beginTM1814<B_32_R2_TM1_4*>(busPtr); break;
case I_32_R3_TM1_4: beginTM1814<B_32_R3_TM1_4*>(busPtr); break;
case I_32_R4_TM1_4: beginTM1814<B_32_R4_TM1_4*>(busPtr); break;
case I_32_R5_TM1_4: beginTM1814<B_32_R5_TM1_4*>(busPtr); break;
case I_32_R6_TM1_4: beginTM1814<B_32_R6_TM1_4*>(busPtr); break;
case I_32_R7_TM1_4: beginTM1814<B_32_R7_TM1_4*>(busPtr); break;
case I_32_I0_TM1_4: beginTM1814<B_32_I0_TM1_4*>(busPtr); break;
case I_32_I1_TM1_4: beginTM1814<B_32_I1_TM1_4*>(busPtr); break;
// ESP32 can (and should, to avoid inadvertantly driving the chip select signal) specify the pins used for SPI, but only in begin()
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->Begin(pins[1], -1, pins[0], -1); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->Begin(pins[1], -1, pins[0], -1); break;
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->Begin(pins[1], -1, pins[0], -1); break;
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->Begin(pins[1], -1, pins[0], -1); break;
#endif
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->Begin(); break;
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->Begin(); break;
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->Begin(); break;
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->Begin(); break;
}
};
static void* create(uint8_t busType, uint8_t* pins, uint16_t len) {
void* busPtr = nullptr;
switch (busType) {
case I_NONE: break;
#ifdef ESP8266
case I_8266_U0_NEO_3: busPtr = new B_8266_U0_NEO_3(len, pins[0]); break;
case I_8266_U1_NEO_3: busPtr = new B_8266_U1_NEO_3(len, pins[0]); break;
case I_8266_DM_NEO_3: busPtr = new B_8266_DM_NEO_3(len, pins[0]); break;
case I_8266_BB_NEO_3: busPtr = new B_8266_BB_NEO_3(len, pins[0]); break;
case I_8266_U0_NEO_4: busPtr = new B_8266_U0_NEO_4(len, pins[0]); break;
case I_8266_U1_NEO_4: busPtr = new B_8266_U1_NEO_4(len, pins[0]); break;
case I_8266_DM_NEO_4: busPtr = new B_8266_DM_NEO_4(len, pins[0]); break;
case I_8266_BB_NEO_4: busPtr = new B_8266_BB_NEO_4(len, pins[0]); break;
case I_8266_U0_400_3: busPtr = new B_8266_U0_400_3(len, pins[0]); break;
case I_8266_U1_400_3: busPtr = new B_8266_U1_400_3(len, pins[0]); break;
case I_8266_DM_400_3: busPtr = new B_8266_DM_400_3(len, pins[0]); break;
case I_8266_BB_400_3: busPtr = new B_8266_BB_400_3(len, pins[0]); break;
case I_8266_U0_TM1_4: busPtr = new B_8266_U0_TM1_4(len, pins[0]); break;
case I_8266_U1_TM1_4: busPtr = new B_8266_U1_TM1_4(len, pins[0]); break;
case I_8266_DM_TM1_4: busPtr = new B_8266_DM_TM1_4(len, pins[0]); break;
case I_8266_BB_TM1_4: busPtr = new B_8266_BB_TM1_4(len, pins[0]); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_R0_NEO_3: busPtr = new B_32_R0_NEO_3(len, pins[0]); break;
case I_32_R1_NEO_3: busPtr = new B_32_R1_NEO_3(len, pins[0]); break;
case I_32_R2_NEO_3: busPtr = new B_32_R2_NEO_3(len, pins[0]); break;
case I_32_R3_NEO_3: busPtr = new B_32_R3_NEO_3(len, pins[0]); break;
case I_32_R4_NEO_3: busPtr = new B_32_R4_NEO_3(len, pins[0]); break;
case I_32_R5_NEO_3: busPtr = new B_32_R5_NEO_3(len, pins[0]); break;
case I_32_R6_NEO_3: busPtr = new B_32_R6_NEO_3(len, pins[0]); break;
case I_32_R7_NEO_3: busPtr = new B_32_R7_NEO_3(len, pins[0]); break;
case I_32_I0_NEO_3: busPtr = new B_32_I0_NEO_3(len, pins[0]); break;
case I_32_I1_NEO_3: busPtr = new B_32_I1_NEO_3(len, pins[0]); break;
case I_32_R0_NEO_4: busPtr = new B_32_R0_NEO_4(len, pins[0]); break;
case I_32_R1_NEO_4: busPtr = new B_32_R1_NEO_4(len, pins[0]); break;
case I_32_R2_NEO_4: busPtr = new B_32_R2_NEO_4(len, pins[0]); break;
case I_32_R3_NEO_4: busPtr = new B_32_R3_NEO_4(len, pins[0]); break;
case I_32_R4_NEO_4: busPtr = new B_32_R4_NEO_4(len, pins[0]); break;
case I_32_R5_NEO_4: busPtr = new B_32_R5_NEO_4(len, pins[0]); break;
case I_32_R6_NEO_4: busPtr = new B_32_R6_NEO_4(len, pins[0]); break;
case I_32_R7_NEO_4: busPtr = new B_32_R7_NEO_4(len, pins[0]); break;
case I_32_I0_NEO_4: busPtr = new B_32_I0_NEO_4(len, pins[0]); break;
case I_32_I1_NEO_4: busPtr = new B_32_I1_NEO_4(len, pins[0]); break;
case I_32_R0_400_3: busPtr = new B_32_R0_400_3(len, pins[0]); break;
case I_32_R1_400_3: busPtr = new B_32_R1_400_3(len, pins[0]); break;
case I_32_R2_400_3: busPtr = new B_32_R2_400_3(len, pins[0]); break;
case I_32_R3_400_3: busPtr = new B_32_R3_400_3(len, pins[0]); break;
case I_32_R4_400_3: busPtr = new B_32_R4_400_3(len, pins[0]); break;
case I_32_R5_400_3: busPtr = new B_32_R5_400_3(len, pins[0]); break;
case I_32_R6_400_3: busPtr = new B_32_R6_400_3(len, pins[0]); break;
case I_32_R7_400_3: busPtr = new B_32_R7_400_3(len, pins[0]); break;
case I_32_I0_400_3: busPtr = new B_32_I0_400_3(len, pins[0]); break;
case I_32_I1_400_3: busPtr = new B_32_I1_400_3(len, pins[0]); break;
case I_32_R0_TM1_4: busPtr = new B_32_R0_TM1_4(len, pins[0]); break;
case I_32_R1_TM1_4: busPtr = new B_32_R1_TM1_4(len, pins[0]); break;
case I_32_R2_TM1_4: busPtr = new B_32_R2_TM1_4(len, pins[0]); break;
case I_32_R3_TM1_4: busPtr = new B_32_R3_TM1_4(len, pins[0]); break;
case I_32_R4_TM1_4: busPtr = new B_32_R4_TM1_4(len, pins[0]); break;
case I_32_R5_TM1_4: busPtr = new B_32_R5_TM1_4(len, pins[0]); break;
case I_32_R6_TM1_4: busPtr = new B_32_R6_TM1_4(len, pins[0]); break;
case I_32_R7_TM1_4: busPtr = new B_32_R7_TM1_4(len, pins[0]); break;
case I_32_I0_TM1_4: busPtr = new B_32_I0_TM1_4(len, pins[0]); break;
case I_32_I1_TM1_4: busPtr = new B_32_I1_TM1_4(len, pins[0]); break;
#endif
// for 2-wire: pins[1] is clk, pins[0] is dat. begin expects (len, clk, dat)
case I_HS_DOT_3: busPtr = new B_HS_DOT_3(len, pins[1], pins[0]); break;
case I_SS_DOT_3: busPtr = new B_SS_DOT_3(len, pins[1], pins[0]); break;
case I_HS_LPD_3: busPtr = new B_HS_LPD_3(len, pins[1], pins[0]); break;
case I_SS_LPD_3: busPtr = new B_SS_LPD_3(len, pins[1], pins[0]); break;
case I_HS_WS1_3: busPtr = new B_HS_WS1_3(len, pins[1], pins[0]); break;
case I_SS_WS1_3: busPtr = new B_SS_WS1_3(len, pins[1], pins[0]); break;
case I_HS_P98_3: busPtr = new B_HS_P98_3(len, pins[1], pins[0]); break;
case I_SS_P98_3: busPtr = new B_SS_P98_3(len, pins[1], pins[0]); break;
}
begin(busPtr, busType, pins);
return busPtr;
};
static void show(void* busPtr, uint8_t busType) {
switch (busType) {
case I_NONE: break;
#ifdef ESP8266
case I_8266_U0_NEO_3: (static_cast<B_8266_U0_NEO_3*>(busPtr))->Show(); break;
case I_8266_U1_NEO_3: (static_cast<B_8266_U1_NEO_3*>(busPtr))->Show(); break;
case I_8266_DM_NEO_3: (static_cast<B_8266_DM_NEO_3*>(busPtr))->Show(); break;
case I_8266_BB_NEO_3: (static_cast<B_8266_BB_NEO_3*>(busPtr))->Show(); break;
case I_8266_U0_NEO_4: (static_cast<B_8266_U0_NEO_4*>(busPtr))->Show(); break;
case I_8266_U1_NEO_4: (static_cast<B_8266_U1_NEO_4*>(busPtr))->Show(); break;
case I_8266_DM_NEO_4: (static_cast<B_8266_DM_NEO_4*>(busPtr))->Show(); break;
case I_8266_BB_NEO_4: (static_cast<B_8266_BB_NEO_4*>(busPtr))->Show(); break;
case I_8266_U0_400_3: (static_cast<B_8266_U0_400_3*>(busPtr))->Show(); break;
case I_8266_U1_400_3: (static_cast<B_8266_U1_400_3*>(busPtr))->Show(); break;
case I_8266_DM_400_3: (static_cast<B_8266_DM_400_3*>(busPtr))->Show(); break;
case I_8266_BB_400_3: (static_cast<B_8266_BB_400_3*>(busPtr))->Show(); break;
case I_8266_U0_TM1_4: (static_cast<B_8266_U0_TM1_4*>(busPtr))->Show(); break;
case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->Show(); break;
case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->Show(); break;
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->Show(); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_R0_NEO_3: (static_cast<B_32_R0_NEO_3*>(busPtr))->Show(); break;
case I_32_R1_NEO_3: (static_cast<B_32_R1_NEO_3*>(busPtr))->Show(); break;
case I_32_R2_NEO_3: (static_cast<B_32_R2_NEO_3*>(busPtr))->Show(); break;
case I_32_R3_NEO_3: (static_cast<B_32_R3_NEO_3*>(busPtr))->Show(); break;
case I_32_R4_NEO_3: (static_cast<B_32_R4_NEO_3*>(busPtr))->Show(); break;
case I_32_R5_NEO_3: (static_cast<B_32_R5_NEO_3*>(busPtr))->Show(); break;
case I_32_R6_NEO_3: (static_cast<B_32_R6_NEO_3*>(busPtr))->Show(); break;
case I_32_R7_NEO_3: (static_cast<B_32_R7_NEO_3*>(busPtr))->Show(); break;
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->Show(); break;
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->Show(); break;
case I_32_R0_NEO_4: (static_cast<B_32_R0_NEO_4*>(busPtr))->Show(); break;
case I_32_R1_NEO_4: (static_cast<B_32_R1_NEO_4*>(busPtr))->Show(); break;
case I_32_R2_NEO_4: (static_cast<B_32_R2_NEO_4*>(busPtr))->Show(); break;
case I_32_R3_NEO_4: (static_cast<B_32_R3_NEO_4*>(busPtr))->Show(); break;
case I_32_R4_NEO_4: (static_cast<B_32_R4_NEO_4*>(busPtr))->Show(); break;
case I_32_R5_NEO_4: (static_cast<B_32_R5_NEO_4*>(busPtr))->Show(); break;
case I_32_R6_NEO_4: (static_cast<B_32_R6_NEO_4*>(busPtr))->Show(); break;
case I_32_R7_NEO_4: (static_cast<B_32_R7_NEO_4*>(busPtr))->Show(); break;
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->Show(); break;
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->Show(); break;
case I_32_R0_400_3: (static_cast<B_32_R0_400_3*>(busPtr))->Show(); break;
case I_32_R1_400_3: (static_cast<B_32_R1_400_3*>(busPtr))->Show(); break;
case I_32_R2_400_3: (static_cast<B_32_R2_400_3*>(busPtr))->Show(); break;
case I_32_R3_400_3: (static_cast<B_32_R3_400_3*>(busPtr))->Show(); break;
case I_32_R4_400_3: (static_cast<B_32_R4_400_3*>(busPtr))->Show(); break;
case I_32_R5_400_3: (static_cast<B_32_R5_400_3*>(busPtr))->Show(); break;
case I_32_R6_400_3: (static_cast<B_32_R6_400_3*>(busPtr))->Show(); break;
case I_32_R7_400_3: (static_cast<B_32_R7_400_3*>(busPtr))->Show(); break;
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->Show(); break;
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->Show(); break;
case I_32_R0_TM1_4: (static_cast<B_32_R0_TM1_4*>(busPtr))->Show(); break;
case I_32_R1_TM1_4: (static_cast<B_32_R1_TM1_4*>(busPtr))->Show(); break;
case I_32_R2_TM1_4: (static_cast<B_32_R2_TM1_4*>(busPtr))->Show(); break;
case I_32_R3_TM1_4: (static_cast<B_32_R3_TM1_4*>(busPtr))->Show(); break;
case I_32_R4_TM1_4: (static_cast<B_32_R4_TM1_4*>(busPtr))->Show(); break;
case I_32_R5_TM1_4: (static_cast<B_32_R5_TM1_4*>(busPtr))->Show(); break;
case I_32_R6_TM1_4: (static_cast<B_32_R6_TM1_4*>(busPtr))->Show(); break;
case I_32_R7_TM1_4: (static_cast<B_32_R7_TM1_4*>(busPtr))->Show(); break;
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->Show(); break;
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->Show(); break;
#endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->Show(); break;
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->Show(); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->Show(); break;
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->Show(); break;
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->Show(); break;
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->Show(); break;
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->Show(); break;
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->Show(); break;
}
};
static bool canShow(void* busPtr, uint8_t busType) {
switch (busType) {
case I_NONE: return true;
#ifdef ESP8266
case I_8266_U0_NEO_3: return (static_cast<B_8266_U0_NEO_3*>(busPtr))->CanShow(); break;
case I_8266_U1_NEO_3: return (static_cast<B_8266_U1_NEO_3*>(busPtr))->CanShow(); break;
case I_8266_DM_NEO_3: return (static_cast<B_8266_DM_NEO_3*>(busPtr))->CanShow(); break;
case I_8266_BB_NEO_3: return (static_cast<B_8266_BB_NEO_3*>(busPtr))->CanShow(); break;
case I_8266_U0_NEO_4: return (static_cast<B_8266_U0_NEO_4*>(busPtr))->CanShow(); break;
case I_8266_U1_NEO_4: return (static_cast<B_8266_U1_NEO_4*>(busPtr))->CanShow(); break;
case I_8266_DM_NEO_4: return (static_cast<B_8266_DM_NEO_4*>(busPtr))->CanShow(); break;
case I_8266_BB_NEO_4: return (static_cast<B_8266_BB_NEO_4*>(busPtr))->CanShow(); break;
case I_8266_U0_400_3: return (static_cast<B_8266_U0_400_3*>(busPtr))->CanShow(); break;
case I_8266_U1_400_3: return (static_cast<B_8266_U1_400_3*>(busPtr))->CanShow(); break;
case I_8266_DM_400_3: return (static_cast<B_8266_DM_400_3*>(busPtr))->CanShow(); break;
case I_8266_BB_400_3: return (static_cast<B_8266_BB_400_3*>(busPtr))->CanShow(); break;
case I_8266_U0_TM1_4: return (static_cast<B_8266_U0_TM1_4*>(busPtr))->CanShow(); break;
case I_8266_U1_TM1_4: return (static_cast<B_8266_U1_TM1_4*>(busPtr))->CanShow(); break;
case I_8266_DM_TM1_4: return (static_cast<B_8266_DM_TM1_4*>(busPtr))->CanShow(); break;
case I_8266_BB_TM1_4: return (static_cast<B_8266_BB_TM1_4*>(busPtr))->CanShow(); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_R0_NEO_3: return (static_cast<B_32_R0_NEO_3*>(busPtr))->CanShow(); break;
case I_32_R1_NEO_3: return (static_cast<B_32_R1_NEO_3*>(busPtr))->CanShow(); break;
case I_32_R2_NEO_3: return (static_cast<B_32_R2_NEO_3*>(busPtr))->CanShow(); break;
case I_32_R3_NEO_3: return (static_cast<B_32_R3_NEO_3*>(busPtr))->CanShow(); break;
case I_32_R4_NEO_3: return (static_cast<B_32_R4_NEO_3*>(busPtr))->CanShow(); break;
case I_32_R5_NEO_3: return (static_cast<B_32_R5_NEO_3*>(busPtr))->CanShow(); break;
case I_32_R6_NEO_3: return (static_cast<B_32_R6_NEO_3*>(busPtr))->CanShow(); break;
case I_32_R7_NEO_3: return (static_cast<B_32_R7_NEO_3*>(busPtr))->CanShow(); break;
case I_32_I0_NEO_3: return (static_cast<B_32_I0_NEO_3*>(busPtr))->CanShow(); break;
case I_32_I1_NEO_3: return (static_cast<B_32_I1_NEO_3*>(busPtr))->CanShow(); break;
case I_32_R0_NEO_4: return (static_cast<B_32_R0_NEO_4*>(busPtr))->CanShow(); break;
case I_32_R1_NEO_4: return (static_cast<B_32_R1_NEO_4*>(busPtr))->CanShow(); break;
case I_32_R2_NEO_4: return (static_cast<B_32_R2_NEO_4*>(busPtr))->CanShow(); break;
case I_32_R3_NEO_4: return (static_cast<B_32_R3_NEO_4*>(busPtr))->CanShow(); break;
case I_32_R4_NEO_4: return (static_cast<B_32_R4_NEO_4*>(busPtr))->CanShow(); break;
case I_32_R5_NEO_4: return (static_cast<B_32_R5_NEO_4*>(busPtr))->CanShow(); break;
case I_32_R6_NEO_4: return (static_cast<B_32_R6_NEO_4*>(busPtr))->CanShow(); break;
case I_32_R7_NEO_4: return (static_cast<B_32_R7_NEO_4*>(busPtr))->CanShow(); break;
case I_32_I0_NEO_4: return (static_cast<B_32_I0_NEO_4*>(busPtr))->CanShow(); break;
case I_32_I1_NEO_4: return (static_cast<B_32_I1_NEO_4*>(busPtr))->CanShow(); break;
case I_32_R0_400_3: return (static_cast<B_32_R0_400_3*>(busPtr))->CanShow(); break;
case I_32_R1_400_3: return (static_cast<B_32_R1_400_3*>(busPtr))->CanShow(); break;
case I_32_R2_400_3: return (static_cast<B_32_R2_400_3*>(busPtr))->CanShow(); break;
case I_32_R3_400_3: return (static_cast<B_32_R3_400_3*>(busPtr))->CanShow(); break;
case I_32_R4_400_3: return (static_cast<B_32_R4_400_3*>(busPtr))->CanShow(); break;
case I_32_R5_400_3: return (static_cast<B_32_R5_400_3*>(busPtr))->CanShow(); break;
case I_32_R6_400_3: return (static_cast<B_32_R6_400_3*>(busPtr))->CanShow(); break;
case I_32_R7_400_3: return (static_cast<B_32_R7_400_3*>(busPtr))->CanShow(); break;
case I_32_I0_400_3: return (static_cast<B_32_I0_400_3*>(busPtr))->CanShow(); break;
case I_32_I1_400_3: return (static_cast<B_32_I1_400_3*>(busPtr))->CanShow(); break;
case I_32_R0_TM1_4: return (static_cast<B_32_R0_TM1_4*>(busPtr))->CanShow(); break;
case I_32_R1_TM1_4: return (static_cast<B_32_R1_TM1_4*>(busPtr))->CanShow(); break;
case I_32_R2_TM1_4: return (static_cast<B_32_R2_TM1_4*>(busPtr))->CanShow(); break;
case I_32_R3_TM1_4: return (static_cast<B_32_R3_TM1_4*>(busPtr))->CanShow(); break;
case I_32_R4_TM1_4: return (static_cast<B_32_R4_TM1_4*>(busPtr))->CanShow(); break;
case I_32_R5_TM1_4: return (static_cast<B_32_R5_TM1_4*>(busPtr))->CanShow(); break;
case I_32_R6_TM1_4: return (static_cast<B_32_R6_TM1_4*>(busPtr))->CanShow(); break;
case I_32_R7_TM1_4: return (static_cast<B_32_R7_TM1_4*>(busPtr))->CanShow(); break;
case I_32_I0_TM1_4: return (static_cast<B_32_I0_TM1_4*>(busPtr))->CanShow(); break;
case I_32_I1_TM1_4: return (static_cast<B_32_I1_TM1_4*>(busPtr))->CanShow(); break;
#endif
case I_HS_DOT_3: return (static_cast<B_HS_DOT_3*>(busPtr))->CanShow(); break;
case I_SS_DOT_3: return (static_cast<B_SS_DOT_3*>(busPtr))->CanShow(); break;
case I_HS_LPD_3: return (static_cast<B_HS_LPD_3*>(busPtr))->CanShow(); break;
case I_SS_LPD_3: return (static_cast<B_SS_LPD_3*>(busPtr))->CanShow(); break;
case I_HS_WS1_3: return (static_cast<B_HS_WS1_3*>(busPtr))->CanShow(); break;
case I_SS_WS1_3: return (static_cast<B_SS_WS1_3*>(busPtr))->CanShow(); break;
case I_HS_P98_3: return (static_cast<B_HS_P98_3*>(busPtr))->CanShow(); break;
case I_SS_P98_3: return (static_cast<B_SS_P98_3*>(busPtr))->CanShow(); break;
}
return true;
};
static void setPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint32_t c, uint8_t co) {
uint8_t r = c >> 16;
uint8_t g = c >> 8;
uint8_t b = c >> 0;
uint8_t w = c >> 24;
RgbwColor col;
//TODO make color order override possible on a per-strip basis
#ifdef COLOR_ORDER_OVERRIDE
if (pix >= COO_MIN && pix < COO_MAX) co = COO_ORDER;
#endif
//reorder channels to selected order
switch (co)
{
case 0: col.G = g; col.R = r; col.B = b; break; //0 = GRB, default
case 1: col.G = r; col.R = g; col.B = b; break; //1 = RGB, common for WS2811
case 2: col.G = b; col.R = r; col.B = g; break; //2 = BRG
case 3: col.G = r; col.R = b; col.B = g; break; //3 = RBG
case 4: col.G = b; col.R = g; col.B = r; break; //4 = BGR
default: col.G = g; col.R = b; col.B = r; break; //5 = GBR
}
col.W = w;
switch (busType) {
case I_NONE: break;
#ifdef ESP8266
case I_8266_U0_NEO_3: (static_cast<B_8266_U0_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_U1_NEO_3: (static_cast<B_8266_U1_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_DM_NEO_3: (static_cast<B_8266_DM_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_BB_NEO_3: (static_cast<B_8266_BB_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_U0_NEO_4: (static_cast<B_8266_U0_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_8266_U1_NEO_4: (static_cast<B_8266_U1_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_8266_DM_NEO_4: (static_cast<B_8266_DM_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_8266_BB_NEO_4: (static_cast<B_8266_BB_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_8266_U0_400_3: (static_cast<B_8266_U0_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_U1_400_3: (static_cast<B_8266_U1_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_DM_400_3: (static_cast<B_8266_DM_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_BB_400_3: (static_cast<B_8266_BB_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_U0_TM1_4: (static_cast<B_8266_U0_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_R0_NEO_3: (static_cast<B_32_R0_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_R1_NEO_3: (static_cast<B_32_R1_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_R2_NEO_3: (static_cast<B_32_R2_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_R3_NEO_3: (static_cast<B_32_R3_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_R4_NEO_3: (static_cast<B_32_R4_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_R5_NEO_3: (static_cast<B_32_R5_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_R6_NEO_3: (static_cast<B_32_R6_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_R7_NEO_3: (static_cast<B_32_R7_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_R0_NEO_4: (static_cast<B_32_R0_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_R1_NEO_4: (static_cast<B_32_R1_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_R2_NEO_4: (static_cast<B_32_R2_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_R3_NEO_4: (static_cast<B_32_R3_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_R4_NEO_4: (static_cast<B_32_R4_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_R5_NEO_4: (static_cast<B_32_R5_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_R6_NEO_4: (static_cast<B_32_R6_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_R7_NEO_4: (static_cast<B_32_R7_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_R0_400_3: (static_cast<B_32_R0_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_R1_400_3: (static_cast<B_32_R1_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_R2_400_3: (static_cast<B_32_R2_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_R3_400_3: (static_cast<B_32_R3_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_R4_400_3: (static_cast<B_32_R4_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_R5_400_3: (static_cast<B_32_R5_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_R6_400_3: (static_cast<B_32_R6_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_R7_400_3: (static_cast<B_32_R7_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_R0_TM1_4: (static_cast<B_32_R0_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_R1_TM1_4: (static_cast<B_32_R1_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_R2_TM1_4: (static_cast<B_32_R2_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_R3_TM1_4: (static_cast<B_32_R3_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_R4_TM1_4: (static_cast<B_32_R4_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_R5_TM1_4: (static_cast<B_32_R5_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_R6_TM1_4: (static_cast<B_32_R6_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_R7_TM1_4: (static_cast<B_32_R7_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
#endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
}
};
static void setBrightness(void* busPtr, uint8_t busType, uint8_t b) {
switch (busType) {
case I_NONE: break;
#ifdef ESP8266
case I_8266_U0_NEO_3: (static_cast<B_8266_U0_NEO_3*>(busPtr))->SetBrightness(b); break;
case I_8266_U1_NEO_3: (static_cast<B_8266_U1_NEO_3*>(busPtr))->SetBrightness(b); break;
case I_8266_DM_NEO_3: (static_cast<B_8266_DM_NEO_3*>(busPtr))->SetBrightness(b); break;
case I_8266_BB_NEO_3: (static_cast<B_8266_BB_NEO_3*>(busPtr))->SetBrightness(b); break;
case I_8266_U0_NEO_4: (static_cast<B_8266_U0_NEO_4*>(busPtr))->SetBrightness(b); break;
case I_8266_U1_NEO_4: (static_cast<B_8266_U1_NEO_4*>(busPtr))->SetBrightness(b); break;
case I_8266_DM_NEO_4: (static_cast<B_8266_DM_NEO_4*>(busPtr))->SetBrightness(b); break;
case I_8266_BB_NEO_4: (static_cast<B_8266_BB_NEO_4*>(busPtr))->SetBrightness(b); break;
case I_8266_U0_400_3: (static_cast<B_8266_U0_400_3*>(busPtr))->SetBrightness(b); break;
case I_8266_U1_400_3: (static_cast<B_8266_U1_400_3*>(busPtr))->SetBrightness(b); break;
case I_8266_DM_400_3: (static_cast<B_8266_DM_400_3*>(busPtr))->SetBrightness(b); break;
case I_8266_BB_400_3: (static_cast<B_8266_BB_400_3*>(busPtr))->SetBrightness(b); break;
case I_8266_U0_TM1_4: (static_cast<B_8266_U0_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetBrightness(b); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_R0_NEO_3: (static_cast<B_32_R0_NEO_3*>(busPtr))->SetBrightness(b); break;
case I_32_R1_NEO_3: (static_cast<B_32_R1_NEO_3*>(busPtr))->SetBrightness(b); break;
case I_32_R2_NEO_3: (static_cast<B_32_R2_NEO_3*>(busPtr))->SetBrightness(b); break;
case I_32_R3_NEO_3: (static_cast<B_32_R3_NEO_3*>(busPtr))->SetBrightness(b); break;
case I_32_R4_NEO_3: (static_cast<B_32_R4_NEO_3*>(busPtr))->SetBrightness(b); break;
case I_32_R5_NEO_3: (static_cast<B_32_R5_NEO_3*>(busPtr))->SetBrightness(b); break;
case I_32_R6_NEO_3: (static_cast<B_32_R6_NEO_3*>(busPtr))->SetBrightness(b); break;
case I_32_R7_NEO_3: (static_cast<B_32_R7_NEO_3*>(busPtr))->SetBrightness(b); break;
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->SetBrightness(b); break;
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->SetBrightness(b); break;
case I_32_R0_NEO_4: (static_cast<B_32_R0_NEO_4*>(busPtr))->SetBrightness(b); break;
case I_32_R1_NEO_4: (static_cast<B_32_R1_NEO_4*>(busPtr))->SetBrightness(b); break;
case I_32_R2_NEO_4: (static_cast<B_32_R2_NEO_4*>(busPtr))->SetBrightness(b); break;
case I_32_R3_NEO_4: (static_cast<B_32_R3_NEO_4*>(busPtr))->SetBrightness(b); break;
case I_32_R4_NEO_4: (static_cast<B_32_R4_NEO_4*>(busPtr))->SetBrightness(b); break;
case I_32_R5_NEO_4: (static_cast<B_32_R5_NEO_4*>(busPtr))->SetBrightness(b); break;
case I_32_R6_NEO_4: (static_cast<B_32_R6_NEO_4*>(busPtr))->SetBrightness(b); break;
case I_32_R7_NEO_4: (static_cast<B_32_R7_NEO_4*>(busPtr))->SetBrightness(b); break;
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetBrightness(b); break;
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetBrightness(b); break;
case I_32_R0_400_3: (static_cast<B_32_R0_400_3*>(busPtr))->SetBrightness(b); break;
case I_32_R1_400_3: (static_cast<B_32_R1_400_3*>(busPtr))->SetBrightness(b); break;
case I_32_R2_400_3: (static_cast<B_32_R2_400_3*>(busPtr))->SetBrightness(b); break;
case I_32_R3_400_3: (static_cast<B_32_R3_400_3*>(busPtr))->SetBrightness(b); break;
case I_32_R4_400_3: (static_cast<B_32_R4_400_3*>(busPtr))->SetBrightness(b); break;
case I_32_R5_400_3: (static_cast<B_32_R5_400_3*>(busPtr))->SetBrightness(b); break;
case I_32_R6_400_3: (static_cast<B_32_R6_400_3*>(busPtr))->SetBrightness(b); break;
case I_32_R7_400_3: (static_cast<B_32_R7_400_3*>(busPtr))->SetBrightness(b); break;
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetBrightness(b); break;
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetBrightness(b); break;
case I_32_R0_TM1_4: (static_cast<B_32_R0_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_32_R1_TM1_4: (static_cast<B_32_R1_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_32_R2_TM1_4: (static_cast<B_32_R2_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_32_R3_TM1_4: (static_cast<B_32_R3_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_32_R4_TM1_4: (static_cast<B_32_R4_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_32_R5_TM1_4: (static_cast<B_32_R5_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_32_R6_TM1_4: (static_cast<B_32_R6_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_32_R7_TM1_4: (static_cast<B_32_R7_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->SetBrightness(b); break;
#endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetBrightness(b); break;
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetBrightness(b); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->SetBrightness(b); break;
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->SetBrightness(b); break;
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->SetBrightness(b); break;
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->SetBrightness(b); break;
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetBrightness(b); break;
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetBrightness(b); break;
}
};
static uint32_t getPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint8_t co) {
RgbwColor col(0,0,0,0);
switch (busType) {
case I_NONE: break;
#ifdef ESP8266
case I_8266_U0_NEO_3: col = (static_cast<B_8266_U0_NEO_3*>(busPtr))->GetPixelColor(pix); break;
case I_8266_U1_NEO_3: col = (static_cast<B_8266_U1_NEO_3*>(busPtr))->GetPixelColor(pix); break;
case I_8266_DM_NEO_3: col = (static_cast<B_8266_DM_NEO_3*>(busPtr))->GetPixelColor(pix); break;
case I_8266_BB_NEO_3: col = (static_cast<B_8266_BB_NEO_3*>(busPtr))->GetPixelColor(pix); break;
case I_8266_U0_NEO_4: col = (static_cast<B_8266_U0_NEO_4*>(busPtr))->GetPixelColor(pix); break;
case I_8266_U1_NEO_4: col = (static_cast<B_8266_U1_NEO_4*>(busPtr))->GetPixelColor(pix); break;
case I_8266_DM_NEO_4: col = (static_cast<B_8266_DM_NEO_4*>(busPtr))->GetPixelColor(pix); break;
case I_8266_BB_NEO_4: col = (static_cast<B_8266_BB_NEO_4*>(busPtr))->GetPixelColor(pix); break;
case I_8266_U0_400_3: col = (static_cast<B_8266_U0_400_3*>(busPtr))->GetPixelColor(pix); break;
case I_8266_U1_400_3: col = (static_cast<B_8266_U1_400_3*>(busPtr))->GetPixelColor(pix); break;
case I_8266_DM_400_3: col = (static_cast<B_8266_DM_400_3*>(busPtr))->GetPixelColor(pix); break;
case I_8266_BB_400_3: col = (static_cast<B_8266_BB_400_3*>(busPtr))->GetPixelColor(pix); break;
case I_8266_U0_TM1_4: col = (static_cast<B_8266_U0_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_8266_U1_TM1_4: col = (static_cast<B_8266_U1_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_8266_DM_TM1_4: col = (static_cast<B_8266_DM_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_8266_BB_TM1_4: col = (static_cast<B_8266_BB_TM1_4*>(busPtr))->GetPixelColor(pix); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_R0_NEO_3: col = (static_cast<B_32_R0_NEO_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_R1_NEO_3: col = (static_cast<B_32_R1_NEO_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_R2_NEO_3: col = (static_cast<B_32_R2_NEO_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_R3_NEO_3: col = (static_cast<B_32_R3_NEO_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_R4_NEO_3: col = (static_cast<B_32_R4_NEO_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_R5_NEO_3: col = (static_cast<B_32_R5_NEO_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_R6_NEO_3: col = (static_cast<B_32_R6_NEO_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_R7_NEO_3: col = (static_cast<B_32_R7_NEO_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_I0_NEO_3: col = (static_cast<B_32_I0_NEO_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_I1_NEO_3: col = (static_cast<B_32_I1_NEO_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_R0_NEO_4: col = (static_cast<B_32_R0_NEO_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_R1_NEO_4: col = (static_cast<B_32_R1_NEO_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_R2_NEO_4: col = (static_cast<B_32_R2_NEO_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_R3_NEO_4: col = (static_cast<B_32_R3_NEO_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_R4_NEO_4: col = (static_cast<B_32_R4_NEO_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_R5_NEO_4: col = (static_cast<B_32_R5_NEO_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_R6_NEO_4: col = (static_cast<B_32_R6_NEO_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_R7_NEO_4: col = (static_cast<B_32_R7_NEO_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_I0_NEO_4: col = (static_cast<B_32_I0_NEO_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_I1_NEO_4: col = (static_cast<B_32_I1_NEO_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_R0_400_3: col = (static_cast<B_32_R0_400_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_R1_400_3: col = (static_cast<B_32_R1_400_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_R2_400_3: col = (static_cast<B_32_R2_400_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_R3_400_3: col = (static_cast<B_32_R3_400_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_R4_400_3: col = (static_cast<B_32_R4_400_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_R5_400_3: col = (static_cast<B_32_R5_400_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_R6_400_3: col = (static_cast<B_32_R6_400_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_R7_400_3: col = (static_cast<B_32_R7_400_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_I0_400_3: col = (static_cast<B_32_I0_400_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_I1_400_3: col = (static_cast<B_32_I1_400_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_R0_TM1_4: col = (static_cast<B_32_R0_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_R1_TM1_4: col = (static_cast<B_32_R1_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_R2_TM1_4: col = (static_cast<B_32_R2_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_R3_TM1_4: col = (static_cast<B_32_R3_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_R4_TM1_4: col = (static_cast<B_32_R4_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_R5_TM1_4: col = (static_cast<B_32_R5_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_R6_TM1_4: col = (static_cast<B_32_R6_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_R7_TM1_4: col = (static_cast<B_32_R7_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_I0_TM1_4: col = (static_cast<B_32_I0_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_I1_TM1_4: col = (static_cast<B_32_I1_TM1_4*>(busPtr))->GetPixelColor(pix); break;
#endif
case I_HS_DOT_3: col = (static_cast<B_HS_DOT_3*>(busPtr))->GetPixelColor(pix); break;
case I_SS_DOT_3: col = (static_cast<B_SS_DOT_3*>(busPtr))->GetPixelColor(pix); break;
case I_HS_LPD_3: col = (static_cast<B_HS_LPD_3*>(busPtr))->GetPixelColor(pix); break;
case I_SS_LPD_3: col = (static_cast<B_SS_LPD_3*>(busPtr))->GetPixelColor(pix); break;
case I_HS_WS1_3: col = (static_cast<B_HS_WS1_3*>(busPtr))->GetPixelColor(pix); break;
case I_SS_WS1_3: col = (static_cast<B_SS_WS1_3*>(busPtr))->GetPixelColor(pix); break;
case I_HS_P98_3: col = (static_cast<B_HS_P98_3*>(busPtr))->GetPixelColor(pix); break;
case I_SS_P98_3: col = (static_cast<B_SS_P98_3*>(busPtr))->GetPixelColor(pix); break;
}
#ifdef COLOR_ORDER_OVERRIDE
if (pix >= COO_MIN && pix < COO_MAX) co = COO_ORDER;
#endif
switch (co)
{
// W G R B
case 0: return ((col.W << 24) | (col.G << 8) | (col.R << 16) | (col.B)); //0 = GRB, default
case 1: return ((col.W << 24) | (col.R << 8) | (col.G << 16) | (col.B)); //1 = RGB, common for WS2811
case 2: return ((col.W << 24) | (col.B << 8) | (col.R << 16) | (col.G)); //2 = BRG
case 3: return ((col.W << 24) | (col.B << 8) | (col.G << 16) | (col.R)); //3 = RBG
case 4: return ((col.W << 24) | (col.R << 8) | (col.B << 16) | (col.G)); //4 = BGR
case 5: return ((col.W << 24) | (col.G << 8) | (col.B << 16) | (col.R)); //5 = GBR
}
return 0;
}
static void cleanup(void* busPtr, uint8_t busType) {
if (busPtr == nullptr) return;
switch (busType) {
case I_NONE: break;
#ifdef ESP8266
case I_8266_U0_NEO_3: delete (static_cast<B_8266_U0_NEO_3*>(busPtr)); break;
case I_8266_U1_NEO_3: delete (static_cast<B_8266_U1_NEO_3*>(busPtr)); break;
case I_8266_DM_NEO_3: delete (static_cast<B_8266_DM_NEO_3*>(busPtr)); break;
case I_8266_BB_NEO_3: delete (static_cast<B_8266_BB_NEO_3*>(busPtr)); break;
case I_8266_U0_NEO_4: delete (static_cast<B_8266_U0_NEO_4*>(busPtr)); break;
case I_8266_U1_NEO_4: delete (static_cast<B_8266_U1_NEO_4*>(busPtr)); break;
case I_8266_DM_NEO_4: delete (static_cast<B_8266_DM_NEO_4*>(busPtr)); break;
case I_8266_BB_NEO_4: delete (static_cast<B_8266_BB_NEO_4*>(busPtr)); break;
case I_8266_U0_400_3: delete (static_cast<B_8266_U0_400_3*>(busPtr)); break;
case I_8266_U1_400_3: delete (static_cast<B_8266_U1_400_3*>(busPtr)); break;
case I_8266_DM_400_3: delete (static_cast<B_8266_DM_400_3*>(busPtr)); break;
case I_8266_BB_400_3: delete (static_cast<B_8266_BB_400_3*>(busPtr)); break;
case I_8266_U0_TM1_4: delete (static_cast<B_8266_U0_TM1_4*>(busPtr)); break;
case I_8266_U1_TM1_4: delete (static_cast<B_8266_U1_TM1_4*>(busPtr)); break;
case I_8266_DM_TM1_4: delete (static_cast<B_8266_DM_TM1_4*>(busPtr)); break;
case I_8266_BB_TM1_4: delete (static_cast<B_8266_BB_TM1_4*>(busPtr)); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_R0_NEO_3: delete (static_cast<B_32_R0_NEO_3*>(busPtr)); break;
case I_32_R1_NEO_3: delete (static_cast<B_32_R1_NEO_3*>(busPtr)); break;
case I_32_R2_NEO_3: delete (static_cast<B_32_R2_NEO_3*>(busPtr)); break;
case I_32_R3_NEO_3: delete (static_cast<B_32_R3_NEO_3*>(busPtr)); break;
case I_32_R4_NEO_3: delete (static_cast<B_32_R4_NEO_3*>(busPtr)); break;
case I_32_R5_NEO_3: delete (static_cast<B_32_R5_NEO_3*>(busPtr)); break;
case I_32_R6_NEO_3: delete (static_cast<B_32_R6_NEO_3*>(busPtr)); break;
case I_32_R7_NEO_3: delete (static_cast<B_32_R7_NEO_3*>(busPtr)); break;
case I_32_I0_NEO_3: delete (static_cast<B_32_I0_NEO_3*>(busPtr)); break;
case I_32_I1_NEO_3: delete (static_cast<B_32_I1_NEO_3*>(busPtr)); break;
case I_32_R0_NEO_4: delete (static_cast<B_32_R0_NEO_4*>(busPtr)); break;
case I_32_R1_NEO_4: delete (static_cast<B_32_R1_NEO_4*>(busPtr)); break;
case I_32_R2_NEO_4: delete (static_cast<B_32_R2_NEO_4*>(busPtr)); break;
case I_32_R3_NEO_4: delete (static_cast<B_32_R3_NEO_4*>(busPtr)); break;
case I_32_R4_NEO_4: delete (static_cast<B_32_R4_NEO_4*>(busPtr)); break;
case I_32_R5_NEO_4: delete (static_cast<B_32_R5_NEO_4*>(busPtr)); break;
case I_32_R6_NEO_4: delete (static_cast<B_32_R6_NEO_4*>(busPtr)); break;
case I_32_R7_NEO_4: delete (static_cast<B_32_R7_NEO_4*>(busPtr)); break;
case I_32_I0_NEO_4: delete (static_cast<B_32_I0_NEO_4*>(busPtr)); break;
case I_32_I1_NEO_4: delete (static_cast<B_32_I1_NEO_4*>(busPtr)); break;
case I_32_R0_400_3: delete (static_cast<B_32_R0_400_3*>(busPtr)); break;
case I_32_R1_400_3: delete (static_cast<B_32_R1_400_3*>(busPtr)); break;
case I_32_R2_400_3: delete (static_cast<B_32_R2_400_3*>(busPtr)); break;
case I_32_R3_400_3: delete (static_cast<B_32_R3_400_3*>(busPtr)); break;
case I_32_R4_400_3: delete (static_cast<B_32_R4_400_3*>(busPtr)); break;
case I_32_R5_400_3: delete (static_cast<B_32_R5_400_3*>(busPtr)); break;
case I_32_R6_400_3: delete (static_cast<B_32_R6_400_3*>(busPtr)); break;
case I_32_R7_400_3: delete (static_cast<B_32_R7_400_3*>(busPtr)); break;
case I_32_I0_400_3: delete (static_cast<B_32_I0_400_3*>(busPtr)); break;
case I_32_I1_400_3: delete (static_cast<B_32_I1_400_3*>(busPtr)); break;
case I_32_R0_TM1_4: delete (static_cast<B_32_R0_TM1_4*>(busPtr)); break;
case I_32_R1_TM1_4: delete (static_cast<B_32_R1_TM1_4*>(busPtr)); break;
case I_32_R2_TM1_4: delete (static_cast<B_32_R2_TM1_4*>(busPtr)); break;
case I_32_R3_TM1_4: delete (static_cast<B_32_R3_TM1_4*>(busPtr)); break;
case I_32_R4_TM1_4: delete (static_cast<B_32_R4_TM1_4*>(busPtr)); break;
case I_32_R5_TM1_4: delete (static_cast<B_32_R5_TM1_4*>(busPtr)); break;
case I_32_R6_TM1_4: delete (static_cast<B_32_R6_TM1_4*>(busPtr)); break;
case I_32_R7_TM1_4: delete (static_cast<B_32_R7_TM1_4*>(busPtr)); break;
case I_32_I0_TM1_4: delete (static_cast<B_32_I0_TM1_4*>(busPtr)); break;
case I_32_I1_TM1_4: delete (static_cast<B_32_I1_TM1_4*>(busPtr)); break;
#endif
case I_HS_DOT_3: delete (static_cast<B_HS_DOT_3*>(busPtr)); break;
case I_SS_DOT_3: delete (static_cast<B_SS_DOT_3*>(busPtr)); break;
case I_HS_LPD_3: delete (static_cast<B_HS_LPD_3*>(busPtr)); break;
case I_SS_LPD_3: delete (static_cast<B_SS_LPD_3*>(busPtr)); break;
case I_HS_WS1_3: delete (static_cast<B_HS_WS1_3*>(busPtr)); break;
case I_SS_WS1_3: delete (static_cast<B_SS_WS1_3*>(busPtr)); break;
case I_HS_P98_3: delete (static_cast<B_HS_P98_3*>(busPtr)); break;
case I_SS_P98_3: delete (static_cast<B_SS_P98_3*>(busPtr)); break;
}
}
//gives back the internal type index (I_XX_XXX_X above) for the input
static uint8_t getI(uint8_t busType, uint8_t* pins, uint8_t num = 0) {
if (!IS_DIGITAL(busType)) return I_NONE;
if (IS_2PIN(busType)) { //SPI LED chips
bool isHSPI = false;
#ifdef ESP8266
if (pins[0] == P_8266_HS_MOSI && pins[1] == P_8266_HS_CLK) isHSPI = true;
#else
if(!num) isHSPI = true; // temporary hack to limit use of hardware SPI to a single SPI peripheral: only allow ESP32 hardware serial on segment 0
#endif
uint8_t t = I_NONE;
switch (busType) {
case TYPE_APA102: t = I_SS_DOT_3; break;
case TYPE_LPD8806: t = I_SS_LPD_3; break;
case TYPE_WS2801: t = I_SS_WS1_3; break;
case TYPE_P9813: t = I_SS_P98_3; break;
default: t=I_NONE;
}
if (t > I_NONE && isHSPI) t--; //hardware SPI has one smaller ID than software
return t;
} else {
#ifdef ESP8266
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_RGB:
case TYPE_WS2812_WWA:
return I_8266_U0_NEO_3 + offset;
case TYPE_SK6812_RGBW:
return I_8266_U0_NEO_4 + offset;
case TYPE_WS2811_400KHZ:
return I_8266_U0_400_3 + offset;
case TYPE_TM1814:
return I_8266_U0_TM1_4 + offset;
}
#else //ESP32
uint8_t offset = num; //RMT bus # == bus index in BusManager
if (offset > 9) return I_NONE;
switch (busType) {
case TYPE_WS2812_RGB:
case TYPE_WS2812_WWA:
return I_32_R0_NEO_3 + offset;
case TYPE_SK6812_RGBW:
return I_32_R0_NEO_4 + offset;
case TYPE_WS2811_400KHZ:
return I_32_R0_400_3 + offset;
case TYPE_TM1814:
return I_32_R0_TM1_4 + offset;
}
#endif
}
return I_NONE;
}
};
#endif

View File

@@ -4,78 +4,213 @@
* Physical IO
*/
void shortPressAction()
#define WLED_DEBOUNCE_THRESHOLD 50 //only consider button input of at least 50ms as valid (debouncing)
void shortPressAction(uint8_t b)
{
if (!macroButton)
if (!macroButton[b])
{
toggleOnOff();
colorUpdated(NOTIFIER_CALL_MODE_BUTTON);
} else {
applyPreset(macroButton);
applyPreset(macroButton[b]);
}
}
bool isButtonPressed()
bool isButtonPressed(uint8_t i)
{
#if defined(BTNPIN) && BTNPIN > -1
if (digitalRead(BTNPIN) == LOW) return true;
#endif
#ifdef TOUCHPIN
if (touchRead(TOUCHPIN) <= TOUCH_THRESHOLD) return true;
#endif
if (btnPin[i]<0) return false;
switch (buttonType[i]) {
case BTN_TYPE_NONE:
case BTN_TYPE_RESERVED:
break;
case BTN_TYPE_PUSH:
case BTN_TYPE_SWITCH:
if (digitalRead(btnPin[i]) == LOW) return true;
break;
case BTN_TYPE_PUSH_ACT_HIGH:
case BTN_TYPE_SWITCH_ACT_HIGH:
if (digitalRead(btnPin[i]) == HIGH) return true;
break;
case BTN_TYPE_TOUCH:
#ifdef ARDUINO_ARCH_ESP32
if (touchRead(btnPin[i]) <= touchThreshold) return true;
#endif
break;
}
return false;
}
void handleSwitch(uint8_t b)
{
if (buttonPressedBefore[b] != isButtonPressed(b)) {
buttonPressedTime[b] = millis();
buttonPressedBefore[b] = !buttonPressedBefore[b];
}
if (buttonLongPressed[b] == buttonPressedBefore[b]) return;
if (millis() - buttonPressedTime[b] > WLED_DEBOUNCE_THRESHOLD) { //fire edge event only after 50ms without change (debounce)
if (buttonPressedBefore[b]) { //LOW, falling edge, switch closed
if (macroButton[b]) applyPreset(macroButton[b]);
else { //turn on
if (!bri) {toggleOnOff(); colorUpdated(NOTIFIER_CALL_MODE_BUTTON);}
}
} else { //HIGH, rising edge, switch opened
if (macroLongPress[b]) applyPreset(macroLongPress[b]);
else { //turn off
if (bri) {toggleOnOff(); colorUpdated(NOTIFIER_CALL_MODE_BUTTON);}
}
}
buttonLongPressed[b] = buttonPressedBefore[b]; //save the last "long term" switch state
}
}
void handleAnalog(uint8_t b)
{
static uint8_t oldRead[WLED_MAX_BUTTONS];
#ifdef ESP8266
uint16_t aRead = analogRead(A0) >> 2; // convert 10bit read to 8bit
#else
uint16_t aRead = analogRead(btnPin[b]) >> 4; // convert 12bit read to 8bit
#endif
// remove noise & reduce frequency of UI updates
aRead &= 0xFC;
if (oldRead[b] == aRead) return; // no change in reading
oldRead[b] = aRead;
// if no macro for "short press" and "long press" is defined use brightness control
if (!macroButton[b] && !macroLongPress[b]) {
// if "double press" macro defines which option to change
if (macroDoublePress[b] >= 250) {
// global brightness
if (aRead == 0) {
briLast = bri;
bri = 0;
} else{
bri = aRead;
}
} else if (macroDoublePress[b] == 249) {
// effect speed
effectSpeed = aRead;
effectChanged = true;
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isSelected()) continue;
seg.speed = effectSpeed;
}
} else if (macroDoublePress[b] == 248) {
// effect intensity
effectIntensity = aRead;
effectChanged = true;
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isSelected()) continue;
seg.intensity = effectIntensity;
}
} else if (macroDoublePress[b] == 247) {
// selected palette
effectPalette = map(aRead, 0, 252, 0, strip.getPaletteCount()-1);
effectChanged = true;
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isSelected()) continue;
seg.palette = effectPalette;
}
} else if (macroDoublePress[b] == 200) {
// primary color, hue, full saturation
colorHStoRGB(aRead*256,255,col);
} else {
// otherwise use "double press" for segment selection
//uint8_t mainSeg = strip.getMainSegmentId();
WS2812FX::Segment& seg = strip.getSegment(macroDoublePress[b]);
if (aRead == 0) {
seg.setOption(SEG_OPTION_ON, 0); // off
} else {
seg.setOpacity(aRead, macroDoublePress[b]);
seg.setOption(SEG_OPTION_ON, 1);
}
// this will notify clients of update (websockets,mqtt,etc)
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification)
// 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa
updateInterfaces(NOTIFIER_CALL_MODE_BUTTON);
}
} else {
//TODO:
// we can either trigger a preset depending on the level (between short and long entries)
// or use it for RGBW direct control
}
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification)
// 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa
colorUpdated(NOTIFIER_CALL_MODE_BUTTON);
}
void handleButton()
{
#if (defined(BTNPIN) && BTNPIN > -1) || defined(TOUCHPIN)
if (!buttonEnabled) return;
static unsigned long lastRead = 0UL;
if (isButtonPressed()) //pressed
{
if (!buttonPressedBefore) buttonPressedTime = millis();
buttonPressedBefore = true;
for (uint8_t b=0; b<WLED_MAX_BUTTONS; b++) {
#ifdef ESP8266
if ((btnPin[b]<0 && buttonType[b] != BTN_TYPE_ANALOG) || buttonType[b] == BTN_TYPE_NONE) continue;
#else
if (btnPin[b]<0 || buttonType[b] == BTN_TYPE_NONE) continue;
#endif
if (millis() - buttonPressedTime > 600) //long press
if (buttonType[b] == BTN_TYPE_ANALOG && millis() - lastRead > 250) { // button is not a button but a potentiometer
if (b+1 == WLED_MAX_BUTTONS) lastRead = millis();
handleAnalog(b); continue;
}
if (buttonType[b] == BTN_TYPE_SWITCH || buttonType[b] == BTN_TYPE_SWITCH_ACT_HIGH) { //button is not momentary, but switch. This is only suitable on pins whose on-boot state does not matter (NOT gpio0)
handleSwitch(b); continue;
}
//momentary button logic
if (isButtonPressed(b)) //pressed
{
if (!buttonLongPressed)
{
if (macroLongPress) {applyPreset(macroLongPress);}
else _setRandomColor(false,true);
if (!buttonPressedBefore[b]) buttonPressedTime[b] = millis();
buttonPressedBefore[b] = true;
buttonLongPressed = true;
if (millis() - buttonPressedTime[b] > 600) //long press
{
if (!buttonLongPressed[b])
{
if (macroLongPress[b]) {applyPreset(macroLongPress[b]);}
else _setRandomColor(false,true);
buttonLongPressed[b] = true;
}
}
}
}
else if (!isButtonPressed() && buttonPressedBefore) //released
{
long dur = millis() - buttonPressedTime;
if (dur < 50) {buttonPressedBefore = false; return;} //too short "press", debounce
bool doublePress = buttonWaitTime;
buttonWaitTime = 0;
if (dur > 6000) //long press
else if (!isButtonPressed(b) && buttonPressedBefore[b]) //released
{
WLED::instance().initAP(true);
}
else if (!buttonLongPressed) { //short press
if (macroDoublePress)
{
if (doublePress) applyPreset(macroDoublePress);
else buttonWaitTime = millis();
} else shortPressAction();
}
buttonPressedBefore = false;
buttonLongPressed = false;
}
long dur = millis() - buttonPressedTime[b];
if (dur < WLED_DEBOUNCE_THRESHOLD) {buttonPressedBefore[b] = false; continue;} //too short "press", debounce
bool doublePress = buttonWaitTime[b];
buttonWaitTime[b] = 0;
if (buttonWaitTime && millis() - buttonWaitTime > 450 && !buttonPressedBefore)
{
buttonWaitTime = 0;
shortPressAction();
if (dur > 6000 && b==0) //long press on button 0
{
WLED::instance().initAP(true);
}
else if (!buttonLongPressed[b]) { //short press
if (macroDoublePress[b])
{
if (doublePress) applyPreset(macroDoublePress[b]);
else buttonWaitTime[b] = millis();
} else shortPressAction(b);
}
buttonPressedBefore[b] = false;
buttonLongPressed[b] = false;
}
if (buttonWaitTime[b] && millis() - buttonWaitTime[b] > 450 && !buttonPressedBefore[b])
{
buttonWaitTime[b] = 0;
shortPressAction(b);
}
}
#endif
}
void handleIO()
@@ -88,51 +223,26 @@ void handleIO()
lastOnTime = millis();
if (offMode)
{
#if RLYPIN >= 0
digitalWrite(RLYPIN, RLYMDE);
#endif
if (rlyPin>=0) {
pinMode(rlyPin, OUTPUT);
digitalWrite(rlyPin, rlyMde);
}
offMode = false;
}
} else if (millis() - lastOnTime > 600)
{
if (!offMode) {
#if LEDPIN == LED_BUILTIN
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
if (!offMode) {
#ifdef ESP8266
// turn off built-in LED if strip is turned off
// this will break digital bus so will need to be reinitialised on On
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
#endif
#if RLYPIN >= 0
digitalWrite(RLYPIN, !RLYMDE);
#endif
}
if (rlyPin>=0) {
pinMode(rlyPin, OUTPUT);
digitalWrite(rlyPin, !rlyMde);
}
}
offMode = true;
}
#if AUXPIN >= 0
//output
if (auxActive || auxActiveBefore)
{
if (!auxActiveBefore)
{
auxActiveBefore = true;
switch (auxTriggeredState)
{
case 0: pinMode(AUXPIN, INPUT); break;
case 1: pinMode(AUXPIN, OUTPUT); digitalWrite(AUXPIN, HIGH); break;
case 2: pinMode(AUXPIN, OUTPUT); digitalWrite(AUXPIN, LOW); break;
}
auxStartTime = millis();
}
if ((millis() - auxStartTime > auxTime*1000 && auxTime != 255) || !auxActive)
{
auxActive = false;
auxActiveBefore = false;
switch (auxDefaultState)
{
case 0: pinMode(AUXPIN, INPUT); break;
case 1: pinMode(AUXPIN, OUTPUT); digitalWrite(AUXPIN, HIGH); break;
case 2: pinMode(AUXPIN, OUTPUT); digitalWrite(AUXPIN, LOW); break;
}
}
}
#endif
}

View File

@@ -12,46 +12,27 @@ void getStringFromJson(char* dest, const char* src, size_t len) {
if (src != nullptr) strlcpy(dest, src, len);
}
void deserializeConfig() {
bool fromeep = false;
bool success = deserializeConfigSec();
if (!success) { //if file does not exist, try reading from EEPROM
deEEPSettings();
fromeep = true;
}
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
DEBUG_PRINTLN(F("Reading settings from /cfg.json..."));
success = readObjectFromFile("/cfg.json", nullptr, &doc);
if (!success) { //if file does not exist, try reading from EEPROM
if (!fromeep) deEEPSettings();
return;
}
//deserializeJson(doc, json);
//int rev_major = doc[F("rev")][0]; // 1
//int rev_minor = doc[F("rev")][1]; // 0
bool deserializeConfig(JsonObject doc, bool fromFS) {
//int rev_major = doc["rev"][0]; // 1
//int rev_minor = doc["rev"][1]; // 0
//long vid = doc[F("vid")]; // 2010020
JsonObject id = doc[F("id")];
JsonObject id = doc["id"];
getStringFromJson(cmDNS, id[F("mdns")], 33);
getStringFromJson(serverDescription, id[F("name")], 33);
getStringFromJson(alexaInvocationName, id[F("inv")], 33);
JsonObject nw_ins_0 = doc["nw"][F("ins")][0];
JsonObject nw_ins_0 = doc["nw"]["ins"][0];
getStringFromJson(clientSSID, nw_ins_0[F("ssid")], 33);
//int nw_ins_0_pskl = nw_ins_0[F("pskl")];
//The WiFi PSK is normally not contained in the regular file for security reasons.
//If it is present however, we will use it
getStringFromJson(clientPass, nw_ins_0["psk"], 65);
JsonArray nw_ins_0_ip = nw_ins_0[F("ip")];
JsonArray nw_ins_0_gw = nw_ins_0[F("gw")];
JsonArray nw_ins_0_sn = nw_ins_0[F("sn")];
JsonArray nw_ins_0_ip = nw_ins_0["ip"];
JsonArray nw_ins_0_gw = nw_ins_0["gw"];
JsonArray nw_ins_0_sn = nw_ins_0["sn"];
for (byte i = 0; i < 4; i++) {
CJSON(staticIP[i], nw_ins_0_ip[i]);
@@ -59,7 +40,7 @@ void deserializeConfig() {
CJSON(staticSubnet[i], nw_ins_0_sn[i]);
}
JsonObject ap = doc[F("ap")];
JsonObject ap = doc["ap"];
getStringFromJson(apSSID, ap[F("ssid")], 33);
getStringFromJson(apPass, ap["psk"] , 65); //normally not present due to security
//int ap_pskl = ap[F("pskl")];
@@ -71,12 +52,18 @@ void deserializeConfig() {
if (apHide > 1) apHide = 1;
CJSON(apBehavior, ap[F("behav")]);
#ifdef WLED_USE_ETHERNET
JsonObject ethernet = doc[F("eth")];
CJSON(ethernetType, ethernet["type"]);
#endif
/*
JsonArray ap_ip = ap[F("ip")];
JsonArray ap_ip = ap["ip"];
for (byte i = 0; i < 4; i++) {
apIP[i] = ap_ip;
}*/
}
*/
noWifiSleep = doc[F("wifi")][F("sleep")] | !noWifiSleep; // inverted
noWifiSleep = !noWifiSleep;
@@ -84,53 +71,130 @@ void deserializeConfig() {
JsonObject hw = doc[F("hw")];
// initialize LED pins and lengths prior to other HW
JsonObject hw_led = hw[F("led")];
CJSON(ledCount, hw_led[F("total")]);
if (ledCount > MAX_LEDS) ledCount = MAX_LEDS;
CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]);
CJSON(strip.milliampsPerLed, hw_led[F("ledma")]);
CJSON(strip.reverseMode, hw_led[F("rev")]);
CJSON(strip.rgbwMode, hw_led[F("rgbwm")]);
JsonObject hw_led_ins_0 = hw_led[F("ins")][0];
//bool hw_led_ins_0_en = hw_led_ins_0[F("en")]; // true
//int hw_led_ins_0_start = hw_led_ins_0[F("start")]; // 0
//int hw_led_ins_0_len = hw_led_ins_0[F("len")]; // 1200
JsonArray ins = hw_led["ins"];
if (fromFS || !ins.isNull()) {
uint8_t s = 0; //bus iterator
strip.isRgbw = false;
busses.removeAll();
uint32_t mem = 0;
for (JsonObject elm : ins) {
if (s >= WLED_MAX_BUSSES) break;
uint8_t pins[5] = {255, 255, 255, 255, 255};
JsonArray pinArr = elm["pin"];
if (pinArr.size() == 0) continue;
pins[0] = pinArr[0];
uint8_t i = 0;
for (int p : pinArr) {
pins[i++] = p;
if (i>4) break;
}
//int hw_led_ins_0_pin_0 = hw_led_ins_0[F("pin")][0]; // 2
uint16_t length = elm[F("len")];
if (length==0) continue;
uint8_t colorOrder = (int)elm[F("order")];
//only use skip from the first strip (this shouldn't have been in ins obj. but remains here for compatibility)
uint8_t skipFirst = elm[F("skip")];
uint16_t start = elm[F("start")] | 0;
if (start >= ledCount) continue;
//limit length of strip if it would exceed total configured LEDs
if (start + length > ledCount) length = ledCount - start;
uint8_t ledType = elm["type"] | TYPE_WS2812_RGB;
bool reversed = elm["rev"];
//RGBW mode is enabled if at least one of the strips is RGBW
strip.isRgbw = (strip.isRgbw || BusManager::isRgbw(ledType));
//refresh is required to remain off if at least one of the strips requires the refresh.
strip.isOffRefreshRequred |= BusManager::isOffRefreshRequred(ledType);
s++;
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst);
mem += busses.memUsage(bc);
if (mem <= MAX_LED_MEMORY) busses.add(bc);
}
strip.finalizeInit(ledCount);
}
if (hw_led["rev"]) busses.getBus(0)->reversed = true; //set 0.11 global reversed setting for first bus
strip.setColorOrder(hw_led_ins_0[F("order")]);
//bool hw_led_ins_0_rev = hw_led_ins_0[F("rev")]; // false
skipFirstLed = hw_led_ins_0[F("skip")]; // 0
useRGBW = (hw_led_ins_0[F("type")] == TYPE_SK6812_RGBW);
// read multiple button configuration
JsonArray hw_btn_ins = hw[F("btn")][F("ins")];
if (!hw_btn_ins.isNull()) {
uint8_t s = 0;
for (JsonObject btn : hw_btn_ins) {
CJSON(buttonType[s], btn["type"]);
int8_t pin = btn["pin"][0] | -1;
if (pin > -1 && pinManager.allocatePin(pin,false)) {
btnPin[s] = pin;
pinMode(btnPin[s], INPUT_PULLUP);
} else {
btnPin[s] = -1;
}
JsonArray hw_btn_ins_0_macros = btn[F("macros")];
CJSON(macroButton[s], hw_btn_ins_0_macros[0]);
CJSON(macroLongPress[s],hw_btn_ins_0_macros[1]);
CJSON(macroDoublePress[s], hw_btn_ins_0_macros[2]);
if (++s >= WLED_MAX_BUTTONS) break; // max buttons reached
}
// clear remaining buttons
for (; s<WLED_MAX_BUTTONS; s++) {
btnPin[s] = -1;
buttonType[s] = BTN_TYPE_NONE;
macroButton[s] = 0;
macroLongPress[s] = 0;
macroDoublePress[s] = 0;
}
} else {
// new install/missing configuration (button 0 has defaults)
if (fromFS)
for (uint8_t s=1; s<WLED_MAX_BUTTONS; s++) {
btnPin[s] = -1;
buttonType[s] = BTN_TYPE_NONE;
macroButton[s] = 0;
macroLongPress[s] = 0;
macroDoublePress[s] = 0;
}
}
CJSON(touchThreshold,hw[F("btn")][F("tt")]);
JsonObject hw_btn_ins_0 = hw[F("btn")][F("ins")][0];
buttonEnabled = hw_btn_ins_0[F("en")] | buttonEnabled;
int hw_ir_pin = hw["ir"]["pin"] | -2; // 4
if (hw_ir_pin > -2) {
if (pinManager.allocatePin(hw_ir_pin,false)) {
irPin = hw_ir_pin;
} else {
irPin = -1;
}
}
CJSON(irEnabled, hw["ir"]["type"]);
//int hw_btn_ins_0_pin_0 = hw_btn_ins_0[F("pin")][0]; // 0
JsonObject relay = hw[F("relay")];
int hw_relay_pin = relay["pin"] | -2;
if (hw_relay_pin > -2) {
if (pinManager.allocatePin(hw_relay_pin,true)) {
rlyPin = hw_relay_pin;
pinMode(rlyPin, OUTPUT);
} else {
rlyPin = -1;
}
}
if (relay.containsKey("rev")) {
rlyMde = !relay["rev"];
}
JsonArray hw_btn_ins_0_macros = hw_btn_ins_0[F("macros")];
CJSON(macroButton, hw_btn_ins_0_macros[0]);
CJSON(macroLongPress,hw_btn_ins_0_macros[1]);
CJSON(macroDoublePress, hw_btn_ins_0_macros[2]);
//int hw_btn_ins_0_type = hw_btn_ins_0[F("type")]; // 0
//int hw_ir_pin = hw[F("ir")][F("pin")]; // 4
CJSON(irEnabled, hw[F("ir")][F("type")]); // 0
//int hw_relay_pin = hw[F("relay")][F("pin")]; // 12
//bool hw_relay_rev = hw[F("relay")][F("rev")]; // false
//int hw_status_pin = hw[F("status")][F("pin")]; // -1
//int hw_status_pin = hw[F("status")]["pin"]; // -1
JsonObject light = doc[F("light")];
CJSON(briMultiplier, light[F("scale-bri")]);
CJSON(strip.paletteBlend, light[F("pal-mode")]);
float light_gc_bri = light[F("gc")]["bri"];
float light_gc_col = light[F("gc")][F("col")]; // 2.8
float light_gc_col = light[F("gc")]["col"]; // 2.8
if (light_gc_bri > 1.5) strip.gammaCorrectBri = true;
else if (light_gc_bri > 0.5) strip.gammaCorrectBri = false;
if (light_gc_col > 1.5) strip.gammaCorrectCol = true;
@@ -138,14 +202,15 @@ void deserializeConfig() {
JsonObject light_tr = light[F("tr")];
CJSON(fadeTransition, light_tr[F("mode")]);
int tdd = light_tr[F("dur")] | -1;
int tdd = light_tr["dur"] | -1;
if (tdd >= 0) transitionDelayDefault = tdd * 100;
CJSON(strip.paletteFade, light_tr[F("pal")]);
CJSON(strip.paletteFade, light_tr["pal"]);
JsonObject light_nl = light["nl"];
CJSON(nightlightMode, light_nl[F("mode")]);
byte prev = nightlightDelayMinsDefault;
CJSON(nightlightDelayMinsDefault, light_nl[F("dur")]);
nightlightDelayMins = nightlightDelayMinsDefault;
if (nightlightDelayMinsDefault != prev) nightlightDelayMins = nightlightDelayMinsDefault;
CJSON(nightlightTargetBri, light_nl[F("tbri")]);
CJSON(macroNl, light_nl[F("macro")]);
@@ -154,16 +219,6 @@ void deserializeConfig() {
CJSON(bootPreset, def[F("ps")]);
CJSON(turnOnAtBoot, def["on"]); // true
CJSON(briS, def["bri"]); // 128
if (briS == 0) briS = 255;
JsonObject def_cy = def[F("cy")];
CJSON(presetCyclingEnabled, def_cy["on"]);
CJSON(presetCycleMin, def_cy[F("range")][0]);
CJSON(presetCycleMax, def_cy[F("range")][1]);
tdd = def_cy[F("dur")] | -1;
if (tdd > 0) presetCycleTime = tdd;
JsonObject interfaces = doc["if"];
@@ -171,24 +226,30 @@ void deserializeConfig() {
CJSON(udpPort, if_sync[F("port0")]); // 21324
CJSON(udpPort2, if_sync[F("port1")]); // 65506
JsonObject if_sync_recv = if_sync[F("recv")];
JsonObject if_sync_recv = if_sync["recv"];
CJSON(receiveNotificationBrightness, if_sync_recv["bri"]);
CJSON(receiveNotificationColor, if_sync_recv[F("col")]);
CJSON(receiveNotificationColor, if_sync_recv["col"]);
CJSON(receiveNotificationEffects, if_sync_recv[F("fx")]);
//! following line might be a problem if called after boot
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
JsonObject if_sync_send = if_sync[F("send")];
JsonObject if_sync_send = if_sync["send"];
prev = notifyDirectDefault;
CJSON(notifyDirectDefault, if_sync_send[F("dir")]);
notifyDirect = notifyDirectDefault;
if (notifyDirectDefault != prev) notifyDirect = notifyDirectDefault;
CJSON(notifyButton, if_sync_send[F("btn")]);
CJSON(notifyAlexa, if_sync_send[F("va")]);
CJSON(notifyHue, if_sync_send[F("hue")]);
CJSON(notifyMacro, if_sync_send[F("macro")]);
CJSON(notifyTwice, if_sync_send[F("twice")]);
JsonObject if_live = interfaces[F("live")];
CJSON(receiveDirect, if_live[F("en")]);
CJSON(e131Port, if_live[F("port")]); // 5568
JsonObject if_nodes = interfaces["nodes"];
CJSON(nodeListEnabled, if_nodes[F("list")]);
CJSON(nodeBroadcastEnabled, if_nodes[F("bcast")]);
JsonObject if_live = interfaces["live"];
CJSON(receiveDirect, if_live["en"]);
CJSON(e131Port, if_live["port"]); // 5568
CJSON(e131Multicast, if_live[F("mc")]);
JsonObject if_live_dmx = if_live[F("dmx")];
@@ -208,51 +269,64 @@ void deserializeConfig() {
CJSON(macroAlexaOn, interfaces[F("va")][F("macros")][0]);
CJSON(macroAlexaOff, interfaces[F("va")][F("macros")][1]);
const char* apikey = interfaces[F("blynk")][F("token")] | "Hidden";
#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_mqtt = interfaces[F("mqtt")];
CJSON(mqttEnabled, if_mqtt[F("en")]);
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"]);
getStringFromJson(mqttServer, if_mqtt[F("broker")], 33);
CJSON(mqttPort, if_mqtt[F("port")]); // 1883
CJSON(mqttPort, if_mqtt["port"]); // 1883
getStringFromJson(mqttUser, if_mqtt[F("user")], 41);
getStringFromJson(mqttPass, if_mqtt["psk"], 41); //normally not present due to security
getStringFromJson(mqttPass, if_mqtt["psk"], 65); //normally not present due to security
getStringFromJson(mqttClientID, if_mqtt[F("cid")], 41);
getStringFromJson(mqttDeviceTopic, if_mqtt[F("topics")][F("device")], 33); // "wled/test"
getStringFromJson(mqttGroupTopic, if_mqtt[F("topics")][F("group")], 33); // ""
#endif
#ifndef WLED_DISABLE_HUESYNC
JsonObject if_hue = interfaces[F("hue")];
CJSON(huePollingEnabled, if_hue[F("en")]);
CJSON(huePollLightId, if_hue[F("id")]);
CJSON(huePollingEnabled, if_hue["en"]);
CJSON(huePollLightId, if_hue["id"]);
tdd = if_hue[F("iv")] | -1;
if (tdd >= 2) huePollIntervalMs = tdd * 100;
JsonObject if_hue_recv = if_hue[F("recv")];
JsonObject if_hue_recv = if_hue["recv"];
CJSON(hueApplyOnOff, if_hue_recv["on"]);
CJSON(hueApplyBri, if_hue_recv["bri"]);
CJSON(hueApplyColor, if_hue_recv[F("col")]);
CJSON(hueApplyColor, if_hue_recv["col"]);
JsonArray if_hue_ip = if_hue[F("ip")];
JsonArray if_hue_ip = if_hue["ip"];
for (byte i = 0; i < 4; i++)
CJSON(hueIP[i], if_hue_ip[i]);
#endif
JsonObject if_ntp = interfaces[F("ntp")];
CJSON(ntpEnabled, if_ntp[F("en")]);
CJSON(ntpEnabled, if_ntp["en"]);
getStringFromJson(ntpServerName, if_ntp[F("host")], 33); // "1.wled.pool.ntp.org"
CJSON(currentTimezone, if_ntp[F("tz")]);
CJSON(utcOffsetSecs, if_ntp[F("offset")]);
CJSON(useAMPM, if_ntp[F("ampm")]);
CJSON(longitude, if_ntp[F("ln")]);
CJSON(latitude, if_ntp[F("lt")]);
JsonObject ol = doc[F("ol")];
prev = overlayDefault;
CJSON(overlayDefault ,ol[F("clock")]); // 0
CJSON(countdownMode, ol[F("cntdwn")]);
overlayCurrent = overlayDefault;
if (prev != overlayDefault) overlayCurrent = overlayDefault;
CJSON(overlayMin, ol[F("min")]);
CJSON(overlayMin, ol["min"]);
CJSON(overlayMax, ol[F("max")]);
CJSON(analogClock12pixel, ol[F("o12pix")]);
CJSON(analogClock5MinuteMarks, ol[F("o5m")]);
@@ -269,13 +343,15 @@ void deserializeConfig() {
CJSON(countdownMin, cntdwn_goal[4]);
CJSON(countdownSec, cntdwn_goal[5]);
CJSON(macroCountdown, cntdwn[F("macro")]);
setCountdown();
JsonArray timers = tm[F("ins")];
uint8_t it = 0;
for (JsonObject timer : timers) {
if (it > 7) break;
if (it > 9) break;
if (it<8 && timer[F("hour")]==255) it=8; // hour==255 -> sunrise/sunset
CJSON(timerHours[it], timer[F("hour")]);
CJSON(timerMinutes[it], timer[F("min")]);
CJSON(timerMinutes[it], timer["min"]);
CJSON(timerMacro[it], timer[F("macro")]);
byte dowPrev = timerWeekday[it];
@@ -285,7 +361,7 @@ void deserializeConfig() {
CJSON(timerWeekday[it], timer[F("dow")]);
if (timerWeekday[it] != dowPrev) { //present in JSON
timerWeekday[it] <<= 1; //add active bit
int act = timer[F("en")] | actPrev;
int act = timer["en"] | actPrev;
if (act) timerWeekday[it]++;
}
@@ -312,17 +388,45 @@ void deserializeConfig() {
CJSON(DMXStart, dmx[F("start")]);
CJSON(DMXStartLED,dmx[F("start-led")]);
JsonArray dmx_fixmap = dmx.createNestedArray("fixmap");
JsonArray dmx_fixmap = dmx[F("fixmap")];
it = 0;
for (int i : dmx_fixmap) {
if (it > 14) break;
DMXFixtureMap[i] = i;
CJSON(DMXFixtureMap[i],dmx_fixmap[i]);
it++;
}
#endif
DEBUG_PRINTLN(F("Starting usermod config."));
JsonObject usermods_settings = doc["um"];
usermods.readFromConfig(usermods_settings);
if (!usermods_settings.isNull()) {
bool allComplete = usermods.readFromConfig(usermods_settings);
if (!allComplete && fromFS) serializeConfig();
}
if (fromFS) return false;
doReboot = doc[F("rb")] | doReboot;
return (doc["sv"] | true);
}
void deserializeConfigFromFS() {
bool success = deserializeConfigSec();
if (!success) { //if file does not exist, try reading from EEPROM
deEEPSettings();
return;
}
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
DEBUG_PRINTLN(F("Reading settings from /cfg.json..."));
success = readObjectFromFile("/cfg.json", nullptr, &doc);
if (!success) { //if file does not exist, try reading from EEPROM
deEEPSettings();
return;
}
deserializeConfig(doc.as<JsonObject>(), true);
}
void serializeConfig() {
@@ -332,7 +436,6 @@ void serializeConfig() {
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
//{ //scope this to reduce stack size
JsonArray rev = doc.createNestedArray("rev");
rev.add(1); //major settings revision
rev.add(0); //minor settings revision
@@ -379,98 +482,93 @@ void serializeConfig() {
wifi[F("sleep")] = !noWifiSleep;
wifi[F("phy")] = 1;
#ifdef WLED_USE_ETHERNET
JsonObject ethernet = doc.createNestedObject("eth");
ethernet["type"] = ethernetType;
#endif
JsonObject hw = doc.createNestedObject("hw");
JsonObject hw_led = hw.createNestedObject("led");
hw_led[F("total")] = ledCount;
hw_led[F("maxpwr")] = strip.ablMilliampsMax;
hw_led[F("ledma")] = strip.milliampsPerLed;
hw_led[F("rev")] = strip.reverseMode;
hw_led[F("rgbwm")] = strip.rgbwMode;
JsonArray hw_led_ins = hw_led.createNestedArray("ins");
JsonObject hw_led_ins_0 = hw_led_ins.createNestedObject();
hw_led_ins_0[F("en")] = true;
hw_led_ins_0[F("start")] = 0;
hw_led_ins_0[F("len")] = ledCount;
JsonArray hw_led_ins_0_pin = hw_led_ins_0.createNestedArray("pin");
hw_led_ins_0_pin.add(LEDPIN);
#ifdef DATAPIN
hw_led_ins_0_pin.add(DATAPIN);
#endif
hw_led_ins_0[F("order")] = strip.getColorOrder();
hw_led_ins_0[F("rev")] = false;
hw_led_ins_0[F("skip")] = skipFirstLed ? 1 : 0;
//this is very crude and temporary
byte ledType = TYPE_WS2812_RGB;
if (useRGBW) ledType = TYPE_SK6812_RGBW;
#ifdef USE_WS2801
ledType = TYPE_WS2801;
#endif
#ifdef USE_APA102
ledType = TYPE_APA102;
#endif
#ifdef USE_LPD8806
ledType = TYPE_LPD8806;
#endif
#ifdef USE_P9813
ledType = TYPE_P9813;
#endif
#ifdef USE_TM1814
ledType = TYPE_TM1814;
#endif
hw_led_ins_0[F("type")] = ledType;
for (uint8_t s = 0; s < busses.getNumBusses(); s++) {
Bus *bus = busses.getBus(s);
if (!bus || bus->getLength()==0) break;
JsonObject ins = hw_led_ins.createNestedObject();
ins[F("start")] = bus->getStart();
ins[F("len")] = bus->getLength();
JsonArray ins_pin = ins.createNestedArray("pin");
uint8_t pins[5];
uint8_t nPins = bus->getPins(pins);
for (uint8_t i = 0; i < nPins; i++) ins_pin.add(pins[i]);
ins[F("order")] = bus->getColorOrder();
ins["rev"] = bus->reversed;
ins[F("skip")] = bus->skippedLeds();
ins["type"] = bus->getType();
}
// button(s)
JsonObject hw_btn = hw.createNestedObject("btn");
hw_btn["max"] = WLED_MAX_BUTTONS; // just information about max number of buttons (not actually used)
JsonArray hw_btn_ins = hw_btn.createNestedArray("ins");
#if defined(BTNPIN) && BTNPIN > -1
// there is always at least one button
JsonObject hw_btn_ins_0 = hw_btn_ins.createNestedObject();
hw_btn_ins_0[F("type")] = (buttonEnabled) ? BTN_TYPE_PUSH : BTN_TYPE_NONE;
hw_btn_ins_0["type"] = buttonType[0];
JsonArray hw_btn_ins_0_pin = hw_btn_ins_0.createNestedArray("pin");
hw_btn_ins_0_pin.add(BTNPIN);
hw_btn_ins_0_pin.add(btnPin[0]);
JsonArray hw_btn_ins_0_macros = hw_btn_ins_0.createNestedArray("macros");
hw_btn_ins_0_macros.add(macroButton);
hw_btn_ins_0_macros.add(macroLongPress);
hw_btn_ins_0_macros.add(macroDoublePress);
#endif
hw_btn_ins_0_macros.add(macroButton[0]);
hw_btn_ins_0_macros.add(macroLongPress[0]);
hw_btn_ins_0_macros.add(macroDoublePress[0]);
// additional buttons
for (uint8_t i=1; i<WLED_MAX_BUTTONS; i++) {
//if (btnPin[i]<0) continue;
hw_btn_ins_0 = hw_btn_ins.createNestedObject();
hw_btn_ins_0["type"] = buttonType[i];
hw_btn_ins_0_pin = hw_btn_ins_0.createNestedArray("pin");
hw_btn_ins_0_pin.add(btnPin[i]);
hw_btn_ins_0_macros = hw_btn_ins_0.createNestedArray("macros");
hw_btn_ins_0_macros.add(macroButton[i]);
hw_btn_ins_0_macros.add(macroLongPress[i]);
hw_btn_ins_0_macros.add(macroDoublePress[i]);
}
hw_btn[F("tt")] = touchThreshold;
#if defined(IRPIN) && IRPIN > -1
JsonObject hw_ir = hw.createNestedObject("ir");
hw_ir[F("pin")] = IRPIN;
hw_ir["pin"] = irPin;
hw_ir[F("type")] = irEnabled; // the byte 'irEnabled' does contain the IR-Remote Type ( 0=disabled )
#endif
#if defined(RLYPIN) && RLYPIN > -1
JsonObject hw_relay = hw.createNestedObject("relay");
hw_relay[F("pin")] = RLYPIN;
hw_relay[F("rev")] = (RLYMDE) ? false : true;
JsonObject hw_status = hw.createNestedObject("status");
hw_status[F("pin")] = -1;
#endif
JsonObject hw_relay = hw.createNestedObject(F("relay"));
hw_relay["pin"] = rlyPin;
hw_relay["rev"] = !rlyMde;
JsonObject light = doc.createNestedObject("light");
//JsonObject hw_status = hw.createNestedObject("status");
//hw_status["pin"] = -1;
JsonObject light = doc.createNestedObject(F("light"));
light[F("scale-bri")] = briMultiplier;
light[F("pal-mode")] = strip.paletteBlend;
JsonObject light_gc = light.createNestedObject("gc");
light_gc["bri"] = (strip.gammaCorrectBri) ? 2.8 : 1.0;
light_gc[F("col")] = (strip.gammaCorrectCol) ? 2.8 : 1.0;
light_gc["col"] = (strip.gammaCorrectCol) ? 2.8 : 1.0;
JsonObject light_tr = light.createNestedObject("tr");
light_tr[F("mode")] = fadeTransition;
light_tr[F("dur")] = transitionDelayDefault / 100;
light_tr[F("pal")] = strip.paletteFade;
light_tr["dur"] = transitionDelayDefault / 100;
light_tr["pal"] = strip.paletteFade;
JsonObject light_nl = light.createNestedObject("nl");
light_nl[F("mode")] = nightlightMode;
light_nl[F("dur")] = nightlightDelayMinsDefault;
light_nl["dur"] = nightlightDelayMinsDefault;
light_nl[F("tbri")] = nightlightTargetBri;
light_nl[F("macro")] = macroNl;
@@ -479,17 +577,6 @@ void serializeConfig() {
def["on"] = turnOnAtBoot;
def["bri"] = briS;
//to be removed once preset cycles are presets
if (saveCurrPresetCycConf) {
JsonObject def_cy = def.createNestedObject("cy");
def_cy["on"] = presetCyclingEnabled;
JsonArray def_cy_range = def_cy.createNestedArray("range");
def_cy_range.add(presetCycleMin);
def_cy_range.add(presetCycleMax);
def_cy[F("dur")] = presetCycleTime;
}
JsonObject interfaces = doc.createNestedObject("if");
JsonObject if_sync = interfaces.createNestedObject("sync");
@@ -498,8 +585,8 @@ void serializeConfig() {
JsonObject if_sync_recv = if_sync.createNestedObject("recv");
if_sync_recv["bri"] = receiveNotificationBrightness;
if_sync_recv[F("col")] = receiveNotificationColor;
if_sync_recv[F("fx")] = receiveNotificationEffects;
if_sync_recv["col"] = receiveNotificationColor;
if_sync_recv["fx"] = receiveNotificationEffects;
JsonObject if_sync_send = if_sync.createNestedObject("send");
if_sync_send[F("dir")] = notifyDirect;
@@ -509,9 +596,13 @@ void serializeConfig() {
if_sync_send[F("macro")] = notifyMacro;
if_sync_send[F("twice")] = notifyTwice;
JsonObject if_nodes = interfaces.createNestedObject("nodes");
if_nodes[F("list")] = nodeListEnabled;
if_nodes[F("bcast")] = nodeBroadcastEnabled;
JsonObject if_live = interfaces.createNestedObject("live");
if_live[F("en")] = receiveDirect;
if_live[F("port")] = e131Port;
if_live["en"] = receiveDirect;
if_live["port"] = e131Port;
if_live[F("mc")] = e131Multicast;
JsonObject if_live_dmx = if_live.createNestedObject("dmx");
@@ -530,69 +621,80 @@ void serializeConfig() {
JsonArray if_va_macros = if_va.createNestedArray("macros");
if_va_macros.add(macroAlexaOn);
if_va_macros.add(macroAlexaOff);
#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[F("en")] = mqttEnabled;
if_mqtt["en"] = mqttEnabled;
if_mqtt[F("broker")] = mqttServer;
if_mqtt[F("port")] = mqttPort;
if_mqtt["port"] = mqttPort;
if_mqtt[F("user")] = mqttUser;
if_mqtt[F("pskl")] = strlen(mqttPass);
if_mqtt[F("cid")] = mqttClientID;
JsonObject if_mqtt_topics = if_mqtt.createNestedObject("topics");
JsonObject if_mqtt_topics = if_mqtt.createNestedObject(F("topics"));
if_mqtt_topics[F("device")] = mqttDeviceTopic;
if_mqtt_topics[F("group")] = mqttGroupTopic;
#endif
#ifndef WLED_DISABLE_HUESYNC
JsonObject if_hue = interfaces.createNestedObject("hue");
if_hue[F("en")] = huePollingEnabled;
if_hue[F("id")] = huePollLightId;
if_hue["en"] = huePollingEnabled;
if_hue["id"] = huePollLightId;
if_hue[F("iv")] = huePollIntervalMs / 100;
JsonObject if_hue_recv = if_hue.createNestedObject("recv");
if_hue_recv["on"] = hueApplyOnOff;
if_hue_recv["bri"] = hueApplyBri;
if_hue_recv[F("col")] = hueApplyColor;
if_hue_recv["col"] = hueApplyColor;
JsonArray if_hue_ip = if_hue.createNestedArray("ip");
for (byte i = 0; i < 4; i++) {
if_hue_ip.add(hueIP[i]);
}
#endif
JsonObject if_ntp = interfaces.createNestedObject("ntp");
if_ntp[F("en")] = ntpEnabled;
if_ntp["en"] = ntpEnabled;
if_ntp[F("host")] = ntpServerName;
if_ntp[F("tz")] = currentTimezone;
if_ntp[F("offset")] = utcOffsetSecs;
if_ntp[F("ampm")] = useAMPM;
if_ntp[F("ln")] = longitude;
if_ntp[F("lt")] = latitude;
JsonObject ol = doc.createNestedObject("ol");
ol[F("clock")] = overlayDefault;
ol[F("cntdwn")] = countdownMode;
ol[F("min")] = overlayMin;
ol["min"] = overlayMin;
ol[F("max")] = overlayMax;
ol[F("o12pix")] = analogClock12pixel;
ol[F("o5m")] = analogClock5MinuteMarks;
ol[F("osec")] = analogClockSecondsTrail;
JsonObject timers = doc.createNestedObject("timers");
JsonObject timers = doc.createNestedObject(F("timers"));
JsonObject cntdwn = timers.createNestedObject("cntdwn");
JsonArray goal = cntdwn.createNestedArray("goal");
JsonObject cntdwn = timers.createNestedObject(F("cntdwn"));
JsonArray goal = cntdwn.createNestedArray(F("goal"));
goal.add(countdownYear); goal.add(countdownMonth); goal.add(countdownDay);
goal.add(countdownHour); goal.add(countdownMin); goal.add(countdownSec);
cntdwn[F("macro")] = macroCountdown;
JsonArray timers_ins = timers.createNestedArray("ins");
for (byte i = 0; i < 8; i++) {
if (timerMacro[i] == 0 && timerHours[i] == 0 && timerMinutes[i] == 0) continue;
for (byte i = 0; i < 10; i++) {
if (timerMacro[i] == 0 && timerHours[i] == 0 && timerMinutes[i] == 0) continue; // sunrise/sunset get saved always (timerHours=255)
JsonObject timers_ins0 = timers_ins.createNestedObject();
timers_ins0[F("en")] = (timerWeekday[i] & 0x01);
timers_ins0["en"] = (timerWeekday[i] & 0x01);
timers_ins0[F("hour")] = timerHours[i];
timers_ins0[F("min")] = timerMinutes[i];
timers_ins0["min"] = timerMinutes[i];
timers_ins0[F("macro")] = timerMacro[i];
timers_ins0[F("dow")] = timerWeekday[i] >> 1;
}
@@ -610,11 +712,10 @@ void serializeConfig() {
dmx[F("start")] = DMXStart;
dmx[F("start-led")] = DMXStartLED;
JsonArray dmx_fixmap = dmx.createNestedArray("fixmap");
JsonArray dmx_fixmap = dmx.createNestedArray(F("fixmap"));
for (byte i = 0; i < 15; i++)
dmx_fixmap.add(DMXFixtureMap[i]);
#endif
//}
JsonObject usermods_settings = doc.createNestedObject("um");
usermods.addToConfig(usermods_settings);
@@ -633,23 +734,29 @@ bool deserializeConfigSec() {
bool success = readObjectFromFile("/wsec.json", nullptr, &doc);
if (!success) return false;
JsonObject nw_ins_0 = doc["nw"][F("ins")][0];
JsonObject nw_ins_0 = doc["nw"]["ins"][0];
getStringFromJson(clientPass, nw_ins_0["psk"], 65);
JsonObject ap = doc[F("ap")];
JsonObject ap = doc["ap"];
getStringFromJson(apPass, ap["psk"] , 65);
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
JsonObject if_mqtt = interfaces[F("mqtt")];
getStringFromJson(mqttPass, if_mqtt["psk"], 41);
#ifdef WLED_ENABLE_MQTT
JsonObject if_mqtt = interfaces["mqtt"];
getStringFromJson(mqttPass, if_mqtt["psk"], 65);
#endif
#ifndef WLED_DISABLE_HUESYNC
getStringFromJson(hueApiKey, interfaces[F("hue")][F("key")], 47);
#endif
JsonObject ota = doc["ota"];
getStringFromJson(otaPass, ota[F("pwd")], 33);
@@ -676,12 +783,18 @@ void serializeConfigSec() {
ap["psk"] = apPass;
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;
#endif
#ifndef WLED_DISABLE_HUESYNC
JsonObject if_hue = interfaces.createNestedObject("hue");
if_hue[F("key")] = hueApiKey;
#endif
JsonObject ota = doc.createNestedObject("ota");
ota[F("pwd")] = otaPass;
@@ -692,4 +805,4 @@ void serializeConfigSec() {
File f = WLED_FS.open("/wsec.json", "w");
if (f) serializeJson(doc, f);
f.close();
}
}

View File

@@ -33,6 +33,11 @@ void colorFromUint24(uint32_t in, bool secondary)
}
}
//store color components in uint32_t
uint32_t colorFromRgbw(byte* rgbw) {
return (rgbw[0] << 16) + (rgbw[1] << 8) + rgbw[2] + (rgbw[3] << 24);
}
//relatively change white brightness, minumum A=5
void relativeChangeWhite(int8_t amount, byte lowerBoundary)
{
@@ -59,7 +64,7 @@ void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) //hue, sat to rgb
case 4: rgb[0]=t,rgb[1]=p,rgb[2]=255;break;
case 5: rgb[0]=255,rgb[1]=p,rgb[2]=q;
}
if (useRGBW && strip.rgbwMode == RGBW_MODE_LEGACY) colorRGBtoRGBW(col);
if (strip.isRgbw && strip.rgbwMode == RGBW_MODE_LEGACY) colorRGBtoRGBW(col);
}
void colorKtoRGB(uint16_t kelvin, byte* rgb) //white spectrum to rgb, calc
@@ -106,7 +111,7 @@ void colorCTtoRGB(uint16_t mired, byte* rgb) //white spectrum to rgb, bins
} else {
rgb[0]=237;rgb[1]=255;rgb[2]=239;//150
}
if (useRGBW && strip.rgbwMode == RGBW_MODE_LEGACY) colorRGBtoRGBW(col);
if (strip.isRgbw && strip.rgbwMode == RGBW_MODE_LEGACY) colorRGBtoRGBW(col);
}
#ifndef WLED_DISABLE_HUESYNC
@@ -164,7 +169,7 @@ void colorXYtoRGB(float x, float y, byte* rgb) //coordinates to rgb (https://www
rgb[0] = 255.0*r;
rgb[1] = 255.0*g;
rgb[2] = 255.0*b;
if (useRGBW && strip.rgbwMode == RGBW_MODE_LEGACY) colorRGBtoRGBW(col);
if (strip.isRgbw && strip.rgbwMode == RGBW_MODE_LEGACY) colorRGBtoRGBW(col);
}
void colorRGBtoXY(byte* rgb, float* xy) //rgb to coordinates (https://www.developers.meethue.com/documentation/color-conversions-rgb-xy)

Some files were not shown because too many files have changed in this diff Show More